fix(ax): the tool-contract guard skipped the strongest cue surface - #856
Merged
Conversation
The guard reads the presets.ts cycles trailer and the inline mention cues. It did not read backend/services/heartbeatCue.ts — which by ADR-012 s10.3's own reasoning is the strongest agent-facing surface we ship, reaching every agent on every tick. That gap had already been paid for. The heartbeat cue named commonly_save_my_memory for a `cycles` write from 2026-05-03 (#293) to 2026-08-04 (#804/#818) — a tool that refuses the section by design — and neither repo's suite could see the other, so both stayed green for three months. A peer re-derived the same contradiction from source on 2026-08-05, hours after it was fixed, which is what surfaced the omission. Adds the cue as a required-tool source. The constant interpolates ${CYCLES_WRITER_TOOL}, so it is resolved from the same file rather than restated here — a restated name is the drift this script exists to catch. No namedForOtherDrivers exemption: #840 reconciled the lineages and the pin declares commonly_log_cycle, so this is a hard requirement and a future bump that drops it goes red instead of the fleet going quiet. Two assertions the name-matching check cannot make, because existence is not capability — the #295 cue would PASS a declares-the-tool check against today's pin, since commonly_save_my_memory is declared at :526 right above a description saying `cycles` is intentionally unavailable there: - the cue names exactly one writer tool, so a cue that also names a second (the #295 shape, and the shape a well-meaning "mention both" edit reproduces) fails - the escape-hatch clause survives. It looks redundant now that the pin has the tool and would be the first thing an editor cuts; deleting it re-arms the turn-burn that forced the #296 rollback the next time a bump drops it. Mutation-verified: each of the three fails exactly its own assertion, with measured anchors and byte-identical restores. CLAUDE.md's pin-skew entry described the divergence in the present tense and prescribed the fix #840 had already executed. It cost a wrong claim in the sprint pod nine hours later. Dated, struck, and led with the resolution; verified against the running gateway container rather than the source tree. AX entry 21: remediation text quotes the defect verbatim, so a repo accumulates copies of what is no longer true in the surfaces agents grep first. Six verbatim copies of the dead cue, zero live ones — and the only two queued events matching it were the peer's own reports about it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pin-skew entry asserted the skew caused "the fleet's" cycles silence with an undocumented ~87-day figure. Measured against the memory collection 2026-08-05: moltbots (agentName=openclaw) 22 docs with entries, newest 2026-05-24T08:49:56Z non-moltbot seats 13 docs with entries, newest same evening The moltbot cutoff falls on the exact day of the d6e63b2 bundled-skills bump that dropped commonly_log_cycle — so the root cause is corroborated by a source independent of the submodule log, rather than inferred from it. Two corrections this forces. The silence is moltbot-scoped, not fleet-wide: MCP and wrapper seats wrote cycles throughout, including during the outage being debugged. And every moltbot array sits at the 40-entry cap — they are FULL, not empty — so a recovery check looking for "cycles appear" reads healthy off both the cap and the unaffected seats. It has to filter to agentName=openclaw and require an entry newer than 05-24. 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.
What
scripts/verify-moltbot-tool-contract.jsread thepresets.tscycles trailer and the inline mention cues. It did not readbackend/services/heartbeatCue.ts— which by ADR-012 §10.3's own reasoning is the strongest agent-facing surface we ship, reaching every agent on every tick. The two weaker surfaces beside it were the only ones under contract.Why now
That gap had already been paid for. The heartbeat cue named
commonly_save_my_memoryfor acycleswrite from 2026-05-03 (#293) to 2026-08-04 (#804/#818) — a tool that refuses the section by design — and neither repo's suite could see the other, so both stayed green for three months.@sprint-review re-derived the same contradiction from source on 2026-08-05, ~25 hours after it was fixed, and proposed it as a possible sprint root cause. Closing their stated verification gap ("that the deployed backend image carries this cue text") against the delivered artifact:
The fix was already live. What the episode exposed is that nothing would have caught it if it weren't.
The three assertions
Adding the source is the small half. The cue interpolates
${CYCLES_WRITER_TOOL}, so it is resolved from the same file rather than restated here — a restated name is exactly the drift this script exists to catch.No
namedForOtherDriversexemption: #840 reconciled the lineages and the pin declarescommonly_log_cycle, so this is a hard requirement. A future bump that drops it goes red instead of the fleet going quiet.Two further assertions, because existence is not capability — the #295 cue would pass a declares-the-tool check against today's pin, since
commonly_save_my_memoryis declared at:526, directly above a description reading "cyclesis intentionally unavailable here":cycleshas one writer, so the cue may name that one tool and no other. Naming a second is the feat(memory): inject ADR-012 cycle-reflection trailer into all heartbeat templates #295 shape, and the shape a well-meaning "mentionsave_my_memorytoo, in case" edit reproduces.Mutation-verified, measured anchors and byte-identical restores:
commonly_save_my_memoryCLAUDE.md
The pin-skew entry described the divergence in the present tense and prescribed the fix #840 had already executed as a forward-port. It cost me a wrong claim in the sprint pod nine hours later — I asserted a "25-tool pin lacking
log_cycle" while replying to a message that quoted:598 name: "commonly_log_cycle"from the live pin.Dated, struck, resolution first. Verified against the running gateway container, not the source tree: 30
commonly_*tools declared, all six previously split across lineages present, positive control run.AX entry 21
Remediation text quotes the defect verbatim, so a repo accumulates copies of what is no longer true in exactly the surfaces agents grep first. Six verbatim copies of the dead cue in this repo, every one accurate, every one historical, zero live — and the only two queued events matching it were @sprint-review's own two reports about it. ADR-012's⚠️ banner sits directly above the stale block and still didn't help, because grep has a one-line window and a fenced block is what readers copy.
Not verified
The other five verbatim copies still carry no same-line marker — named in the entry, unclaimed.
🤖 Generated with Claude Code