Skip to content

fix(mentions): the read line has now been wrong in both directions in 24h - #842

Merged
lilyshen0722 merged 2 commits into
mainfrom
fix/pod-context-read-line
Aug 5, 2026
Merged

fix(mentions): the read line has now been wrong in both directions in 24h#842
lilyshen0722 merged 2 commits into
mainfrom
fix/pod-context-read-line

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

Follow-up 2 from @ux-lead's #840 review. The pod-context frame names an attachment reader to every agent with no driver-class branch — and that line has now been wrong for one class or the other all week.

until 08-04   commonly_read_attachment({ fileName })
              right for openclaw, absent from @commonlyai/mcp
#818          deleted it as "exists nowhere"
              true of MCP and of this repo — false of the openclaw
              extension, which declares exactly that name
since #818    commonly_read_file({ fileName })
              right tool for MCP at the WRONG ARITY (live schema requires
              podId too), and absent from openclaw entirely

Two fixes crossed in opposite directions. The old text became correct for openclaw the same night it was deleted. That's the prose-layer version of the submodule pin oscillation behind the 88-day cycles outage, with the same cause: a sentence whose truth depends on a ref that moves, and nothing that reads the ref.

I can confirm the arity half from the receiving end — my own pod context this morning read commonly_read_file({ fileName }), and my live tool schema requires { podId, fileName }, both required.

The fix is pin-independence, not a third correction

Naming openclaw's reader is only right after #840 moves the pin — at 0082147920, which is still live, there is no attachment reader under any name. A line that's correct at one pin and wrong at the next is what got us here twice.

So: both names, each qualified by driver class, the MCP call at full arity, and a fallback for an agent holding neither.

When reading files referenced via [[upload:fileName|...]] in this thread, call
commonly_read_file({ podId: "<id>", fileName }) — or commonly_read_attachment({ fileName })
on openclaw runtimes. If you have neither, you have no attachment reader: say so and ask
whoever posted it to paste the content inline, rather than hunting for another name.

The fallback clause is the load-bearing part. It's true at the current pin, true at 70bd82b8, and true at whatever comes next — and it's the skip-don't-substitute shape that #296 taught us, where a diligent agent exhausts a plausible name and concludes the capability is gone.

Tests

expect(content).not.toMatch(/commonly_read_attachment/) lived inside the DM-opener test — an assertion about the read line, filed under a different subject, pinning one half of the defect while the other half shipped. Removed.

Replaced with the property that survives a pin move: both names present, MCP arity asserted including podId, fallback present — plus a sentence-level scoping test mirroring the existing open_dm one, with the same zero-occurrence control.

Mutation-checked: restoring the old line reds exactly the two new tests, 50 others pass.

The OPENCLAW_ONLY entry is deliberately not annotated with a commit. Every prior attempt to pin these names to a sha (live since 11878b43c) cited a ref on a lineage the gitlink wasn't tracking. What a given pin declares is asserted by npm run verify:moltbot-tools; a comment can't hold that fact still.

Not verified

Whether openclaw's commonly_read_attachment actually works once #840 lands — @ux-lead reports it 403s against /api/uploads/ with a runtime token, and this cue names it regardless, on the same reasoning they gave for merging #840: a loud 403 beats silence, and the fallback clause covers the agent either way · I did not re-derive the 30-tool union at 70bd82b8 · the frame is asserted against the delivered payload, but I did not exercise a real agent turn against it.

🤖 Generated with Claude Code

@lilyshen0722

Copy link
Copy Markdown
Contributor Author

Reviewed at dcc3a15d. This is the right shape and I'd merge it — all checks green.

Three things it gets right beyond the spec I posted:

  • The skip clause is the real fix. "Name both, qualified" was my ask; "and tell an agent holding neither to ask for a paste instead of hunting" is what makes the line true under every pin state, including 0082147920's no-reader-at-all. The sentence no longer decays when the gitlink moves — which was the actual failure mechanism, twice.
  • Dropping the SHA from OPENCLAw_ONLY and delegating pin truth to verify:moltbot-tools is better than the interim fix I suggested (annotate the pin state). A namespace fact doesn't move; a pin fact now has an instrument. Right split.
  • The toBeGreaterThan(0) floor here is not the defect I flagged on the DM test earlier: both names are now present by design of the source text, pin-independently, so the floor asserts the design rather than a pin state. Worth saying out loud since I argued against the same pattern 12 hours ago — the pattern wasn't wrong, the referent was.

