Skip to content

fix(seal): --save maintains every seal naming the spec, and never twins by case - #3419

Merged
gHashTag merged 1 commit into
masterfrom
seal/canonical-names
Sep 7, 2026
Merged

fix(seal): --save maintains every seal naming the spec, and never twins by case#3419
gHashTag merged 1 commit into
masterfrom
seal/canonical-names

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 7, 2026

Copy link
Copy Markdown
Owner

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; --save wrote one

seal files 1313
distinct specs they name 728
specs carrying more than one 547
of those, same module under two names 501

The pattern is a naming scheme that changed: <Module>.json beside <dir>_<Module>.json.

The duplicates are not deletable. bootstrap/src/math_compare.rs opens .trinity/seals/PellisFormulas.json by its bare name, and that file is one of a pair with physics_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_path computes <parent>_<module>.json. For specs/ar/restraint.t27 that is ar_restraint.json — while ar_Restraint.json is what is tracked.

filename vs derived seals
identical 751
differs only by case 4
older bare-<module> scheme 558

macOS resolves those to one file and hides it. On the case-sensitive filesystem CI runs on, --save would 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 Aa then aA yields one file. Rather than keep a test that cannot distinguish the guard from its absence, it probes the filesystem and skips loudly:

SKIP save_writes_into_an_existing_case_variant: this filesystem is case-insensitive,
so the assertion cannot distinguish the guard from its absence

It discriminates on Linux. The other test — a stale duplicate must be refreshed, and must keep its own moduledoes 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.t27 has "[]const u8".json, utils_"[]const u8".json and utils_TriLogger.jsona seal named after a type string.

compiler.rs is untouched, so FROZEN_HASH is unchanged.

…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
gHashTag enabled auto-merge (squash) September 7, 2026 21:36
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-07 21:36:14 UTC

Summary

Status Count
Total Open PRs 16
PRs with Failing Checks 13
PRs with All Checks Green 3
READY 2
FAILING 13
PENDING 0
NO CHECKS YET 0

These columns do not partition: 2 + 13 + 0 + 0 = 15, and there are 16 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=9c910e4dfae2 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@gHashTag
gHashTag merged commit 6fd970d into master Sep 7, 2026
29 of 31 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

t27c seal --save writes one of a spec seal files, and can twin it by case

1 participant