Release Notes¶
Changelog¶
All notable changes to spectrafit-core will be documented in this file.
This project follows repository release policy enforced by repo-release-tools.
Release status —
0.1.0is the first release. Tagv0.1.0is the first version published to PyPI, as a GitHub release archived on Zenodo; its date is thedate-releasedinCITATION.cff. Before it,v0.1.0rc1was a release candidate with the same content, published to TestPyPI only as a GitHub pre-release. The[0.1.0b1]and[0.1.0a1]sections below record the dates on which those version bumps were prepared in-tree; neither was ever cut as a release, so neither date is a release date.Scope note.
[0.1.0]below collects everything since the0.1.0b1bump and summarises the development log by theme rather than reproducing it entry by entry.
[Unreleased]¶
Added¶
- Zenodo concept DOI. v0.1.0 is archived on Zenodo; its concept DOI
10.5281/zenodo.23043544(always the latest version) now sits inCITATION.cff(identifiers),codemeta.json(@id) and a README badge, andtests/meta/test_citation_metadata_consistency.pyfails if the three disagree.
Changed¶
- Release guide from the v0.1.0 run.
docs/contributor-guide/releasing.mdnow spells out the hand-set tags on both remotes (GitHub: bot tagger, deploy key, sync commit; GitLab: theGitLab-Commit:trailer SHA), the dates to move if a tag slips, and what a slow step looks like: TestPyPI index lag inverify-testpypi, thepypiapproval, Zenodo's Received queue and DOI resolution lag. The GitHub repository is never deleted again; restarts go throughpublish:github:resetonly. - The citation names the software, not an unpublished article.
CITATION.cffdrops itspreferred-citation(a journal article withstatus: in-preparation), which GitHub's "Cite this repository" showed in place of the release; it comes back once the article is accepted, and a meta test holds that line. The concept DOI is also the top-leveldoi, so the citation carries it. - Docs, README and citation metadata may start as a GitHub PR. The
mirror and release guides no longer require them on GitLab; a
squash-merged GitHub PR reaches GitLab through
backport:github. Release bumps stay on GitLab.
Fixed¶
- The docs home page no longer says 0.1.0 is unreleased. Its Citing section claimed no tag or archive existed and described the in-preparation article; it now gives the release date and the concept DOI.
[0.1.0] - 2026-09-29¶
Added¶
- Only the CI sync can write to the GitHub mirror. A
guard-public-pushpre-push hook (scripts/guard_public_push.py) refuses any push to a github.com remote whose tree holds a path outside the publish include list, so no branch can reach the mirror aroundscripts/publish_sync.py. The benchmark-host helper (scripts/remote_bench.sh) now pushes to GitLab only and fails loudly instead of silently trying the mirror first, and the "Fast iteration on GitHub" lane (a direct branch push to the mirror) is gone from the contributor docs, replaced by a server-side ruleset checklist for the mirror repository. - Zenodo and release metadata.
.zenodo.jsonfor the GitHub integration, areleasing.mdcontributor checklist for the first archival release, and a meta-test that keepspyproject.toml,CITATION.cff,codemeta.jsonand.zenodo.jsonagreeing on version, licence, author ORCID and repository. - A guard against shipped tests reading stripped paths. A meta-test scans every test that ships to the public mirror for path literals matching a publish exclusion, the failure class behind a red mirror CI run. It exempts its own scanner self-tests, which feed it excluded literals on purpose.
- A
robustbenchmark category. Iteratively reweighted least squares on outlier-contaminated spectra, six cases, all JAX-eligible. - Credibility-ladder rung 3 closed. Significant digits are measured against NIST StRD certified values rather than asserted, and an uncertainty budget splits error into numerical, model-form and input terms.
- Surrogate oracles — metamorphic relations that must hold where no reference answer exists, for the cases certified values cannot reach.
- Analytic Jacobians are checked across every kernel in multiple regimes, rather than trusted.
- Three new model kernels wired end to end:
rational_cubic,generalised_logisticandexp_over_linear. - All 22 NIST StRD data modules carry a documented
Attributes:section, so the certified values, starting points and degrees of freedom are readable without opening the upstream page. - Two gallery pages about being wrong — a converged fit reporting success with a negative \(r^2\), and a model that is simply misspecified while every scalar score improves.
- FAIR tooling for the published evidence (
uv run poe fair): a whole-tree sha256 manifest that also reconciles the per-subtree manifests and every RO-Crate digest against the files themselves, a provenance registry for vendored third-party data, and an RO-Crate 1.1 describing the tree.
Changed¶
- README for the first public release. A light/dark hero image (absolute
raw.githubusercontent.comURLs, since the README is also the PyPI description), a new Installation section (pip install spectrafit-core/uv add spectrafit-core, with the source build as the alternative), status "first public release" instead of "public sneak preview" (also in the docs site announce banner; both rrt patterns inpyproject.tomlfollow), and a Contributing section that welcomes issues and pull requests on GitHub and names the MPCDF GitLab as the second, synced home. The mirror notice and two bits of internal jargon are gone. - Renovate replaces Dependabot, including the GitLab CI image.
.github/renovate.jsoncovers cargo, npm,uv.lock, GitHub Actions (kept SHA-pinned), the image's base andARG *_VERSIONpins (annotated inDockerfile.ci), the ShellCheck and cargo-llvm-cov pins in CI, weekly lock file maintenance and OSV/GitHub security alerts; related pins are grouped (pyo3 + rust-numpy, Playwright, cargo-llvm-cov). A newdocker-pinscheck verifies the image's SHA-256-pinned downloads, so a version bump without its digest is red on the PR, not on GitLab after the backport. Update PRs skip the Claude review only when the bot opened them from this repository. - One Claude review per PR. The code-review plugin job is gone: it saw the
traffic-light comment as "Claude has already reviewed this PR", stopped and
still billed the run (and was red for a denied
WebFetch). The traffic-light review stays the only one, and it no longer re-posts an inline "must fix" comment that is already on the PR at the same line. - Compact review comment. A findings table (light, linked file:line,
a one-line title) replaces the wall of text; each finding's problem and
"do" sit in a collapsible HTML
<details>(open for must, closed otherwise), and Claude is asked for a <=10-word title and at most three sentences per finding. - The GitLab -> GitHub sync refuses to lose work.
publish_sync.pynow skips a stale pipeline (GitHub already synced from a newer GitLab commit; syncing would roll GitHub back) and refuses, listing the commits, while GitHubmainhas native commits after the sync boundary that are not on GitLab yet (the sync would revert them and move the boundary past them, sobackport:githubwould never find them). The sync PR is opened as a draft, so it cannot be squash-merged by hand or auto-merge. The sync jobs run withGIT_DEPTH: "0"; the guard runs only when a sync would actually happen, so an up-to-date pipeline never reports REFUSED, and a git failure inside it is reported asFAILEDinstead of a traceback. The mirror guide documents the working rulesets, the review gate and where cross-platform changes should start. - The code-review plugin finishes its run. Its reviewer subagents ran in
the background and were still running when the headless session ended
(4 turns, nothing posted);
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1keeps them in the foreground.Skillis allowed (it was denied), and the plugin job no longer runs (and bills) on the GitLab sync PR. - PRs that change the review workflow get a red light. Claude cannot run
on them (the action only runs the default branch's workflow), so they were
passing unreviewed; they now need a maintainer's
/claude-override. - The review gate status is actually set. GitHub rejects 4-byte Unicode in commit-status descriptions (HTTP 422), so every red/orange/yellow/green status with an emoji failed and the gate stayed "pending". Status texts now read RED/ORANGE/YELLOW/GREEN; the PR comment keeps the emoji.
- Readable review comments. Findings are numbered sections with a link to the exact file line at the reviewed commit, the problem and a separate Do:; Claude is asked for Markdown code formatting; a count table heads the comment; "what was checked" folds into a list; and a collapsible "Prompt for Claude Code" carries every must/should item ready to paste.
- Built wheels are installed and tested before any tag. The wheel build
moved into the reusable
build-wheels.yml, called byrelease.ymland by the newwheel-smoke.yml, which runs on PRs andmainpushes touching the wheel. It installs each manylinux wheel in a barepython:3.13-slim/python:3.14-slimcontainer and each macOS/Windows wheel in a fresh environment outside the checkout, checks that the universal2 wheel carries both architectures, and runs the README quick start -- the first install of a built wheel no longer happens on TestPyPI after a tag. - Claude review report with a traffic light, required and overridable. A
second review job (Sonnet only) writes one sticky PR comment -- summary,
must / should / don't, what was checked -- and sets the commit status
claude-review-gate: red with any "must" finding (blocks the merge once the status is a required check), orange with "should" findings, yellow with only minor "could" findings, green otherwise. "Must" findings also become inline comments. A maintainer overrides a red gate with/claude-override <reason>; sync and Dependabot PRs get a green "skipped" gate, external authors a red one until overridden. The code-review plugin runs on Sonnet too (its "opus" agents are remapped). - The Claude review actually reviews and posts, and says so. The workflow
now passes
--comment(without it thecode-reviewplugin stops before posting) and allows the plugin's full tool list plus the subagent tool it launches its reviewers with; before, the first subagent launch was denied and the run ended green after 3 turns with nothing posted. A newReport review outcomestep writes turns, cost, denied tools and the result to the job summary and fails the job on any permission denial. - Every backport MR gets a pipeline, and the job says which. After opening
the MR,
backport_from_github.py --open-mrreads the MR'shead_pipelinefor up to 60 s and logs its id and URL; only if GitLab attached none does it start exactly one (POST /merge_requests/:iid/pipelines), and a failure to do so exits non-zero. The branch push itself never gets a pipeline: theworkflow:rulesskip branch pipelines once an MR is open, by design. - The GitLab CI image is rebuilt every week. A pipeline schedule with
CI_IMAGE_REBUILD=truerunsbuild:ci-imagealone -- the shared rule anchors and every job with its ownmain/schedule rules skip that pipeline -- so base-image security patches reach the runners even whenDockerfile.ciis unchanged. The image job no longer repeats the tool versions as--build-argcopies;Dockerfile.ci's ARG defaults are the single source. - Web toolchain on React 19 and TypeScript 7. The dashboard (
web/) and the root tooling package move to React 19, the TypeScript 7 native compiler fornpm run typecheck, Vitest 5,@vitejs/plugin-react6, KaTeX 0.18 (which ships its own types, so@types/katexis gone) and current Vite, Biome, Playwright and Testing Library releases.openapi-typescriptstill needs the TypeScript JavaScript API, so the plaintypescriptpackage stays on 5.9 for contract codegen.npm auditreports no vulnerabilities in either lockfile. - Cancelled CI runs actually stop. Workflow steps and the
coverage-atlas-fusedjob usedif: always(), which also runs on cancel, so a run superseded by a newer push kept going and held theci-${{ github.ref }}concurrency group: the new run's requiredlintcheck sat at "waiting for status". They now use!cancelled(). - Wheels and CI cover every promised platform. Linux wheels build in
manylinux_2_28 (manylinux2014's cmake 2.8 / gfortran 4.8 could not build
netlib LAPACK), every platform builds CPython 3.13 and 3.14 wheels, and
verify-testpypiinstalls them on Linux, macOS and Windows for both. A newtest-matrixCI job runs the full suite on Linux/3.14 and the binding tests plus the README quick start on macOS and Windows for 3.13 and 3.14. The Claude review workflow may now post its review (claude_args --allowedTools); before, it ran, was billed and posted nothing. - PyO3 0.29 and rust-numpy 0.29; Python 3.14 support. The extension
moves from PyO3/numpy 0.22 to 0.29, which builds and imports on CPython
3.14 as well as 3.13 (PyO3 0.22 refused 3.14 as newer than its maximum
supported version). The Python API and the JSON boundary are unchanged;
pyproject.tomlnow lists the 3.14 classifier. - Windows builds without LAPACK.
spectrafit-varprokeeps Accelerate on macOS and netlib LAPACK on Linux, but uses varpro's pure-Rust nalgebra backend on Windows, wherenetlib-srcwould need a Fortran compiler. - Python dev and docs tooling bumped. ty 0.0.84, ruff 0.16.9, zensical
0.0.65, mkdocstrings-python 2.0.9 and repo-release-tools 1.18.0, with the
pre-commit hook revs moved in step; minor bumps of hypothesis, maturin,
pydantic, numpy, matplotlib, typer, uvicorn and jax. The two cross-pipeline
CI artifact fetches are now declared for
rrt doctor's new check. - Docs name the project SpectraFit-Core in prose. The architecture page
used the package spelling
spectrafit-coreas the project name. - Shorter README benchmark section. The benchmark overview and the contract paragraph are a short list now; content and links unchanged.
-
GitHub Actions bumped to their current majors, still SHA-pinned.
checkoutv4→v7,setup-pythonv5→v7,setup-uvv4→v10,cachev4→v6,upload-/download-artifactv4→v7/v8,setup-nodev4→v7,github-scriptv7→v9,configure-pagesv5→v6,upload-pages-artifactv3→v5 (now pinned withinclude-hidden-files: trueto keep its pre-v4 "include everything" behaviour),deploy-pagesv4→v5,softprops/action-gh-releasev2→v3, andanchore/sbom-action/pypa/gh-action-pypi-publish/anthropics/claude-code-actionto their latest same-major commit.dtolnay/rust-toolchain@stableandtaiki-e/install-action(pinned in lockstep withDockerfile.ci'sLLVM_COV_VERSION, per its own comment) are unchanged by design. -
GitHub contributor PRs are squash-merged. The mirror allows squash merges only (repository setting and the
mainruleset), so each PR is one commit onmain, authored by the PR author with other committers asCo-authored-by:trailers;backport:githubcherry-picks that commit unchanged. The anonymous sync still lands by deploy-key fast-forward (docs/contributor-guide/github-mirror-workflow.md). -
The GitHub mirror sync is anonymous one way and keeps history the other. GitLab -> GitHub: each sync lands as one commit authored and committed by
spectrafit-core-sync[bot], the bot account of a dedicated GitHub App (so GitHub shows a bot, never a person), with the fixed subjectsync: gitlab main, a fixed body and theGitLab-Commit:trailer as the only GitLab-derived content. It no longer lands by GitHub squash auto-merge (which re-authors the commit to the token's owner and sets the committer to GitHub):scripts/publish_sync.pywaits for the ruleset's required checks on the sync PR and fast-forwardsmainto the exact checked commit through a deploy-key SSH remote, the mirror's only push identity; a movedmainis refused, never forced. GitHub -> GitLab:scripts/backport_from_github.pyexpands a merge-commit PR into its individual commits (original author, date and message,-x) instead of one-m 1commit attributed to whoever merged, and opens the GitLab MR withsquash: false. -
The GitHub mirror keeps its history. GitLab
mainnow reaches GitHub as one squashed pull request per change (scripts/publish_sync.py): a single commit whose tree is the filtered GitLab tree, parented on GitHubmain, carrying aGitLab-Commit:trailer, merged by squash auto-merge once CI is green. The history-rewriting orphan snapshot survives only as the manualpublish:github:resetjob. The reverse direction is automated too: a scheduledbackport:githubjob cherry-picks GitHub-native commits (Dependabot, outside pull requests) onto a branch and opens a GitLab merge request, skipping anything already carried over. - BREAKING:
FitOptions.etamust be< 0.25(was< 1.0). Values at or above the new bound never produced a usable trust-region step. - IRLS warm-starts each outer pass and converges on its own weight sequence.
fit/fit_fastraiseValueErrorwhen datasets disagree onn_dims, instead of failing later and less legibly.spectrafit-solver::fitis the only site that sets faer's global thread configuration, so a library caller's threading is no longer silently overridden from several places.- The evidence tree moved to
reproducibility/and third-party measured data is vendored under its own licence, recorded per asset inreproducibility/assets.toml.
Deprecated¶
- Two public surfaces are deprecated but still ship in 0.1.0; neither is removed by this version.
Fixed¶
- The weekly CI-image rebuild actually runs. GitLab drops a pipeline whose
only jobs sit in
.pre/.postas empty, and on the schedule every job butbuild:ci-image(a.prejob) iswhen: never-- so the schedule answered "201 Created" and never started a pipeline. A newtest:ci-imagejob in theteststage runs only on the rebuild, after the build, on the fresh image: the defaultbefore_scriptchecks every baked tool and the job prints their versions. A meta test pins the job outside.pre/.post. -
Both mirror directions work against a private GitHub repository.
backport:githubsendsGITHUB_TOKENas a secondhttp.extraheaderscoped tohttps://github.com/(GIT_CONFIG_COUNT=2, one header per host), andpublish:github/publish:github:fastauthenticate their fetches too; previously only pushes carried a token, so every fetch of a private mirror failed. -
tests/meta/test_publish_sync.py's fast-forward tests no longer depend on the ambientinit.defaultBranch. Its bare-remotegit initand its_race_remote_mainclone didn't pin-b main, so under a non-maindefault (git <2.28's built-in "master", or any global override) the race helper'sgit push origin mainfailed outright. Both call sites now pin-b main, matching the pattern already used intest_backport_from_github_script.py. -
The PyO3 binding crate is held to a 75% line floor on both CIs again. GitHub's gate read 41.7% because llvm-cov counted three instrumented copies of
lib.rs, only one of which ever ran:cargo testnow excludes the test-lessspectrafit-corecrate, and the instrumented pytest step no longer letsuvrebuild the extension. GitLab had dropped the floor in June; itsrust-covjob now runs the binding tests against the instrumented build and enforces it. New binding tests cover the zero-copy numpy path, sigma weighting, ragged and empty inputs and overflow guards, taking the measured binding coverage from 68% to 93%. - The RO-Crate no longer drifts on every commit.
datePublishedwas read fromgit log, so a crate generated before its own commit could never match a rebuild after it (and shallow clones broke it outright). It now comes fromCITATION.cff'sdate-released, falling back to a pinned date; the two nested crates also name their author with an ORCID. -
Packaging metadata uses the PEP 639 licence form (
license = "MIT"pluslicense-files), with classifiers for the platforms, interpreter and typing the project actually supports and aDocumentationURL. The maturin floor rises to 1.9, the first release with full PEP 639 support. -
fit_arrays/fit_arrays_numpymapped a Rust panic to an uncatchablePanicException; solver-invoking entry points now raise a catchableValueError. - VarPro reported permuted standard errors —
fill_parameter_stderrassociated each error with the wrong parameter. - Floating-point values crossing the PyO3 boundary could round to a different value than the one computed.
gradient_normand the lastgradient_norm_historyentry mixed two different quantities.- The rat43 NIST StRD fixture certified the wrong degrees of freedom.
- Three published model formulas were wrong, including
log_normal's docstring. - Documentation claims the code does not support, and counts across
docs/that had drifted with nothing guarding them.
[0.1.0b1] - 2026-06-23¶
Prepared, not released — see the release-status note at the top of this file. No
0.1.0b1tag exists.
Changed¶
- Promoted to beta (
Development Status :: 4 - Beta). Intended as a non-public, GitLab/GWDG-only reproducible source release (clone +uv run maturin develop); no PyPI/wheel publish, no DOI. The version bump and metadata landed in-tree on this date; the tag was never cut.
Fixed¶
- Lean wheel (Option A packaging). Removed the
spc-bench[project.scripts]console script (it ImportError'd on a clean install because its deps live in the[benchmark]extra) and repointed every caller — thepoetasks and the GitLab CI jobs — topython -m benchmark.cli/uv run poe benchmark. Scoped maturin topython-packages = ["spectrafit_core"]. - Benchmark gate integrity. The accuracy axis now fails on a non-finite
|Δr²|(previouslyNaN > thresholdsilently passed and the value was coerced to0.0); the primary GitLab pipeline now enforcespython -m benchmark.cli gateon push.
[0.1.0a1] - 2026-06-13¶
Prepared, not released — see the release-status note at the top of this file. No
0.1.0a1tag exists.
Added¶
- MIT
LICENSEfile;CITATION.cff(CFF 1.2.0);CONTRIBUTING.md,CODE_OF_CONDUCT.md(Contributor Covenant 2.1),SECURITY.md. LIMITATIONS.mddisclosing the benchmark's self-audit gaps (W2c κ(J), NIST 4-of-27 subset, χ²-floor convergence proxy, JAX-no-σ).pyprojectauthors,[project.urls], and PyPI classifiers (alpha).- Research-grade
README.mdintro with status, citation, and license sections.
Changed¶
- Markdown documentation consolidated (129 → 91 tracked files).
rrtrepo-root-required-filescontract extended to enforce the new governance/legal files.
Added — 2026-06-08 / 2026-06-09¶
spc-benchCLI surface gained four subcommands.forensics [--run ID]renders matplotlib PNGs of {observed spectrum, per-backend fit, residuals} for everyregression_case_idsentry of a run;sweep --tiers 1,2,5,10runs the bench at multiple--repsbudgets and emits a budget-vs-signal table;trend [--field --last N]reads.spectrafit_reports/index.jsonand prints ASCII sparklines plus a table of the four gate axes across history;pin-baseline/show-baseline/clear-baselinemanage.spectrafit_reports/perf_baseline.json.BenchReport.manifest: ManifestSignals | None(SCHEMA_VERSION1.1 → 1.2). Surfaces the four gate-axis numbers —geomeanSpeedupVsBaseline,maxAbsDeltaR2,spectrafitWinRate,regressions— plus optionalPinnedBaselineon the typed contract so the webGateBadgerenders real values instead of pointing users at the CLI. Additive minor; Pydantic defaults keep every 1.1 payload on disk valid.- IRLS robust-loss selection from Python.
FitOptions(solver="irls:huber" | "irls:bisquare" | "irls:biweight" | "irls:cauchy")reaches the underlyingWeightFnvariant via the colon-split parser indispatch.rs:108-110. Newtests/test_irls_weights.pypins each variant. - Trust-region power-user knobs. Three new
Option<f64>fields onFitOptions—delta0,max_delta,eta— reachTrustRegionConfigindispatch.rsfor thedogleg/newton-cgsolvers.Nonekeeps the library default;Some(v)overrides. Newtests/test_tr_knobs.pyproves the knobs reach the TR core (an impossibleeta=1.5forcesNoImprovement). - Cycle methodology codified at
docs/methodology.md(cycle pattern, fan-out playbook, verification loop, which-skill-when matrix, sprint cadence, anti-patterns).CLAUDE.mdSynopsis links to it. - Rust binding audit at
docs/reference/rust/binding-audit.mdenforced by ascripts/audit_bindings.pyCI guard — fails the pipeline when a new#[pymodule]registration orSolver::variant lands without a doc entry. - Runnable examples at
docs/examples/{fitting,shared_params,multi_dataset,3d_fitting}.md.
Changed — 2026-06-08 / 2026-06-09¶
- CI redundant-loading elimination (Cycle 30, four commits). GitLab
base image switched from
python:3.13-slimto the public Docker Hubnikolaik/python-nodejs:python3.13-nodejs22-bookworm(Python 3.13 + Node 22 + uv baked in — no own-registry maintenance per user constraint).CARGO_HOME+RUSTUP_HOMEmoved under$CI_PROJECT_DIRso rustup + cargo-llvm-cov persist via the project-tree cache.cargo install cargo-llvm-cov --lockedreplaced with the prebuilt tarball fromtaiki-e/cargo-llvm-cov/releases/v0.8.7(GitLab) andtaiki-e/install-action@v2(GitHub). Apt build-deps install gated on a job-levelNEEDS_BUILD_DEPS=1variable — cmake/gfortran/lapack/openblas now installs only in the 3 jobs that actually compilenetlib-src(lint:rust, test:python, test:rust); the other 8 jobs skip the ~1.5 min apt cost. The step-summary step refactored from fouruv run coverage reportshell-outs to onecoverage json+ fourjqreads. Expected savings: GitLab ~40–55 min/pipeline (image swap + cache + apt gating); GitHub ~2.5–3.5 min/pipeline. - GateBadge redesign (
cupertino-councilskill). Vertical accent edge, four equal-weight number cells, "All clear" / "Investigate N case(s)" subtitle (grepable tag survives indata-gate-status), informational 6 s breath on the status dot (pulses only when PASS AND perf ratio ≥ 95 % of pinned), 60×14 SVG sparkline under the geomean cell when a pin exists. - Suite distributions trio redesign (council + data fix).
panels.tsxsuiteSpeedRowsandsuiteAccRowsno longer use?? 1/?? 0defaults for missing backend metrics — the previous behaviour faked 85 jax samples per violin. Per-backendn=Nannotations surface via the existingannotslot; partial-surface backends get a dimmed row label via a newdim?: booleanfield onDistRow. Layout overridden to1fr 1fr 2frso the scatter (the thesis) is the centrepiece. - scipy-ls trio (
scipy-ls-lm/scipy-ls-trf/scipy-ls-dogbox) rejoins the benchmark backend roster (3 → 6 oracle).SOLVER_METAextended;synth.pyperturb/base_ms extended; tests relaxed to subset assertions.
Fixed — 2026-06-08 / 2026-06-09¶
- Engine regression policy (
engine.py:run_suite) excludes oracle failures onoptfncases, mirroring the accuracy-axis policy. Without this, 9 of 11 regressions on2026-06-06_run_012were oracle multimodal-trap noise the accuracy axis already accepted. - Off-domain runaway guard r²-escape
(
crates/spectrafit-solver/src/postfit.rs). CX-017 reached r² = 0.96236 but was mislabelledsuccess=falsebecauseamplitude = 2.55e3was outside the data envelope — for area-normalised peak models the amplitude is an integrated area, not a peak height. Guard now skips abover² ≥ 0.5. - Soft-failure r²-quality upgrade in
apply_postfit_guards. OF-005 reached r² = 0.9921 but reportedno_improvement_possible; the upgrade promotessuccess=false → truewhen termination is soft AND r² ≥ 0.9. Numerical errors stay failures regardless. graph.pycoverage raised from 69.8 % to 82.6 % by exercisingGlobalFitGraph.fit_all_slices; per-module CI floor lifted from 65 → 80.- GitLab CI hardening.
.gitlab/00-defaults.ymlbefore_scriptnow fail-fasts when build tools are missing post-apt-install — surfaces the cause in 20 lines instead of 200 lines of cargo trace.
Fixed¶
- CI / pre-commit governance: repaired a long-red pre-commit suite — corrected
validator script paths after the
.github/skills→.claude/skillsmove, removed two obsolete validators (validate-scenariosYAML,validate-model-stubModelKernel) that checked a superseded architecture, updatedpre-merge-dag.shallowed-deps for the per-method solver-crate split, dropped the uninstalledmypyhook (superseded byty), and scoped thety/ docstring checks to project sources (not.claude/tooling). - Type checking: cleared all
tyerrors acrosspython/andtests/— widenedMeasurementData.xto match its 1-D→2-D runtime promoter, typedParameterbound validators and the graph evalparams(Mapping[str, object], no bareAny). - Solver: order-safe VarPro routing (filter
amplitudeby name, not positional.skip(1)over a HashMap) and point-major n-Dxlayout in the DE/global path. - Graph: reject duplicate node IDs and out-of-range
dataset_indexinstead of panicking. - PyO3 boundary: removed the dead
ExpressionNotImplementederror variant, corrected the_core.pyiexception docs, and madeevaluatereject multi-dataset / n-D input. - Tests: removed a stale
xfailmasking the (landed) 2-D fit path and enabledxfail_strict; added aModelType↔ Rust parity entry for the new kernels.
Added¶
poe report_htmlpipeline: build the Rust extension → run the benchmark → bundle a single, self-contained, deployablereport.html(JS/CSS inlined viavite-plugin-singlefile, the report inlined aswindow.__BENCH__) that opens offline with no server. Stored at.spectrafit_reports/benchmark/<run>/report.html.data.loadReport()prefers the inlined data when present, else fetches/api/reportas before.- Benchmark web UI — greenfield rebuild on the frozen JSON contract: a Vite + React
app with 5 views — Overview (new default hero: all-backend head-to-head with
co-winner ties on metric equality, suite distributions, initial→best parameter recovery
±σ, error-vs-runtime, and the 2-D map + time-resolved series as sections) / Dashboard /
Report / Cockpit / Export. Category-grouped sidebar navigation. The data binding has no
silent
?? PRIMARYfallback and enumerates backends viasolversOf(F)(no hardcoded backend ids — enforced by a source-scan test). Avitestsuite (web/src/__tests__/*) replaces the ad-hocweb/scripts/*.mjssmokes;npm run smokenow runs vitest. - 2-D fitting as a real subject: the benchmark
_multidim()now fits the 2-D map with spectrafit's nativegaussian2dkernel (source=spectrafit-core), replacing the prior scipy oracle. - Time-resolved series: a real
GlobalFitGraphjoint multi-dataset fit (_time_resolved()) — peak centers/widths shared across all time slices, per-slice amplitudes free (recovered kinetics). - Contract:
TimeResolved/TimeSlice/PeakTraceshapes andFeatured.{time_resolved, guess_params}(initial-guess values for the recovery table). - Ground-truth invariants:
tests/test_bench_invariants.py(Tier-1 fast + Tier-2slow) — every suite category is deep-dived, the analyzed set is multiple + unique, per-case plots are distinct, all floats finite, and optfn carries spectrafit + lmfit but not jax. - New model kernels:
tauc,cauchy_dispersion,kww(+ catalog drift-guard test). - Project-scoped MCP servers (
context7,github) in.mcp.json.