feat(ratchet): verify first contact and the guard's own routing table - #2494
Conversation
The anchor ratchet checked docs, skills and runbooks — but not the two surfaces
that decide whether an agent entering this repo succeeds or breaks something.
FIRST CONTACT. .claude/CLAUDE.md, BOOTSTRAP.md, PATTERNS.md, CATALOG.md, the root
CLAUDE.md and AGENTS.md are ALWAYS LOADED. An agent reads them before it reads
anything else, so a dead reference there is the most expensive kind: the agent is
misdirected at step one, improvises, breaks something, and the model gets blamed
for the field's error. They were not scanned. Now they are, along with
.claude/context, .claude/commands and .claude/agents.
Four real ghosts surfaced immediately, all in always-loaded files:
.claude/CLAUDE.md `make -C pmoves worktree-sitrep-strict`
— described in-file as "Authoritative worktree sitrep.
Prefer this to per-worktree spot checks." No such target.
.claude/PATTERNS.md `worktree-sitrep`, `worktree-sitrep-strict`
.claude/BOOTSTRAP.md `health-quick`
.claude/learnings/ is excluded alongside archive/: a learnings file records what
was true in a past session. It is a log, not a promise.
THE GUARD'S OWN ROUTING TABLE. .claude/hooks/damage-control/patterns.yaml is the
best thing in this repo — 121 entries, each carrying why-this-is-wrong, the
correct path, the valid parameter set, and how to verify afterwards, with
ask:true rather than a hard no. It routes a blocked agent to safety. But nothing
checked where it routes TO. New GHOST_ROAD class points the same GHOST_TARGET
question one layer inward, at the thing that answers that question for everyone
else. Two of its 13 offered targets do not exist:
mini-cli stop/down/reset -> "use make -C pmoves targets" GHOST
schema changes -> "apply with make -C pmoves db-migrate" GHOST
A well-behaved agent that follows the offered road lands in a wall at the moment
it is least able to recover.
TWO DISCRIMINATORS, so the gate does not punish the docs doing this right.
.claude/PATTERNS.md:38 and AGENTS.md:28 both carry a
"| Raw command (blocked) | Known Road |" table mapping `docker volume rm` ->
`make -C pmoves volume-reset SERVICE=...`. They name the dangerous operation in
order to route away from it. A line that names its Known Road on the same line,
or that frames the command as blocked/dangerous/anti-pattern, is documentation
rather than instruction. Semantic, deliberately not a file whitelist — a
whitelist would also hide a real instruction hiding in the same file.
Also fixed: the first cut tested the inline SPAN rather than the LINE it sits on,
so the road sitting next to the command in a table row was invisible and both
correct docs were flagged. And `up-<service>` placeholders render as `up-` after
extraction; the trailing hyphen is the tell.
One bug worth recording because it has now bitten twice in one day: writing a
literal backslash-b into the discriminator through a heredoc escape-interprets it
to 0x08, silently replacing the word boundary with a backspace. The regex then
matched nothing and the exemption looked broken rather than absent. Same class as
the corruption that turned `feat/` into `eat/` in the claim register (#2476).
Regexes are now built rather than typed, and a test asserts no control characters
survive in any of them.
Baseline 409 -> 418. 32 tests.
The four orientation ghosts and two dead roads are baselined, not approved. They
are the first things that should be fixed, and now they cannot silently multiply.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 33 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 950176d162
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if ROAD_IN_LINE_RE.search(line) or DESCRIBES_BLOCK_RE.search(line): | ||
| continue |
There was a problem hiding this comment.
Evaluate exemptions per command candidate
This skips the entire line before extracting command candidates, so a live instruction such as “If deployment is blocked, run docker system prune -a” is ignored merely because it contains the word blocked; likewise, an unrelated make -C pmoves ... anywhere on the line suppresses every blocked command on that line. A newly documented destructive command can therefore pass the ratchet silently. Apply the exemption to the relevant candidate or require explicit anti-pattern/Known-Road framing rather than broad substring matches on the whole line.
Useful? React with 👍 / 👎.
| rel = GUARD_PATTERNS.relative_to(REPO_ROOT).as_posix() | ||
| text = GUARD_PATTERNS.read_text(encoding="utf-8", errors="replace") | ||
| findings: List[dict] = [] | ||
| for t in sorted({m.group(1) for m in GUARD_ROAD_RE.finditer(text)}): |
There was a problem hiding this comment.
Exclude YAML comments from guard-road discovery
The regex scans the complete YAML text, including comments and pattern values, although only corrective routing messages actually offer roads. For example, the current file already contains a historical comment mentioning make up-z890; if a similar comment mentions a removed make -C pmoves old-target, this workflow reports a new GHOST_ROAD and blocks the PR even though the guard never presents that target to an agent. Restrict discovery to non-comment routing fields such as reason.
Useful? React with 👍 / 👎.
…PARK (#2495) Today's ratchets made every NAMED thing in this repo checkable. They made nothing WORKED checkable. validate-command-anchors (#2488, #2494) proves a skill's targets, path citations, ssh hosts and the guard's own routing table all resolve. It cannot prove the skill does what it claims. A skill can be perfectly anchored and completely wrong. The ladder, named so the rungs stop being confused for each other: T1 anchored does everything it names exist? validate-command-anchors SHIPPED T2 exercised does it do what it claims, on a Danger Room THIS HANDOFF real node? T3 receipted can another node verify that CHIT + NATS designed, follows T2 without re-running it? T1 without T2 is a spell-checked lie. T2 without T3 is a result only the node that ran it can trust. SPARK on capability, not availability. GB10 / 128 GB unified means a skill claiming "route through TensorZero to a local model" can actually be exercised; on a CPU node that run is a mock, and a mock receipt is worse than none. Its claw scope already carries sign_trail: true + damage_control_hooks: true, so T3 needs no new trust wiring. And a sandbox resets free — skills get run against their worst fixture rather than their friendliest. The stage requested is five pieces, each grounded in something that bit this node today: 1. a pmoves-skill-check template beside templates/base, WITH SUBMODULES POPULATED — SUBMODULE_BUILD_AND_MOUNT_GAP.md applies directly here and an empty-submodule sandbox produces confident false negatives. 2. an ADDITIVE skill frontmatter contract (scope / nodes / danger_room), where scope reuses the classifier vocabulary validate_command_anchors already has rather than inventing a second one. Declared scope disagreeing with actual command scope is itself a finding. 3. fixtures that PLANT A FALSIFIABLE FAILURE, versioned beside the skill. A fixture that plants nothing verifies nothing. 4. artifacts that are deterministic, self-describing (carrying the skill CONTENT HASH, or the verdict cannot be tied to what it judged), signed-or-explicitly-labelled-unsigned, and addressable. A verdict on one node's local disk is a rumour everywhere else. 5. CHIT reflection: one skill.verified.v1 receipt per run, closed schema registered BEFORE first publish. archon.crawl.* is the cautionary tale of a subject registered against an operation nobody built. Acceptance is written to be observable from artifacts alone, so the lane does not require me present for a run. The load-bearing criterion is that a DELIBERATELY BROKEN SKILL PRODUCES A RED ARTIFACT — every gate shipped on this node today was weaker than advertised until something proved it could say no. Why it matters past hygiene: a 1B-param agent cannot judge whether a skill will work, but it can read a receipt. The heavy model pays discovery cost once in the sandbox and leaves pre-digested context; the small model selects on it instead of reasoning about it. Same principle patterns.yaml already proves — the guard hands over the correct path, valid parameters, and how to verify, so the model need not know PMOVES. Capability in the field rather than the model, extended from "what to do when blocked" to "what actually works." Not claimed by 4090 — SPARK is named owner and the row is left open for SPARK to CLAIM. Out of scope and flagged: deploying e2b-mcp-server (Agent Zero registers e2b.* against :7073, which is not a compose service, is not running, and for surf has no route in the fork at all — separate lane, must not block this one); retrofitting all 31 skills; building T3 before T2 emits something worth signing. Gitlink for PMOVES-E2B-Danger-Room is currently drifted (+) — noted in the doc, sync before building the template. Verified: make -C pmoves validate-command-anchors passes on both new files. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…reign RELEASE lines (#2498) * docs(agnote): WS2 RELEASE + lane sweep — evidence for owners, no foreign RELEASE lines Two entries. RELEASE — WS2 (z890's coordination plan) is complete inside its 72h TTL. Eight PRs merged: #2482 claim+handoff+corrections, #2483 ci-expedition skill, #2484 claude-pmoves delegation, #2485 submodule gap runbook, #2486 up-* inventory, #2488 validate-command-anchors, #2494 first-contact + guard routing table, #2495 Danger Room handoff to SPARK. pmoves/mk/infra.mk untouched throughout — z890's #2480, no collision. The entry records what the audit found BEYOND its enumerated items, because that is the reusable part: a gate can advertise coverage it does not have (three separate instances, each caught by review rather than by me); the always-loaded orientation file misdirects first contact; the guard's own routing table has two dead roads; and patterns.yaml is the pattern worth generalizing while pre-tool.sh duplicates 5 of its entries minus the affordance. It also records four corrections to my own prior work — the wrong 13-of-15 figure, the retracted hf-mcp-server entry, the dangerous first up-* retire list, and the yt-cookies pair that was never a duplicate. A closeout that only lists wins is not a closeout. NOTE — lane sweep. 115 CLAIM against 119 RELEASE. Four Mavis lanes verified shipped and missing only a RELEASE (harness v0 #2437/#2443/#2450, multi-fork follow-ups #2477, OpenRoom slice 2, creative-pipeline v0). Four older lanes still open with no PR cited and nothing found merged, now 8-10 days. Mine that are correctly still open: #2446 draft, #2468 held for review, and the SPARK handoff awaiting its CLAIM. ZERO RELEASE lines written on another agent's behalf — verified in the diff. Those lanes are Mavis's to close under Village Rule; this records evidence so they can close from it rather than from memory. Kept distinct from the KIMI-SPARK / CRUSH stale claims, which need a release OR re-claim — different category, and conflating them would make the ping inaccurate. Verified: make -C pmoves validate-command-anchors passes. Note for follow-up: dogfooding this entry surfaced a real false-positive generator in MAKE_CITE_RE — `-C \S+` swallows a closing backtick, so prose that backticks "make -C pmoves" alone captures the following word as a target. Fixed separately, not folded in here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(agnote): state the counting method, and stop treating merge as acceptance Three review findings, all correct, all mine. 1. THE TOTALS WERE NOT REPRODUCIBLE. I published "115 CLAIM against 119 RELEASE" with no method attached. Recounting three ways on the same file: anchored bullet rows 115 / 119 <- what I published token anywhere in text 284 / 221 (prose mentions) timestamped, no anchor 116 / 119 and the 2026-08-07 sweep reported 121 / 115 on a fourth. None of these is wrong; a bare number with no pattern is. The entry now states the exact regex, lists what the other methods give, and says to treat the ratio as a rough signal rather than a metric — the per-lane table is the checkable part. 2. MERGE IS NOT RUNTIME ACCEPTANCE. I listed OpenRoom slice 2 under "verified shipped, missing only a RELEASE" on the strength of #2437 merging. That lane was claimed against six handoff priorities with room-level acceptance, and #2437 is scaffold plus iframe wiring. Merging it does not demonstrate the rooms render. 3. Same for line 1723: it covers three deliverables including fork-side consumers, and #2477 merging in PMOVES.AI says nothing about whether the fork consumers landed. Both are now "merged, acceptance unverified" rather than ready-to-release, with the reasoning stated so the owner closes from runtime evidence instead of from my table. This is precisely the error the register exists to prevent, made by the sweep that exists to catch it — which is worth leaving visible rather than quietly correcting. Every gate I shipped this week was weaker than advertised until something proved it could say no; this one was a bookkeeping claim that had not been asked to reproduce itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
What
The anchor ratchet checked docs, skills and runbooks. It did not check the two surfaces that decide whether an agent entering this repo succeeds or breaks something.
1. First contact
.claude/CLAUDE.md,BOOTSTRAP.md,PATTERNS.md,CATALOG.md, rootCLAUDE.mdandAGENTS.mdare always loaded. An agent reads them before anything else — so a dead reference there is the most expensive kind there is. The agent is misdirected at step one, improvises, breaks something, and the model gets blamed for the field's error.Four real ghosts, immediately:
.claude/CLAUDE.mdworktree-sitrep-strict— described in-file as "Authoritative worktree sitrep. Prefer this to per-worktree spot checks.".claude/PATTERNS.mdworktree-sitrep,worktree-sitrep-strict.claude/BOOTSTRAP.mdhealth-quickAlso now scanned:
.claude/context,.claude/commands,.claude/agents..claude/learnings/is excluded alongsidearchive/— a learnings file records what was true in a past session. It's a log, not a promise.2. The guard's own routing table
patterns.yamlis the best thing in this repo. 121 entries, each carrying why this is wrong, the correct path, the valid parameter set, and how to verify afterwards — withask: truerather than a hard no. It routes a blocked agent to safety instead of just refusing it.Nothing checked where it routes to. New
GHOST_ROADclass points the sameGHOST_TARGETquestion one layer inward — at the thing that answers that question for everyone else. Two of its 13 offered targets don't exist:A well-behaved agent that follows the offered road lands in a wall at the moment it is least able to recover. That's the failure being described when models "get dropped in and break things" — it was never the model.
Two discriminators, so the gate doesn't punish the docs doing it right
.claude/PATTERNS.md:38andAGENTS.md:28both carry a| Raw command (blocked) | Known Road |table mappingdocker volume rm→make -C pmoves volume-reset SERVICE=.... They name the dangerous operation in order to route away from it. Flagging those would punish the cure.A line is treated as documentation rather than instruction when it either names its Known Road on the same line, or frames the command as blocked / dangerous / anti-pattern. Semantic, deliberately not a file whitelist — a whitelist would also hide a real instruction hiding in the same file.
Two bugs fixed along the way: the first cut tested the inline span rather than the line it sits on, so a road sitting next to the command in a table row was invisible and both correct docs got flagged. And
up-<service>placeholders render asup-after extraction — the trailing hyphen is the tell.One bug worth recording
Writing a literal
\binto a discriminator through a heredoc escape-interprets it to 0x08, silently replacing the word boundary with a backspace:The regex then matched nothing, so the exemption looked broken rather than absent. Same class as the corruption that turned
feat/intoeat/in the claim register (#2476) — second time today. Regexes are now built rather than typed, and a test asserts no control characters survive in any of them.Verification
Baseline 409 → 418.
Not approved, just baselined
The four orientation ghosts and two dead roads are recorded, not accepted. They're the first things that should be fixed — and now they can't silently multiply.
Follow-ups from the same thread, in leverage order:
.claude/hooks/pre-tool.shcarries a flat 11-entry list.make -C pmovesappears nowhere in it; "Known Road" appears nowhere in it. 5 of its 11 entries are already inpatterns.yaml— duplicated, minus the affordance. Same "layer re-types instead of naming" defect as the four cleanup copies. Its flatrm -rf /also matchesrm -rf /var/lib/apt/lists/*, a standard Dockerfile line.docker volume rm pmoves_neo4jdatawhenvolume-reset SERVICE=neo4jexists.🤖 Generated with Claude Code