fix(agents): the guard's scope was answering a question nobody re-asked - #847
Merged
Conversation
The tool contract shipped in #843 read one source of agent-facing text — the cycles reflection trailer. That scope was deliberate and stated: #818/#842 were open on the inline mention cues, and a guard straddling an open PR is a merge conflict rather than a safeguard. #842 has landed, so this is the widening it named. What the narrow scope also did was fix the answer to "how many tools is the fleet told to call that its runtime does not have?" at one. That number reached CLAUDE.md, the PR body and the audit doc, and nobody re-derived it once the check was green. Re-running the widened contract against 00821479 — the pin that was live until today's deploy: required: log_cycle attach_file read_attachment post_message get_messages open_dm MISSING: log_cycle read_attachment open_dm Three, not one. commonly_open_dm was named to openclaw seats by the consultation cue on EVERY mention while the pinned extension did not declare it — the same defect as log_cycle on a surface ~100x wider, uncounted for the same 88 days. Its absence was separately known; nothing connected it to the cue demanding it. The naive widening is wrong, which is why this took care. The cues name commonly_read_file and commonly_dm_agent (MCP) beside commonly_read_attachment and commonly_open_dm (openclaw) because they ship to every seat unconditionally — that pairing IS the fix from #842 and audit entry #13. Requiring an MCP name of an openclaw pin would red the build over a deliberately correct line. So a source declares namedForOtherDrivers, and the exemption is self-checking: a name exempted but no longer present in the cue is a hard error, because an exemption outliving its justification is a hole that would excuse the next real violation. Failure directions verified by mutation against the live files, not asserted: unclassified new name in a cue exit 1 (FAIL, names the cue) exemption no longer in the cue exit 2 (CANNOT VERIFY) a cue defined but not registered exit 2 (CANNOT VERIFY) The third is the coverage-gap guard: a new cue would otherwise ship tool names nobody checks while the guard still printed OK, reproducing this commit's own defect inside the fix for it. Throws from the source layer now exit 2 rather than crashing out as 1 — could-not-read is not contract-violated. The PASS line now names the sources it read and says what it did not cover, for the same reason: a green check is read as "nothing is wrong", and the narrower the scope the more confidently that is over-read. Also drops "live since 11878b43c" from the consultation cue's comment. That commit is on the lineage .gitmodules DECLARED, not the one the gitlink tracked, so the sentence was false for 88 days and is true now only because the pin moved. No ref replaces it: this check reads that cue on every CI run, so the claim has a reader instead of a citation. Guard: 35 tests, 0.7s. agentMentionService: 87 pass with the suite above. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Widens
scripts/verify-moltbot-tool-contract.jsfrom one source of agent-facing text to two — the trigger #843 named, now that #842 has landed on the lines in question.The finding this produced
The guard shipped reading only the cycles reflection trailer. That scope was deliberate and documented. What it also did was fix the answer to "how many tools is the fleet told to call that its runtime doesn't have?" at one — a number that reached
CLAUDE.md, three PR bodies and the audit doc, and that nobody re-derived once the check went green.Re-running the widened contract against
00821479, the pin that was live until today's deploy:Three, not one.
commonly_open_dmwas named to openclaw seats by the consultation cue — on every mention, to every agent — while the pinned extension did not declare it. Same defect ascommonly_log_cycle, on a surface roughly 100× wider than the heartbeat, uncounted for the same 88 days. Its absence was independently known (CLAUDE.mdrecords it); nothing connected it to the cue demanding it.Why the naive widening would be wrong
The cues name
commonly_read_fileandcommonly_dm_agent(MCP) besidecommonly_read_attachmentandcommonly_open_dm(openclaw), because they ship to every seat unconditionally. That pairing is the fix from #842 and audit entry #13. Feeding the cue through the contract unchanged would demand an MCP tool of an openclaw pin and red the build over a line that is deliberately correct — a guard that reds on the fix is worse than no guard.So a source declares
namedForOtherDrivers, and the exemption is itself checked: a name listed there that no longer appears in the cue is a hard error. An exemption that outlives its justification is a hole that would go on excusing the next real violation.Failure directions, verified by mutation against the live files
Not asserted in a test — mutated, run, restored, restore confirmed:
The third is the coverage-gap guard. Without it a new cue ships tool names nobody checks while the guard still prints OK — this PR's own defect, reproduced inside the fix for it. Throws from the source layer now exit 2 instead of crashing out as 1: could-not-read is not contract-violated.
Also
live since 11878b43cfrom the consultation cue's comment. That commit is on the lineage.gitmodulesdeclared, not the one the gitlink tracked — the sentence was false for 88 days and is true today only because the pin moved. No ref replaces it: this check reads that cue on every CI run, so the claim has a reader instead of a citation.Test
Not verified: that no other agent-facing surface names an undeclared tool. Two sources are covered; registry presets beyond the trailer, MCP tool descriptions, and the agent-runtime
contextpayload are not — and nothing yet reports that they aren't.🤖 Generated with Claude Code