land the limits stack on main: #44, #45, #46 (merged into their bases before retarget) - #52
Merged
Merged
Conversation
…deciders Every gate, warn, grant and egress receipt carries a matcher version, and it is the field a reader uses to ask whether two decisions were made by the same rules. It hashes thirteen top-level functions plus the two rule tables. Function.toString returns a function's own source and nothing it calls, so the helpers that actually decide self-mod are outside it: the protected-path list, both self-mod matchers, the path normalizer, the segment matcher, the brace expander and the prose strippers. The dispatcher that IS hashed only names the matchers it calls. Change the protected list or the way a path is folded before matching, and the gate stops a different set of actions while the stamp stays byte-identical. The silence runs the wrong way: a matcher weakened between two runs keeps stamping the old, stronger version. Enforcement is unaffected; the trace misdescribes it, which is why this is filed as a witness defect. Measured, not read off the source. The new test asserts the absence directly against the hashed inputs, with controls asserting the hashed bodies are present so the block cannot pass vacuously. Stamp on this build: matcher/1 95291ff6385151ca. The stamp was introduced 2026-08-09 (b1b7bf8); the protected list gained an entry on 2026-08-23 and the version did not move. The repair is core and is not attempted here: name the helpers in parts and bump the schema marker to matcher/2, since the hashing method changes and a value moving on its own would be indistinguishable from a rule edit. Suite 962 pass / 0 fail (8 new). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…record
Following limit 63's stated residual ("behavior that reaches the decision
from outside the policy module would remain unstamped") to where it leads,
and finding that the instrument which closes it is already built.
Two code identities exist. matcherVersionHash() covers named functions in
src/policy/index.js and is on every receipt. computeSourceDigest() covers
all 50 .js files under src/ and bin/ - including src/gate, src/grant,
src/chain, src/store and bin/hook-pre-tool-use.js - and is attached only to
MCP tool responses, which are discarded when the call returns.
So the ephemeral artifact carries the strong identity and the permanent one
carries the weak one. Change the gate or the hooks and every receipt after
the change is byte-comparable with every receipt before it.
Measured: build digest 47ed7876d2652e68 over 50 files / 522,651 bytes;
matcher stamp matcher/1 95291ff6385151ca; exactly two consumers of the
digest in the tree, neither of which writes to the chain.
Ships KNOWN-LIMITS 64 plus a 4-assertion test. Every absence claim is
paired with a control (the same file IS asserted to carry matcherHash, and
the MCP server IS asserted to carry the build stamp) so the block cannot
pass vacuously. Suite 966/966, up from 962.
The repair touches src/gate and bin/hook-* and therefore queues for a
signing sitting rather than riding along with this disclosure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s in The KNOWN-LIMITS pin names the last commit that touched the source tree, on purpose, so that stamping (which edits only the log) cannot invalidate itself. The consequence was not carried through: a commit that edits only the log does not move that commit either, so the check cannot see it. Measured on a synthetic tree, with the shipped writePin/checkPin and the commit resolution reproduced verbatim. Appending an entry, deleting an entry, and reversing an existing claim were each reported "current", exit 0. Only a source change was reported "diverged". The checker does not merely fail to complain about an edited log; it certifies it. Adds the entry and five characterization tests that hold the gap in the suite rather than only in prose. They are written to fail when the repair lands, which is the prompt to rewrite them as the assertions for the fixed behaviour. The repair is drafted and gated: add a body digest to the pin, covering the file with the pin block removed so stamping stays stable, and report a matching commit with a mismatched digest as a third status. The gate refused it as a self-modification of the source tree, correctly, so it queues for a signing sitting rather than riding along here. Suite 971 pass / 0 fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…that do exist disagree Four limits found 2026-09-02 on a live three-child concurrent dispatch, filed the day they were found per the confession-log rule. 66: subagent tool use produces zero chain rows while the parent's calls in the same minutes produce one every time. A hole in enforcement under a herded posture, not only in bookkeeping. 67: the harness's reported per-child token figure disagrees with the child's own transcript by ~7,400 on two of three calls. Price from the transcript. 68: the reader that found the children globs an undocumented harness path with no schema check across 91 lines, so its failure mode is a false zero. 69: no dollar figure is reported anywhere; every one is computed against a price table dated four days earlier, so the date has to travel with the number. Tests: 971 pass / 0 fail, including the read-only numbering test, which now reads 1..69 contiguous. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…epending on who runs it Entry 66 shipped at its declared floor: it confirmed the witness records nothing a subagent does, and explicitly said reachability by an honest agent was NOT established. That probe has now run. Every subagent transcript on this machine (3,962 tool calls, 1,073 child transcripts, 104 parent sessions, six weeks) was replayed through the repo's own matcher under an all-gate policy. 519 calls (13.1%) would have gated the parent, in 38.5% of parent sessions, 377 of them after the gate was armed. Entry 66 comes off its floor with those numbers. The asymmetry the measurement exposes is filed as its own entry 70: the protected-path rules are enforced against the parent's calls and structurally unenforced against a child's. Both blocks carry the caveat rather than the headline: the nine core-file edits found inside child sessions all predate the arming of the gate by one day and are not evidence that anyone walked around an armed gate. Suite 971 pass / 0 fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nt-blindspot limits 66-69: the subagent path leaves no chain row, and the numbers that do exist disagree
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.
PRs #44, #45 and #46 were stacked and merged in one pass; GitHub had not yet retargeted them to main when the merge ran, so they landed in their base branches. This PR carries limits 63 to 70 and the entry-66 amendment onto main. No new content.
🤖 Generated with Claude Code