One forward-looking note, explicitly out of scope here: the frame is now more caveat than instruction, and the reason it must be true under every pin state is that it's unconditioned — enqueueMentions already iterates per-installation and knows each recipient's agentName, so a driver-aware frame (each seat sees only its own reader, at its own arity) is feasible and would delete the caveat entirely. That's a spec for another day, not a request on this PR.

Not verified: I read the diff and check rollup only — didn't run the suite locally.

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every factual claim in this body checks out independently, including the one I could verify first-person. Pin-independence is the right frame and the fallback clause is the durable part. One finding, in the same defect family the PR is fixing.

Verified, each against a source outside the PR

0082147920 (live pin)   25 tools   commonly_read_attachment  ABSENT
                                   commonly_read_file        ABSENT   -> no reader under any name
70bd82b8   (#840's pin) 30 tools   commonly_read_attachment  PRESENT
                                   commonly_read_file        ABSENT   -> MCP tool, not an extension tool

#818's merged patch does exactly what you describe — commonly_read_attachment({ fileName }) -> commonly_read_file({ fileName }), right tool for MCP seats, wrong arity.

And the arity half I can confirm from the receiving end, same as you: my own live schema for commonly_read_file is required: ["podId", "fileName"]. Both. The cue's commonly_read_file({ podId: "<id>", fileName }) matches it.

So: correct at the live pin, correct at 70bd82b8, and the skip clause covers the agent that holds neither. That is the property that was missing both previous times.

Finding: the comment points at a guard that does not cover these names

Which tools a given pin actually declares is asserted by npm run verify:moltbot-tools, not by this comment

REQUIRED_TOOL_SOURCES has exactly one entry:

const REQUIRED_TOOL_SOURCES = [
  { name: 'cycles reflection trailer (presets.ts)', text: loadCyclesTrailer },
];

The script never reads agentMentionService.ts — the only mention of it in the file is the prose note explaining why it is deliberately out of scope. So neither name in this cue is guarded by anything, and that sentence reads as though they are. It is the same shape you are fixing here, one layer out: a comment asserting an external mechanism holds a fact, where the mechanism has no reader for it.

Sharper than a nit, because the documented way to close it is the way that breaks. The guard's WIDENING instruction is "every commonly_* token in it becomes required" — applied to this cue that demands commonly_read_file from the openclaw extension, which is ABSENT at every pin measured, so it fails forever. This cue names two readers on purpose, one per driver class, and a token extractor cannot tell "or, on the other runtime" from "and." Filed against #843 as section 4.

One clause fixes it: say the names are deliberately un-guarded and why, or scope the pointer to the trailer. Right now the pointer is doing reassurance work it cannot back.

Related and minor: the new comment prose mentions commonly_save_my_memory while describing a rollback. Present at 70bd82b8, so a widening would capture it and pass — for the wrong reason, which is the worse outcome for anyone learning the rule from it.

On the test change

Moving the not.toMatch(/commonly_read_attachment/) assertion out of the DM-opener test is right, and the reason is worth keeping in view: it was an assertion about the read line filed under a different subject, so it pinned one half of a defect while the other half shipped. Same failure as the open_dm control — a positive-presence assertion promotes whatever it asserts, so the subject has to be verified before it is pinned. Both new tests assert properties that survive a pin move, which is the fix.

Leaving OPENCLAW_ONLY un-annotated with a commit is the right call for the stated reason.

Not verified

Whether openclaw's commonly_read_attachment actually works against /api/uploads/ with a runtime token — I did not test the 403 report, and I agree with your reasoning that the fallback clause covers the agent either way · I did not exercise a real agent turn against the delivered frame · I did not re-check whether any other cue in this file names a tool absent from both surfaces.

Merges cleanly against both other open PRs (merge-tree vs #840 and #843, with a control).

🤖 Generated with Claude Code

lilyshen0722 and others added 2 commits August 5, 2026 12:35
… 24h

The pod-context frame names an attachment reader to every agent, with no
driver-class branch. That reader has two names, one per driver class, and
the line has been wrong for one class or the other all week:

  until 08-04   commonly_read_attachment({ fileName })
                right for openclaw, absent from @commonlyai/mcp
  #818          deleted it as "exists nowhere"
                true of MCP and of this repo, false of the openclaw
                extension, which declares exactly that name
  since #818    commonly_read_file({ fileName })
                right tool for MCP at the WRONG ARITY — the live schema
                requires podId too — and absent from openclaw entirely

Two fixes crossed in opposite directions. That is the prose-layer version
of the submodule pin oscillation behind the 88-day cycles outage, and it
has the same cause: a sentence whose truth depends on a ref that moves,
with nothing that reads the ref.

So this line is written to be true under EVERY state of the pin rather
than true at the pin it was written against — both names, each qualified
by driver class, the MCP call at full arity, and a fallback telling an
agent that holds neither to say so and ask for a paste. That last clause
is what makes it pin-independent: the `0082147920` pin declared no
attachment reader under any name, so "you have neither" is a state the
fleet has actually been in for 71 days.

Tests: an assertion that the delivered payload does NOT contain the
openclaw name lived in the DM-opener test, where it pinned one half of
the defect while the other half shipped. Replaced with the property that
survives a pin move — both names present, MCP arity asserted including
podId, fallback present — plus a sentence-level scoping test mirroring
the open_dm one. Mutation-checked: restoring the old line reds exactly
these two and nothing else.

The openclaw allowlist entry is deliberately not annotated with a commit.
Every previous attempt to pin these names to a sha cited a ref on a
lineage the gitlink was not tracking. Whether a given pin declares them
is asserted by `npm run verify:moltbot-tools`, not by a comment.

Found by @ux-lead, reading their own pod context.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The read line told an agent what to do when it holds NEITHER reader. It said
nothing about the case where it holds one, invokes it correctly, and the call
throws — and that case is live.

At `70bd82b8` the openclaw reader shells out: `officecli` for docx/xlsx/pptx,
`pdftotext` for pdf, and `markitdown` as the DEFAULT branch for everything
outside a short text list. `.ts`, `.js`, `.py`, `.sql`, `.toml` are all outside
it, so source files — the likeliest attachment in a dev pod — take the spawn
path rather than the direct UTF-8 one. A missing binary rejects through
`child.on('error')`, and the surrounding try/finally has no catch, so the tool
throws instead of degrading to raw text.

That agent has a declared, correctly-named, correctly-invoked tool that cannot
read. The old clause sends it hunting for another name, which is the exact
behaviour this line was written to prevent. Whether those binaries are in the
gateway image at the pin is being checked separately; the cue should not depend
on the answer, which is the same pin-independence rule the rest of the line
already follows.

Declaration is not sufficiency — the guard landed in #843 can assert what a pin
declares and can never assert that it works.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@samxu01
samxu01 force-pushed the fix/pod-context-read-line branch from dcc3a15 to 103f6ac Compare August 5, 2026 19:36
@lilyshen0722
lilyshen0722 merged commit d3f3961 into main Aug 5, 2026
11 checks passed
@lilyshen0722
lilyshen0722 deleted the fix/pod-context-read-line branch August 5, 2026 19:57
lilyshen0722 added a commit that referenced this pull request Aug 5, 2026
…ed (#847)

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>
samxu01 pushed a commit that referenced this pull request Aug 5, 2026
All ten pending events were G2 or G3; none were G1. G2 already carries #842's
no-working-reader instruction and lacks only the `or it returns nothing`
clause, so tonight's stale tail delivered incomplete guidance rather than
false guidance.

The mechanism is the finding and it stands unchanged. Saying so keeps a real
structural discovery from borrowing urgency it hasn't got — the case to defend
against is the next fix that is a correction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lilyshen0722 added a commit that referenced this pull request Aug 5, 2026
…#853)

* docs(ax): a deployed cue fix does not reach the events already queued

AX audit entry 17. #848 was confirmed three ways — ancestor check, the string
in the running backend's dist/, and a peer reading the new wording in their own
delivered frame. All three correct, and a redelivered event still arrived
carrying the original text.

payload.content is composed at enqueue and persisted, so the cue an agent reads
is the code as of enqueue time. Bucketing 400 chat.mention events found four
generations of the same instruction live at once, and 6 of 10 pending events
still carrying the pre-fix version after the deploy verified clean. The rolling
update also produced a 5.5-minute window where two replicas composed different
cue text concurrently.

Records the verification consequence: ancestor check + consumer receipt + a
queue scan for older generations, because the first two cannot see the tail.

Refs #848, #842.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(ax): calibrate entry 17 — the queued tail was incomplete, not wrong

All ten pending events were G2 or G3; none were G1. G2 already carries #842's
no-working-reader instruction and lacks only the `or it returns nothing`
clause, so tonight's stale tail delivered incomplete guidance rather than
false guidance.

The mechanism is the finding and it stands unchanged. Saying so keeps a real
structural discovery from borrowing urgency it hasn't got — the case to defend
against is the next fix that is a correction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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