Repository navigation
seal --verify never checks dependency hashes; 21 claw seals record stale ones #10727
Description
What
t27c seal <spec> --verify does not compare the dependency hashes that a seal records. It checks the spec's own spec_hash and the four gen_hash_* values, and nothing else.
As a result, 21 claw seals on master record 25 dependency hashes that no longer match their dependency, and every one of them still verifies.
Negative control (Railway lab, /root/t27c-master, 2026-10-19)
On .trinity/seals/claw_ClawMergeLane.json:
| Edit | seal specs/claw/merge_lane.t27 --verify |
|---|---|
| none | rc 0, "all hashes MATCH" |
the specs/claw/text.t27 dependency spec_hash set to sha256:000000... |
rc 0, "all hashes MATCH" |
the seal's own spec_hash set to sha256:000000... |
rc 1 |
The verifier prints a line for spec_hash and for each gen_hash_*. It prints no line for the dependencies.
What is stale on master now
The recorded dependency hash differs from sha256(git show origin/master:<dep>) in these seals:
text.t27: CheckLatest, CheckState, Evidence, Gates, LedgerUnion, MergeLane, Preview, PushGate, QueueCost, RequiredRed, SarifPins, Service, ShapeRace, Suggest, SuggestFeed, TokenShapesarif.t27: Refresh, SarifFeed, SarifPinsgithub_surface.t27: ControlPinsevidence.t27andt27f/spec_graph.t27: EvidencePinsgates.t27: GatesPinsrefresh.t27: PushGatesuggest.t27: SuggestFeed
Most of the text.t27 entries date from #10714, which changed text.t27 without resealing its importers. That PR passed CI because nothing reads these hashes.
Why it matters
A seal says "this spec, built against these dependencies, generated this code". If only the first half is checked, a dependency can change underneath a sealed spec, and the seal goes on saying the spec was checked against it.
Possible fixes
- Verifier.
seal --verifyhashes each recorded dependency and fails on a mismatch, printing one line per dependency. This is a t27c change (Rust), so it needs the owner'sowner-approved-foreignlabel; the claw lane does not make it. - Data, until then. Reseal the stale seals (19 once test(claw): kill the real check_state.t27 and merge_lane.t27 mutation survivors #10726 merges) with
seal --save, with zig on PATH and without--force. This is tool-written data, so it is allowed under the t27-only rule. The two seals in test(claw): kill the real check_state.t27 and merge_lane.t27 mutation survivors #10726 are already resealed this way.
Found while resealing for #10724 / PR #10726: the seal diff changed the recorded text.t27 hash, although that PR does not touch text.t27.
User Scenarios
- Given a claw seal that records dependency hashes, when running
t27c seal <spec> --verifywith a dependency whose current hash no longer matches the recorded hash, then the command fails with a non-zero exit code and prints a mismatch line for that dependency. - Given a claw seal where all recorded dependency hashes match their current dependencies, when running
t27c seal <spec> --verify, then the command exits 0 and prints one line per dependency hash checked showing MATCH.
Requirements
- FR-001: The verifier MUST compute the current hash of each recorded dependency and compare it against the hash stored in the seal.
- FR-002: The verifier MUST print one line per dependency hash checked, similar to the existing lines for
spec_hashandgen_hash_*. - FR-003: The verifier MUST exit with a non-zero code if any dependency hash mismatches.
Success Criteria
t27c seal specs/claw/merge_lane.t27 --verifyexits 0 and prints dependency hash lines for all recorded dependencies when all hashes matcht27c seal specs/claw/merge_lane.t27 --verifyexits non-zero and prints a MISMATCH line fortext.t27when its recorded hash in.trinity/seals/claw_ClawMergeLane.jsonis altered to an incorrect value- The verifier output includes exactly one line per dependency (e.g., "dependency specs/claw/text.t27: MATCH")
Boundary
- cli/tri/src/seals.rs
- .trinity/seals/claw_ClawMergeLane.json
- specs/claw/merge_lane.t27
- specs/claw/text.t27
Boundary
Fix 2 (data, done in #10729):
- .trinity/seals/claw_ClawCheckLatest.json
- .trinity/seals/claw_ClawControlPins.json
- .trinity/seals/claw_ClawEvidence.json
- .trinity/seals/claw_ClawEvidencePins.json
- .trinity/seals/claw_ClawGates.json
- .trinity/seals/claw_ClawGatesPins.json
- .trinity/seals/claw_ClawLedgerUnion.json
- .trinity/seals/claw_ClawPreview.json
- .trinity/seals/claw_ClawPushGate.json
- .trinity/seals/claw_ClawQueueCost.json
- .trinity/seals/claw_ClawRefresh.json
- .trinity/seals/claw_ClawRequiredRed.json
- .trinity/seals/claw_ClawSarifFeed.json
- .trinity/seals/claw_ClawSarifPins.json
- .trinity/seals/claw_ClawService.json
- .trinity/seals/claw_ClawShapeRace.json
- .trinity/seals/claw_ClawSuggest.json
- .trinity/seals/claw_ClawSuggestFeed.json
- .trinity/seals/claw_ClawTokenShape.json
Fix 1 (verifier) is in t27c's seal command, which is Rust. It needs the owner's owner-approved-foreign label, and the claw lane does not make it.
User Scenarios
- Given a seal whose recorded dependency
spec_hashdiffers from the dependency file on disk, whent27c seal <spec> --verifyruns, then it exits non-zero and names the dependency. - Given a seal whose dependencies all match, when
--verifyruns, then it prints one MATCH line per dependency and exits 0.
Requirements
- FR-001:
seal --verifyMUST hash every recorded dependency and fail on a mismatch. - FR-002: Until FR-001 lands, stale dependency hashes on master MUST be resealed with
seal --save, without--force.
Success Criteria
- The stale-dependency scan over
.trinity/seals/claw_*.jsonon master finds 0 files with a stale dependency hash (true after chore(claw): reseal 19 seals with stale dependency hashes #10729). - With a dependency hash zeroed,
t27c seal specs/claw/merge_lane.t27 --verifyexits non-zero (open: needs fix 1).
Status
Fix 2 is done (#10729, merged). Fix 1 stays open and waits for the owner's label.
Update 2026-10-19: the whole repository, not only claw
A scan of every seal on master 2520ee1 compared each recorded dependency hash with sha256(git show HEAD:<dep>). It found 154 stale dependency entries:
- 2 in claw seals (CheckLatest and RequiredRed, which still cited
check_state.t27from before test(claw): kill the real check_state.t27 and merge_lane.t27 mutation survivors #10726). chore(claw): reseal 19 seals with stale dependency hashes #10729 resealed them against a lab tree that predated test(claw): kill the real check_state.t27 and merge_lane.t27 mutation survivors #10726. PR test(claw): kill the real check_latest.t27 mutation survivors (#10739) #10741 fixes both. - 152 entries in 131 seals outside
specs/claw. The dependencies cited most often:
| Stale entries | Dependency |
|---|---|
| 74 | specs/base/types.t27 |
| 8 | specs/verified/corpus_receipt.t27 |
| 7 | specs/math/constants.t27 |
| 5 | specs/tri/t27b/type_text.t27 |
| 5 | specs/base/ops.t27 |
| 4 | specs/trinet/tri-bridge.t27 |
| 4 | specs/compiler/parser.t27 |
The claw lane leaves those 131 seals to the lanes that own them. Each one verifies today only because seal --verify skips dependencies (fix 1 above). So a verifier fix would turn all 131 red on its first run: it has to land together with one reseal sweep.
Activity
github-actions commentedon Oct 19, 2026
Closed by the issue-shape gate (specs/policy/issue_shape.t27, #7498): an issue must have the template's sections. Edit the body; an edit that passes reopens it.
- specs/x.t27); without it the Queen reserves nothing and no bee can take the issueFR-001: ... MUST ...exit 0, or a backticked commandchore(claw): reseal 19 seals with stale dependency hashes (#10729)
github-actions commentedon Oct 19, 2026
The shape check passes now (specs/policy/issue_shape.t27); reopened.