Conversation
The frozen T0 names an accepted harness identity that, on its own branch, does
not exist yet: `104c384d01bf` arrives with the memory-semantics repair, which is
a different pull request. That is fine while the merge order holds — and an
order held by agreement is one stray click from becoming an archaeological
artifact, with a document saying FROZEN in a tree where its referent is absent.
So the order is checked instead of promised, and checked against the TARGET
TREE rather than against pull-request numbers: a number proves someone pressed a
button and says nothing about what the merged tree contains. The gate refuses
unless, at the commit a merge would produce:
- T0 declares FROZEN *and* collection_authorized: true;
- the harness digest recomputed from that tree's own instrument sources, by
the frozen formula, equals the one T0 names;
- the policy freeze, the design constants and the training preregistration all
bind that same digest — present is not the same as re-bound;
- the capture, qualification and binding tools exist and actually enforce the
campaign link and the authority state;
- the step-7 note still revokes the automatic collection authority, so hosts
plus a binding cannot again be enough to start a clock.
The expected digest is read out of the frozen T0 and recomputed from the tree; a
control proves no digest literal lives in the gate's own source, because a gate
trusting its own constant would be checking itself.
Exercised against real merges, not only fixtures. The full chain
(#355 -> #356 -> #353 -> #354) is allowed. #354 alone is refused on three
predicates at once — the instrument hashes to 562a7f7232da where T0 names
104c384d01bf, the bindings are stale, the machinery is absent. #355 plus #354
without the machinery is refused on one. T0-0 turns out to need no predicate of
its own: #353 is an ancestor of the freeze commit, so it cannot be skipped.
merge gate controls: 7 passed, 0 failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The first revision proved words. `step7_machinery_present` asked whether the two tool files existed and whether certain identifiers appeared in them, which a file containing `def check_campaign_link(): pass` satisfies — and the fixture that was supposed to exclude that world was exactly that file. A check for the name of a mechanism is not a check of the mechanism. `step7_machinery_enforces` now extracts the target tree's own hostqual and execbinding, runs them in a subprocess against a synthetic campaign, and requires all four attacks to be refused: a link naming another execution binding, a freeze edited after the link was made, FROZEN with collection_authorized false (on both readers), and a campaign swapped between preflight and postflight. A tool that accepts any of them fails the gate, and the refusal says which attack got through. `steps_4_5_6_rebound` searched each artifact's text for the digest. The right digest sitting in any field — a comment, a history entry, a field nobody binds — was read as a binding. It now walks the exact path each artifact actually binds at: measurement_harness_digest in the policy freeze, bound_measurement_harness_digest in the design constants, bindings.measurement_harness_digest in the training preregistration. Anywhere else is not a binding, and the refusal names the path and both digests. Fixtures ship the real tools; the two attack controls mutate one enforcement point each and require the gate to notice. Twelve controls, four of them new: a permissive link check is refused, an authority check that always passes is refused, a stale binding with the right digest in a decoy field is refused, and correct exact fields are allowed. Against the real merges: the full chain is allowed (rc 0); #354 without #355 and #356 is refused on three predicates (rc 1); #354 with the instrument but without the step-7 tools is refused on one (rc 1). 30d2f32 is untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh
…thing Two holes in the proof, not in the mechanism. The authority table exercised three of the four states. R15 defines all four, and `NOT_FROZEN+false` was the one nobody checked — so a future tree in which a reader quietly authorised it would have passed the gate. It is in the table now, and `DEAD_AUTHORITY` has a twin that breaks `execbinding.t0_at` instead of `hostqual.bind_t0`: the gate's message claims enforcement on both readers, and a control that only breaks the first left half that claim resting on nothing. Both mutations must now be caught naming all three forbidden states. The campaign-swap witness asserted only that a swapped campaign is inadmissible. A `session_admissibility` returning `admissible: False` unconditionally would have satisfied it — the witness would have read "nothing is admissible" as "the swap was caught". It now requires the unchanged campaign to survive preflight to postflight first, and requires the swapped one to be refused by a reason naming the campaign link, so a broken closing probe cannot stand in for the continuity check. Two mutations prove each half bites: removing only the preflight/current campaign-link comparison while leaving `check_campaign_link` intact, and a postflight that refuses everything. A mutation that changes no bytes now raises rather than passing as a second positive control nobody reads as one. 15 controls, 0 failed. The probes are unchanged: full chain rc 0, #354 alone rc 1 on three predicates, #354 without the tools rc 1 on one. 30d2f32 is untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh
Three ruff findings, all mine: an unsorted import block, one over-long line inside the witness source, and an unpacked name a control never reads. The long line is inside WITNESS_SOURCE, so wrapping it edits the script the gate runs against the target tree's tools. Hoisting the identity into a local changes nothing it does, and the controls and probes were re-run to say so rather than assumed: 15 merge gate controls pass, host qualification 26/26, and the three real merge probes still come back 0 / 1 / 1 with the same predicates. This is a lint pass only. No predicate, no attack and no message moved, and 30d2f32 is untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh
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.
Stacked on #356. The last structural piece before #263-B: the merge order stops being an agreement and becomes a predicate on the target tree.
The problem this closes
The frozen T0 (#354,
30d2f32) names an accepted harness identity —104c384d01bf— that arrives with the memory-semantics repair in #355. On the T0 branch alone,perf_baseline.pyis still the pre-repair version and hashes to562a7f7232da. Nothing is wrong with the contract: it describes the state of the merged history. What is wrong is that the order holding it together lives in a conversation, and an order held by agreement is one stray Merge click from becoming an archaeological artifact — a document saying FROZEN in a tree where its referent does not exist.What the first revision got wrong
Two defects, both found by the owner against the live PR rather than by these controls, and both of the same shape: the gate read descriptions of enforcement instead of enforcement.
step7_machinery_presentasked whether the two tool files existed and whether certain identifiers appeared in them. A file containingdef check_campaign_link(): passsatisfies that — and the fixture meant to exclude that world was precisely that file. The control demonstrated the false positive it existed to prevent.steps_4_5_6_reboundsearched each artifact's text for the digest. The right digest anywhere in the file — a comment, a history entry, a field nobody binds — was read as a binding.Neither was a slip in a corner. A gate that checks for the name of a mechanism licenses exactly the tree it was built to refuse.
What the gate checks now
Against the commit a merge would produce, never against PR numbers. A number proves someone pressed a button; it says nothing about what the merged tree contains.
Exit 0 only when all hold; exit 1 on any refusal. A refusal means the frozen contract would become reachable from a tree that cannot satisfy it, and the merge is refused however mergeable the forge believes it to be.
The binding is read at one path per artifact, and only there
p022-263a-policy-freeze.jsonmeasurement_harness_digestp022-263a-design-constants.jsonbound_measurement_harness_digestp022-263a-training-preregistration.jsonbindings.measurement_harness_digestAnywhere else is not a binding. The refusal names the path and both digests:
p022-263a-policy-freeze.json: measurement_harness_digest is 562a7f7232da, not 104c384d01bf.The machinery is run, not recognised
step7_machinery_enforcesextractshostqual.pyandexecbinding.pyfrom the target commit, runs them in a subprocess against a synthetic campaign, and drives them through the whole authority table and the whole campaign path — negative and positive, because a refusal is only evidence if acceptance was possible.hostqual.bind_t0execbinding.t0_atFROZEN+falseNOT_FROZEN+trueNOT_FROZEN+falseFROZEN+trueThen, on the campaign:
session_eligibility→session_admissibility;Step 2 is what stops the witness reading "nothing is admissible" as "the swap was caught". A tool that fails any of this fails the gate, and the refusal says what happened —
hostqual accepted NOT_FROZEN+false, orthe swapped campaign was refused, but by no reason naming the campaign link.The digest is not hard-coded. It is read out of the frozen T0 and recomputed from the target tree, and a control proves no 64-hex literal exists in the gate's own source — a gate trusting its own constant would be checking itself.
Controls
Fixtures now ship the real tools; each attack control mutates exactly one enforcement point and requires the gate to notice. A mutation that changes no bytes raises, rather than passing quietly as a second positive control nobody reads as one.
The last two mutations are the sensitivity proof for the campaign witness: one shows the negative half is specific to continuity, the other shows the positive half is load-bearing.
merge gate controls: 15 passed, 0 failed, includingcontrol-inventory-complete(the documented list and the executed set are the same set).Exercised against real merges, not only fixtures
#355 → #356 → #353 → #354(270e3c1)#354alone (8c87118)562a7f7232da, all three bindings stale, machinery absent#355 + #354, no machinery (2e1a6c7)T0-0 needs no predicate of its own: #353 is an ancestor of the freeze commit, so it cannot be skipped.
Known limits
The witness proves behavioural compatibility at the named enforcement points, not byte identity:
hostqual.pyandexecbinding.pyare deliberately not pinned by SHA, so a comment or a diagnostic can be fixed without turning the gate into a museum alarm. It is also not a complete threat model — it proves these attacks are refused, not that no other attack exists.The gate is a script, not yet a required check. Branch protection on this repository has no ruleset, so nothing currently prevents a merge that this gate refuses. Wiring
mergegate.pyas a required status check is an owner act and is not part of this PR.Scope
scripts/step7/mergegate.pyand its controls only. No instrument change, no digest movement, and #354 is untouched —30d2f32is terminal and is not to be amended, rebased or force-pushed.Regression on this host: step-7 qualification 26/26, envcapture 11/11, perf instrument 16/16, calibration policy 10/10, calibration freeze 7/7, calibration constants 4/4, training preregistration 9/9. Four unrelated suites (
byte_variants,repro_fixtures,cli_ownir_fixtures,checkpoint_status) fail identically on the frozen T0 branch — they need a built Rust engine and dotnet, which this host does not have.🤖 Generated with Claude Code
https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh