fix(seal): --save maintains every seal naming the spec, and never twins by case - #3419
Merged
Conversation
…ns by case Closes #3418 Two defects in one place, both found by being bitten rather than by review. A spec can own more than one seal file and `--save` wrote one. Measured over .trinity/seals: 1313 seals name 728 distinct specs, and 547 specs carry more than one -- 501 of them the same module under two names, `<Module>.json` beside `<dir>_<Module>.json`, left by a naming scheme that changed. Refreshing 116 stale seals through this command in #3416 reached 58 and stopped for exactly that reason; editing four specs in #3417 produced ten stale seals where --save would have fixed four. The duplicates are not deletable: math_compare.rs opens .trinity/seals/PellisFormulas.json by its bare name and that file is one of a pair. So the tool maintains them. Only hashes and the timestamp are rewritten; each file keeps its own `module`, because the file is NAMED after that field and overwriting it would leave a seal whose name and contents disagree. Second defect, invisible on macOS: seal_file_path derives `ar_restraint.json` where `ar_Restraint.json` is tracked. 751 filenames match the derived name exactly, 4 differ only by case, 558 use the older scheme. A case-insensitive filesystem resolves the pair to one file; the case-sensitive one CI runs on would grow a SECOND seal for the same spec, and neither would look wrong. An existing file now wins the lookup. The test for that half CANNOT FAIL on this machine and says so. Mutation proved it -- making the lookup case-sensitive leaves the test green here, because creating `Aa` then `aA` yields one file. It probes the filesystem and skips loudly rather than reporting a pass it did not earn; it discriminates on Linux, where CI runs. The other test does kill its mutant here. The child runs with its own working directory rather than set_current_dir, which is per-process and would race every other test in this binary under the default parallel runner. Left alone and filed: 93 specs carry a seal whose name is not an identifier -- specs/tri/utils/logger.t27 has `"[]const u8".json`, a seal named after a type string. compiler.rs is untouched, so FROZEN_HASH is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gHashTag
enabled auto-merge (squash)
September 7, 2026 21:36
Contributor
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
gHashTag
added a commit
that referenced
this pull request
Sep 7, 2026
…s did (#3421) Closes #3420 rings/ring-090-rust says in its own doc comment that it is "faithful to the spec" specs/fpga/simulator.t27. A differential harness driving every shared function from both modules on the same inputs -- 14 u32 values crossed with 3 name strings -- disagreed on 126 of 1190 cases. sim_time_ns 4/10 2_000_000_000 -> hand 4294967295, spec 2820130816 sim_time_us 1/10 -> hand 4294967, spec 2820130 sim_time_ms 1/10 -> hand 4294, spec 2820 cycles_for_time_ns 0/10 The hand-written model widens to u64 and then saturates. The spec widened and then narrowed with a bare `as u32`, which wraps: at 2_000_000_000 cycles on the default 100 MHz clock, 20_000_000_000 ns becomes 2_820_130_816. The spec states the assumption that fails, in a comment on the line above: "Widen the intermediate to u64 and narrow the (small) result back." It is not always small. With the guard, the harness reports 1190 of 1190 agreeing, and its control confirms it can see a difference. Context for why this was worth looking for. Ten of the seventeen rings/* crates name a spec that exists in this repository, and the overlap between them is wildly uneven: ring-090 22 hand items, 21 shared, 0 spec-only <- 15/16 signatures identical ring-097 18 hand items, 11 shared, 0 spec-only ring-099 9 hand items, 4 shared, 0 spec-only ring-098 12 hand items, 5 shared, 2 spec-only ring-088 3 hand items, 0 shared <- names gf16.t27, unrelated ring-101 5 hand items, 0 shared <- same ring-090 is the closest pair in the repository and it had still drifted in BEHAVIOUR, which a comparison of names and signatures alone could never show. Two tests are added to the spec in its own language rather than around it: the saturating case and an exact case below the ceiling. Both reach the Zig output. The seal was refreshed by `t27c seal --save`, which reported "and 1 other seal file(s) naming the same spec" and updated both fpga_Simulator.json and Simulator.json -- the repair from #3419 on its first real case, after check_seal_currency.py from #3416 named the stale hashes. Co-authored-by: lab <lab@example.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3418
Two defects in one place. Both were found by being bitten — twice, in my own last two merged changes — rather than by reading the code.
1. A spec owns more than one seal;
--savewrote oneThe pattern is a naming scheme that changed:
<Module>.jsonbeside<dir>_<Module>.json.--savereached 58 and stopped. The other 58 had to be rewritten by hand.--savewould have fixed four.The duplicates are not deletable.
bootstrap/src/math_compare.rsopens.trinity/seals/PellisFormulas.jsonby its bare name, and that file is one of a pair withphysics_PellisFormulas.json. So the tool maintains them.Only hashes and the timestamp are rewritten. Each file keeps its own
module, because the file is named after that field — overwriting it would leave a seal whose name and contents disagree, which is the defect being repaired rather than a second copy of it. The command says how many others it touched; one that silently wrote to extra files would be worse than one that writes too few.2. The derived name can differ from the tracked file only in case
seal_file_pathcomputes<parent>_<module>.json. Forspecs/ar/restraint.t27that isar_restraint.json— whilear_Restraint.jsonis what is tracked.<module>schememacOS resolves those to one file and hides it. On the case-sensitive filesystem CI runs on,
--savewould create a second seal for the same spec, and neither would look wrong. An existing file now wins the lookup.The test for the second half cannot fail here — and says so
Mutation proved it: making the lookup case-sensitive leaves the test green on this machine, because creating
AathenaAyields one file. Rather than keep a test that cannot distinguish the guard from its absence, it probes the filesystem and skips loudly:It discriminates on Linux. The other test — a stale duplicate must be refreshed, and must keep its own
module— does kill its mutant here.The child process runs with its own working directory rather than
std::env::set_current_dir, which is per-process and would race every other test in this binary under the default parallel runner.Left alone and filed
93 specs carry a seal whose name is not an identifier at all:
specs/tri/utils/logger.t27has"[]const u8".json,utils_"[]const u8".jsonandutils_TriLogger.json— a seal named after a type string.compiler.rsis untouched, soFROZEN_HASHis unchanged.