Skip to content

fix(ax): the tool-contract guard skipped the strongest cue surface - #856

Merged
lilyshen0722 merged 2 commits into
mainfrom
fix/tool-contract-covers-heartbeat-cue
Aug 5, 2026
Merged

fix(ax): the tool-contract guard skipped the strongest cue surface#856
lilyshen0722 merged 2 commits into
mainfrom
fix/tool-contract-covers-heartbeat-cue

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

What

scripts/verify-moltbot-tool-contract.js read the presets.ts cycles trailer and the inline mention cues. It did not read backend/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_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.

@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:

heartbeat events sampled: 400 of 18,888   (positive control ran)
  cue names commonly_log_cycle : 391
  cue names save_my_memory     :   9   ← all acked, all 08-04T20:21–20:31
PENDING heartbeats carrying the dead envelope: 0
log_cycle band: 2026-08-04T20:36:01Z → 2026-08-05T21:18:01Z

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 namedForOtherDrivers exemption: #840 reconciled the lineages and the pin declares commonly_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_memory is declared at :526, directly above a description reading "cycles is intentionally unavailable here":

  1. Single writer. cycles has 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 "mention save_my_memory too, in case" edit reproduces.
  2. The escape hatch 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 fix(memory): rollback cycle-reflection trailer — DM regression #296 rollback the next time a bump drops the tool — three unnoticed drops are on the record.

Mutation-verified, measured anchors and byte-identical restores:

mutation result
writer → a tool the pin doesn't declare exit 1, pin-contract failure
cue also names commonly_save_my_memory exit 2, single-writer failure
escape-hatch clause deleted exit 2, escape-hatch failure

CLAUDE.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

lilyshen0722 and others added 2 commits August 5, 2026 14:35
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>
@lilyshen0722
lilyshen0722 merged commit 0df6123 into main Aug 5, 2026
10 checks passed
@lilyshen0722
lilyshen0722 deleted the fix/tool-contract-covers-heartbeat-cue branch August 5, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant