scripts/ci/check_record_ids.py runs in the ci-scripts job. IDs that were already shared
on main (UB-100, UB-175, UB-185, UB-204, UB-210, UB-238, UB-256, WR-190, WR-254) are frozen
as legacy: merged records are not renamed, and no new record may join them.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJzQKZ9sjU1DhgS253e8J5
A known-mismatch entry with cpp_defect = "<id>" (a confirmed upstream issue) is now parked:
not run in the required gate, not a pass, not blocking green, counted as parked=N and listed
on the unit's site page with its issue id. Removing the marker runs the case again.
--run-parked runs only the parked cases. The new parked-upstream workflow checks usd-brep's
tags and HEAD daily with git ls-remote (no checkout, no build) and, only when they change or
on workflow_dispatch, builds that upstream, runs the parked cases and opens or updates one
"usd-brep <tag> released: N parked cases now agree, adopt?" issue. It needs only
contents: read and issues: write.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A known-mismatch entry may name a confirmed upstream issue with
cpp_defect = "<UB-/WR-/ND-/HANG- id>" (validated against
plan/upstream-issues/<id>--*.md with "Confirmed: yes"). Such a case is
classified cpp_defect: reported separately (cases_cpp_defect, its own run
output line and site row) and counted as a success, so it does not keep
the unit from green; the site's units table counts it as passed. Plain
known mismatches still make a unit partial.
Ratchet (Rust and scripts/ci/difftest_baseline.py): known_mismatch and
cpp_defect may turn into each other or into a pass, never disappear,
defer, be rejected or fail; a baseline pass may not become either.
topology.brep_p10 read_Solids_fillet_ts is marked cpp_defect = UB-255
(glibc tcache reuse of a freed SmVertexProps in
SmHealData::Fix_CoinVertices); the unit is green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011gJAwcLRBPQXYLmvXd3pWf
CI runs ~200 times a day at ~47 runner-minutes each (Blacksmith, sponsored).
- New `changes` job (scripts/ci/changes.sh). code=false only for a pull_request or
merge_group whose diff touches nothing but prose no build/test/difftest reads
(plan/findings/, plan/upstream-issues/, plan/*.md, site/, docs/, assets/, top-level
*.md). Those skip clippy/test/wasm/publish-dry-run/deny and the difftest jobs; the
required checks report "skipped" (passing). fmt (with the frozen-archive guard) and
ci-scripts always run. Any doubt (no base, fetch failure) means code=true.
- Pushes to main (full=false) were already tested on the identical tree by the merge
queue: they now run only what publishes main's artifacts, difftest-sweep (the
difftest-reports the next PRs' exact-base ratchet reads) and the solids report step.
- `difftest` aggregate: passes on non-full runs when the sweep succeeded or was skipped;
on full runs it still requires every part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaDuBSC1mqS9DnQoEsr7LJ
- crates/step-import: ISO 10303-21 reader and AP203/AP214 B-rep mapper to a
neutral plain-data model (mm, resolved placements), written from the public
Part 21 syntax and Part 41/42 entity definitions; bounded entity count, list
nesting and reference depth; every reference checked; no allocation from
file counts. Flat f64 encoding (+ validating decoder) for the C++ side.
- harness/cpp/shim/step_import.cpp + solidcore-ffi binding: builds the SMLib
B-rep from the same encoded body (CreateCanonical geometry,
CreateFacesWithCurves, SmApiStitchIntoSolid, mass properties, SmApiHealBrep).
- difftest --step-corpus: the same recipe on the Rust port, each (body, kernel)
in a child process, stage-by-stage comparison at 1e-9 relative.
- scripts/fetch-steptools-corpus.sh: STEP Tools sample files into the gitignored
corpus/steptools/, SHA-256 pinned (never committed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaDuBSC1mqS9DnQoEsr7LJ
Keeps #983's known_mismatches.toml guard next to the archive guards in
check-frozen-archives.sh, and both sides' wording in AGENTS.md and
plan/findings/README.md. split_archives.py leaves the issues archive's own
"Frozen archive" notice (3 lines, dead relative link) out of the README; the
check lists those lines and compares the rest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaDuBSC1mqS9DnQoEsr7LJ
scripts/split_archives.py (run with python3 -I) splits the frozen plan/findings.md
(425 `## ` sections) and plan/upstream-issues.md (436 `#### ` entries) into one file
per record under plan/findings/ and plan/upstream-issues/, moves the archives' intro
and "Note: duplicate IDs" text into the READMEs, and deletes the archives.
`split_archives.py check` proves the split lossless against the archives at 4fe6f4d51.
site/build.py reads issues only from the directory (intro from its README);
check-frozen-archives.sh now fails if either archive comes back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaDuBSC1mqS9DnQoEsr7LJ
Known mismatches move from the shared crates/difftest/known_mismatches.toml
to crates/difftest/known_mismatches/<unit id>.toml (same [[mismatch]]
schema, the 24 entries copied verbatim). for_unit reads only that unit's
file (missing = no entries) and rejects an entry whose `unit` differs from
the file name. A test checks every file names a plan unit and is non-empty,
and check-frozen-archives.sh fails if the old shared file comes back.
SolidCraft cases were already one JSON file per issue; the conflicts came
from a hard-coded total case count in every_case_file_loads that each case
PR bumped. The test now derives the total from the files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaDuBSC1mqS9DnQoEsr7LJ
scripts/ci/check-frozen-archives.sh runs in the required fmt job and fails a
change that adds lines to plan/findings.md or plan/upstream-issues.md once the
base has the freeze notice (deletions pass). Updates to an earlier record,
including archive entries, are new addendum files that link to it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaDuBSC1mqS9DnQoEsr7LJ
Split the serial difftest job: the exact-base artifact is fetched and validated in
one job while the green sweep and the mandatory perturbation check run in their own
jobs. A new difftest --compare-reports mode applies the same ratchet as --baseline to
the sweep's finished reports, and the required check name 'difftest' becomes an
aggregator that fails unless every part succeeded.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011gJAwcLRBPQXYLmvXd3pWf
Resolve speculative queue bases before selecting the exact trusted push/main artifact, and allow 25 minutes for pending main CI. Keep ancestry provenance and add queue, retention, integrity and timeout regressions.
Co-Authored-By: OpenAI Codex <noreply@openai.com>
Mutation testing finds behavior no check constrains. For solidcore the useful
question is two-tier: a mutant matters only if neither the package's unit tests
nor the unit's difftest notices.
scripts/mutants.sh runs cargo-mutants (unit tests) on a module, a shard or a PR
diff, then patches each unit-test survivor into the working tree and replays it
against difftest --unit <id>, classifying it as caught-by-difftest or
both-tier-missed. Docker mode reuses one container for the replays; the script
prefers the pinned rustup toolchain and runs under nice with CARGO_BUILD_JOBS=2.
docs/mutation-testing.md documents the method, measured costs (~16 s/mutant,
~1 min/survivor), triage, and a CI proposal (per-PR in-diff tier 1, nightly
sharded tier 2), with the pilot findings: topology.topology's change-tracker
bookkeeping is unconstrained by both tiers.
reports/mutants/ is gitignored.
Co-Authored-By: Claude Code <noreply@anthropic.com>
A known mismatch may remain known or become a real pass, never deferred or rejected. Retained cases keep numerical value coverage. Legacy indexed integer mismatch evidence proves presence without fabricating omitted comparison counts; ambiguous known integer coverage fails closed.
Co-authored-by: Codex <codex@openai.com>
Preserve the original regression-gate author commits while retaining current CLI, report, known-mismatch and CI behavior. Validate trusted exact-base artifacts before applying case retention and the unchanged value-share floor.
Co-authored-by: Codex <codex@openai.com>
The primitives example takes --json <path> and records per shape whether it
built, the blocker (unit + upstream symbol), and each check (manifold, Euler,
counts, volume, area, bbox) with expected/actual/rel. error. The checks run
independently (evaluate); check() keeps its first-failure messages, and the
human-readable output is unchanged.
CI's test job runs it and, on pushes to main, uploads solids-report;
scripts/fetch-solids.sh fetches it with fetch-reports.sh's provenance rule
and the Pages build uses it. New Solids page (headline count, table of
primitives with checks and blockers, Corpus (real models) placeholder) and an
overview tile; both say "no data yet" without the report.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Use one borrowed/owned store for sweep and primitive access. Preserve caller ownership, source notifications and merge state across errors and unwinding; verify real point-to-edge construction against the original C++ kernel.
Co-Authored-By: Codex <noreply@openai.com>
- difftest no longer waits for the test job; cpp clippy moves to the clippy job and
the runner tests to the test job (both 8 vCPU), sharing a composite action that
clones upstream, enables sccache and installs the C++ reference.
- [profile.difftest.package.difftest] opt-level = 0: the ~300k-line runner crate took
~5 of the ~6 build minutes at opt-level 3; solidcore stays at 3. Reports are
byte-identical.
- difftest --jobs orders its queue by each unit's wall time in the previous run
(reports/difftest/times.tsv, uploaded with main's reports), so the slowest unit
starts first and the run ends at the longest unit's time.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158BfaB2oaoX64fskuKP1bm
- `difftest --digest-dir DIR [--digest-debug]` runs only `run_rust` and writes the
DIFFTEST_RUST_DIGEST-style per-case digest (and optionally the Debug output).
- On wasm32, solidcore-ffi links no C++: shims stay `env` imports, served by the stub
module from scripts/wasm_stub.py (SC_ERR_UNLINKED + call counter -> `needs-cpp`).
- scripts/wasm-digest.sh builds both targets, runs native and under wasmtime, compares.
- solidcore-ffi: MAX_LOOP_INDEX `1 << 40` overflowed on 32-bit targets (E0080).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9QwJFBTKLZ9FFK3nVd1X4
Selecting artifacts by head_branch alone accepted fork PR runs whose branch
is named main. Runs are now listed per workflow and filtered on event, path,
repository and head repository before their artifact is downloaded.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158BfaB2oaoX64fskuKP1bm
- plan/modules.toml -> plan/units/<id>.toml (scripts/units-split.py), read everywhere
through harness/plan/units.py (Python) and crates/difftest/src/plan.rs (difftest).
- reports/difftest/ untracked and gitignored: CI uploads main's set as the
difftest-reports artifact; scripts/fetch-reports.sh downloads it for the progress
checklist, the site and the --jobs queue order (fixture-size fallback otherwise).
- .gitattributes: merge=union for plan/findings.md and plan/upstream-issues.md.
- scripts/units-migrate-branch.sh moves an old branch's modules.toml edits into the
unit files after it merges main.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- KnotVector::basis_eval_into: N_BasisEval into a caller buffer (same
values, same errors); basis_eval wraps it
- srf_nurb::Surface::eval (N_SrfEvalPt) and crv_nurb::Curve::eval
(N_CrvEval) keep N on the stack, as upstream; one malloc/free less per
basis per point evaluation
- test: basis_eval_into equals basis_eval bit for bit, errors included
- scripts/perf/samply_top.py: self/inclusive/callee/allocator tables and a
Rust/C++ split from samply profiles (no GUI)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9QwJFBTKLZ9FFK3nVd1X4
scripts/cpp-ref-key.sh names a harness/cpp/build.sh output (upstream commit plus a
hash of CMakeLists.txt, build.sh, stubs/**, patches/**). The new cpp-reference
workflow builds it on Ubuntu 24.04 and publishes it as GitHub Release
cpp-ref-<key>; scripts/fetch-cpp.sh downloads and verifies it (curl, no token) or
falls back to build.sh. CI's difftest job fetches instead of building and runs
Rust and the cc-compiled shims through sccache.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfe7dodPG5rpuN9B8QcnuX
Pushes to main used the org's self-hosted runners (two of four are offline) while every PR
already ran on Blacksmith's sponsored runners. Move main onto Blacksmith too, give
difftest 16 vCPU, and remove the self-hosted-only steps and warm-target.sh. Caching is
actions/cache throughout. Fallback when the sponsorship ends: blacksmith-* labels back
to ubuntu-latest.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfe7dodPG5rpuN9B8QcnuX
- UnitResult.timing (not in the report): wall time of case building,
run_cpp, run_rust, compare and the slowest Rust case; main prints
`TIME <unit> ...` to stderr per unit
- DIFFTEST_RUST_DIGEST=<dir>: writes <dir>/<unit>.digest with one
`<case>\t<fnv1a64 of format!("{rust_output:?}")>` line per case, so two
builds can be proved bit-identical; off by default
- README: --green takes ~35 min on 16 threads (not ~2 min), TIME line,
digest, and scripts/perf (ptrace sampler + analyzer, no sudo needed)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9QwJFBTKLZ9FFK3nVd1X4
Review (#215): a failed fetch or extraction of the base commit's reports only
warned, so --green then ran without --baseline and the ratchet was silently
off. Move the step into scripts/ci/difftest-baseline.sh: a pull request must
have a base; for a real base, fetch, extraction and "has reports" must all
succeed or the job fails. Only a push without a base commit (all-zero
`before`) or a manual run skips the ratchet, with a notice.
scripts/ci/test-difftest-baseline.sh exercises it in a throwaway repo (real
base, failed fetch for PR and push, base without reports, missing/zero base,
bootstrap cases), run by a new ci-scripts job. Restoring the old warn-only
behaviour makes it fail.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ci: run trusted jobs on the self-hosted runners
Pushes to main and pull requests from branches of this repository now run fmt,
clippy, tests, wasm, the publish dry run and difftest on the org's self-hosted
runners, which keep their Cargo build directory and C++ reference build between
jobs. cargo-deny (a container action), fork pull requests and Run workflow stay
on GitHub's runners. The C++ cache on GitHub is no longer shared with the
self-hosted build, whose newer compiler and glibc must not reach GitHub's runners.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ci: re-run on the self-hosted runners
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- difftest: serde_json float_roundtrip, so inputs parse correctly rounded like strtod
(1610 corpus numbers were 1 ULP off); regenerated reports/ssi-corpus.json and the five
difftest reports whose values_compared counts moved (all still pass; --green 0 failed)
- ATTRIBUTION.md: ssi-corpus copyright line from its LICENSE-MIT
- surface.plane_p1/p2: record that natural_uv_domain does not cache the NURB as
upstream GetNaturalUVDomain does, which changes IntersectWithPlane's parallel branch;
findings entry moved under surface.plane_p1, follow-up in modules.toml notes
- engine: Rust side rejects what the C++ encoder rejects; torus deferrals name
surface.surface_p2 (owner of SurfaceKind); shim degree bound NL_MAXDEG
- fetch script: refuse an existing checkout with local changes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BY8UpXKuMci15rAWGXsbsm
- scripts/fetch-ssi-corpus.sh pins b04c8a6093a8b3505d865d1dd874ec3879ccb1f9, checks
the existing checkout's origin URL and requires HEAD to equal the pin
- plan/upstream-issues.md: the 38 C++ misses on the corpus, grouped (WR-74..WR-80)
- plan/findings.md: scorer agreement and the plane NURB state note
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BY8UpXKuMci15rAWGXsbsm