chore(submodule): bump _external/clawdbot fc6a22319 → a67f0df63 (commonly_attach_file + 24 more) - #418
Closed
samxu01 wants to merge 1 commit into
Closed
chore(submodule): bump _external/clawdbot fc6a22319 → a67f0df63 (commonly_attach_file + 24 more)#418samxu01 wants to merge 1 commit into
samxu01 wants to merge 1 commit into
Conversation
Brings in 25+ commits on rebase-2026.3.29 including the tools the backend's pod-context cue + ADR-013 Phase 0b assume are available: - 8b50281b8 feat(commonly): commonly_attach_file extension tool - 18c2967c7 fix(commonly): commonly_attach_file workspace resolution (#6) - ae3235c12 feat(commonly): commonly_read_attachment tool (#5) - a67f0df63 feat(commonly): commonly_log_cycle — ADR-012 Phase 2 (#7) - 11878b43c feat(commonly): commonly_open_dm — autonomous a2a DM (#1) - 826d46475 feat(commonly): commonly_read_my_memory + commonly_save_my_memory - 6c99dc312 fix(commonly): route acpx_run through LiteLLM via opencode - 480298e30 fix(commonly): strip [[reply_to]] tags in commonly_post_message - 518415ab1 fix(commonly): wire replyToId through message pipeline - 519cdb664 feat(commonly): TOOL_ROUTING_HINT, SELF_IDENTITY_NOTE, gh CLI - 5a05c963d feat(commonly): pod-scoped sessions, websocket, client refactor - 16717d8c8 fix(commonly): acpx_run — catch rate-limit in resolved stdout - (more — see git log fc6a223..a67f0df63 -- extensions/commonly/) Root-cause smoke 2026-05-20 cycle 6: Aria reported 'Validation failed for tool commonly_post_message: content required'. That error came from her runtime falling back to commonly_post_message because commonly_attach_file DID NOT EXIST in the deployed image at fc6a22319. The pod-context cue in agentMentionService.formatPodContextFrame tells agents to call commonly_attach_file({ podId, filePath, message }), which AT fc6a22319 had no matching tool. CLAUDE.md memory and ADR-013 Phase 0b document this tool as shipped (Nova nailed e2e attach 2026-05-08 deploy d7a6892) — the submodule SHA appears to have regressed since then. Same submodule bump also unblocks the 5 missing tools cloud-codex / MCP runtimes already advertise. Should be deployable as a Deploy Dev workflow run after merge.
samxu01
pushed a commit
that referenced
this pull request
Aug 4, 2026
Fourth rewrite of this comment in one afternoon, and the reason is the finding: the pin is not a stale pointer nobody moved. It has moved 15+ times and has crossed lineages repeatedly. 2026-05-09 f4b7a48 a67f0df6 BRANCH log_cycle ARRIVES 2026-05-17 b6a811b fc6a2231 main LOST (bump was for react_to_message) 2026-05-21 0168f01 a67f0df6 BRANCH RESTORED (#418, explicitly) 2026-05-24 d6e63b2 84549161 main LOST (bump was for bundled-skills) 2026-06-26 a3de6d0 00821479 main current Three corrections to what this file said an hour ago: - not "never pinned" -- pinned twice, and it worked twice - not "nobody looked at the gitlink" -- #418's subject is literally `bump _external/clawdbot fc6a22319 -> a67f0df63`. Somebody caught this exact regression on 05-21. A bundled-skills bump undid it three days later. - not "a stale fork to avoid" -- the branch was a deliberate target twice Nobody was negligent. A submodule bump surfaces the tool it was made for and says nothing about the five it trades away; the diff is one line of hex. Cross-validated by @ux-lead against per-agent last-cycles-append timestamps: writes cluster at 05-09..05-13 and 05-21..05-23, both strictly inside a branch-pinned window, nothing outside them. Mongo and the submodule log agree to the day. Remedy restated: not a bump in either direction, but ending the divergence. Anything less leaves the next unrelated bump free to swap the set back -- which is what happened twice after #418 had already fixed it. Gitlink history read here from `git ls-tree` at each commit that touched _external/clawdbot; windows and cycles correlation from @ux-lead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
samxu01
pushed a commit
that referenced
this pull request
Aug 4, 2026
… paragraph The comment in presets.ts describing the openclaw lineage skew was rewritten FOUR times in one afternoon, each version confidently wrong in a different direction. Not carelessness at any one step -- every version was verified before it shipped. A claim about another repo's state does not get fixed; it decays, and prose has no mechanism to notice. scripts/verify-moltbot-tool-contract.js resolves the pinned submodule tree, parses the tool DECLARATIONS out of extensions/commonly/src/tools.ts, and fails when a tool the cycles trailer instructs moltbots to call is not among them. Required tools are derived from CYCLES_REFLECTION_TRAILER itself rather than restated, so editing the trailer to name a different tool is covered without touching the script. Would have fired on 2026-05-17 and again on 2026-05-24 -- the two unrelated bumps that swapped the lineage back after #418 had fixed it by hand. Design notes that are load-bearing: - Declarations, not mentions. `name: "commonly_x"` counts; the string appearing in a description does not. The original defect was a name in prose asserting a capability, so a parser that accepts prose reproduces that defect inside the guard against it. Mutation-checked: loosening the regex reds exactly the prose test. - Exit 2 for "cannot verify" (submodule absent, or zero declarations parsed from a non-empty file), never 0. Four instruments returned clean zeros today whose controls also returned zero; an unrun check must not look like a passing one. - Scoped to the trailer, not the agentMentionService cues, because #818 is changing those and a guard straddling an open PR is a merge conflict rather than a safeguard. Widening point documented in the header. NOT WIRED TO A WORKFLOW YET, deliberately. Only deploy-dev.yml checks out submodules, and the check fails there today because the regression is live -- so wiring it now means either a blocked deploy or a non-blocking check, and a check that cannot fail is exactly the decorative-config defect this whole investigation is about, one layer up. It belongs in the reconciliation PR, where it goes green the moment it goes live. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 4, 2026
lilyshen0722
added a commit
that referenced
this pull request
Aug 4, 2026
… bump (#831) * fix(agents): deliver the cycle directive on the default heartbeat path 10 of 27 deployed moltbots had no cycle-reflection directive in their HEARTBEAT.md, and one (theo) carried a fossil that named `commonly_write_agent_memory` as the cycles writer — a tool that writes the whole memory envelope and cannot append to `cycles` at all. The trailer itself was never wrong. `withCyclesDirective` was applied at exactly two sites, `provision.ts:294` and `reprovision.ts:137`, both inside `matchedPreset?.heartbeatTemplate ? {...}`. Preset ids are role names (`backend-engineer`, `dev-pm`, …) and the fallback match is `p.id === normalizedInstanceId`, so for any agent installed without an explicit `presetId` the match failed, `ensureHeartbeatTemplate` fell through to the raw default, and the delivered file structurally could not carry the directive. The 17/10 split is exactly presetId-set vs presetId-unset. Two fixes, because either alone leaves the fleet broken: 1. Apply the trailer to the default branch in `ensureHeartbeatTemplate`, so a preset match failure can no longer silence the directive. 2. Count "missing the directive" as stale. Previously the only rewrite triggers for a non-forceOverwrite reprovision were two 2026-era marker strings, so a pre-trailer HEARTBEAT.md survived every reprovision indefinitely — fix 1 would otherwise only help freshly provisioned agents. This is staleness, not customization: operator-edited files are already short-circuited upstream by `customizations.heartbeat === true`. The grep marker is exported from the trailer's own module rather than re-typed at the grep site. A second literal would drift silently, and the drift would present as "nothing to rewrite" rather than as a failure. Tests assert on the base64 payload actually written into the gateway pod, not on the source that composes it — the defect was invisible at every layer above the delivered file. Both fixes mutation-checked: reverting each one fails its own test and nothing else. Not fixed here: `agentProvisionerService.ts` (non-k8s path) has the same missing import; the scheduler's inline cue is #818's surface. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(agents): record hand-authored HEARTBEAT.md as a customization The staleness clause in the previous commit claimed operator edits were "already short-circuited upstream by customizations.heartbeat === true". That guard was never set by the endpoint that performs the edits. `routes/registry/files.ts` lets any pod member or creator POST a HEARTBEAT.md; it writes the file verbatim and persists `AgentProfile.heartbeatContent`, and never touches `customizations`. The flag only ever arrived from `installationConfig.customizations` at provision time, and the frontend only reads it for a badge. So `skipHeartbeat` stayed false for exactly the files a user had hand-written, and the new "missing the directive ⇒ stale" clause would have overwritten them. Fixed at the cause rather than by narrowing the clause: the endpoint that accepts a user's file now records the file as user-owned. A `reset` clears the flag, which is the one case where the provisioner should own the file again. This also explains the 5 agents that carry the cycle trailer while matching no preset id (fakesam/liz/tarik/tom, all mtime 2026-05-24; ops 2026-07-29). They were written through this endpoint, which is why no store records a presetId for them — it never sets one. Found by @ux-lead running the control group I had left out of my own query. Mutation-checked: removing the updateOne fails both new assertions and leaves the pre-existing write test passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(security): rate-limit the HEARTBEAT.md write endpoint CodeQL flagged js/missing-rate-limiting (high) at files.ts:211 on this PR and NOT on main — the previous commit's `AgentInstallation.updateOne` added a database access to an unlimited route handler, which is what tripped it. Self-inflicted, so fixed here rather than deferred. The POST heartbeat-file handler is more expensive than the read surface the existing inspectorRateLimit guards: it execs into the gateway pod to write the PVC and writes two Mongo documents. 30/min per user rather than 120. Declared in this file on purpose — CodeQL's query only recognises the middleware when it is declared alongside the route registration, per the note already at the top of the file. Not widened: the identity-file POST has a similar shape and is not alerted; files.ts:368 carries a pre-existing alert on main. Both are out of scope for a fleet-provisioning fix and neither was introduced here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(security): correct the CodeQL same-file claim that propagated a non-fix The header comment asserted that CodeQL's js/missing-rate-limiting query "only recognises the middleware on the SAME file as the route registration." The repo's own evidence contradicts it: alert #1658 has been open against the a2a-dms route since 2026-05-11, and that route has carried inspectorRateLimit inline the entire time. I read the comment as settled, copied the pattern for the heartbeat POST, and produced alert #1720 instead of clearing anything. The comment is the surface that generated the error, so it is the thing to fix — a correction filed only in the PR thread would reach nobody writing the next route. The limiters stay. 30/min on an endpoint that execs into the gateway pod and writes two Mongo documents is correct on the merits whether or not a scanner models it. What changes is the claim about why it is there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(security): run the workspace rate limiters before auth, not after CodeQL alert #1720 stayed open after `workspaceWriteRateLimit` was applied inline, and #1658 has been open against the a2a-dms route since 2026-05-11 with `inspectorRateLimit` applied the whole time. A header comment in this file blamed same-file placement; the previous commit corrected that to "unproven". The actual mechanism is ordering. `js/missing-rate-limiting` anchors to the first middleware in the chain, and `auth` does a Mongo lookup — so a limiter placed after `auth` leaves that lookup unprotected. Cross-tabbed against main: of ~37 routes with the limiter before auth, zero are flagged; of the 9 with it after, 6 are flagged, including both routes here. Three after-auth routes escaped and I did not chase them. The scanner was reporting something true, so this is a real fix rather than a silencing. Reordering costs `req.userId`, which auth had been setting, so the key generators now hash the Authorization header — the idiom `routes/messages.ts` already uses for its pre-auth limiters, and the reason those routes are clean. Per-caller isolation is preserved; unauthenticated callers still fall back to ipKeyGenerator. Test asserts the limiter sits at index 0 on both routes. Position, not presence: "a limiter is somewhere in the chain" stays green through exactly the regression this exists to catch. Mutation-verified — reverting either route's order turns it red. Matching on path alone silently resolved to the GET route, which has no limiter, so it matches on method too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(security): correct the rate-limit comment at the source of the belief Comment-only; no behavior change in this file. routes/messages.ts credited its clean CodeQL status to inlining dualAuth in the same file the limiter is declared in. That is the wrong cause, and the belief propagated: it was copied into routes/registry/files.ts, where the limiter WAS same-file and the routes were flagged anyway — #1720, and #1658 for three months. What keeps these routes clean is order. The limiter precedes the auth middleware, so the Mongo lookup auth performs is itself covered. Cross-tab against main: ~37 routes with the limiter before auth, none flagged; 9 with it after, 6 flagged. Correcting it here rather than only in files.ts, because this is the copy the next author reads before writing the next route — fixing the diagnosis where it was diagnosed leaves the surface that generates it untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(security): raise the workspace write cap to 120, correct the last 3 copies Two changes, both from @sprint-review's review. 1. workspaceWriteRateLimit 30 -> 120 (and the 429 message, which would otherwise have stated the old cap). 30 was a guess. Measured: the UI's five call sites are all single-agent from an open dialog, so no human reaches 30 by clicking. The path that can is a scripted fleet-wide heartbeat repair — the operation this endpoint exists to make correct — at one POST per agent under one operator token. The fleet is 27 agents. 27 of 30 is not headroom; it is a coincidence that expires when the fleet passes 30, and it fails by killing a repair script partway and leaving exactly the split population this PR is untangling. Behind auth and keyed per caller, 120 is as un-DoS-able as 30, and it matches inspectorRateLimit in the same file. 2. The same-file claim survived in three more files; comment-only fixes. agentsRuntime.ts:39 is the origin, and it is refuted 2000 lines below itself — /memory and /memory/sync follow the recipe exactly and both carry open high-severity alerts. install.ts:31 and provision.ts:41 inherited it, and are clean for a reason their comments do not state: both apply the limiter BEFORE auth. Each now records the real discriminator. agentsRuntime's routes are genuinely under-protected rather than false-positived, so the comment says so and says what fixing them requires (reorder plus an auth-independent key generator). Not doing it here: agentRateLimitKeyGenerator needs reading first, and that is a separate change rather than an oversight. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(security): the agentsRuntime reorder is specified, not blocked The comment added in a4d6935 said fixing those routes needs "a key generator that does not depend on auth-set state" and deferred on reading agentRateLimitKeyGenerator. I read it: no change is needed. Its first branch uses req.agentTokenHash, which agentRuntimeAuth sets, but it falls through to a sha256 of the Authorization / x-commonly-agent-token header — present before any middleware runs. Moving the limiter ahead of auth just takes the header branch: same per-caller isolation, different key prefix. Correcting it because a comment naming a blocker that has since been checked and cleared is the same defect this PR spent four commits removing from three other files — a claim about the past that reads as current. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(memory): the cycle trailer names a tool moltbots do not have The comment above withCyclesDirective asserted that the 2026-05-08 forward fix added commonly_log_cycle to the openclaw extension. It did not. Inside the running clawdbot-gateway the extension exposes 25 commonly_* tools and grep -rl commonly_log_cycle /app returns nothing; the tool is defined only in commonly-mcp/src/tools.js. Measured consequence: every moltbot's last cycles append in agentmemories is 83-87 days old, dating to when this trailer started naming the tool, while MCP seats append hourly. The 17 agents carrying the trailer verbatim are the control -- correct directive text is not sufficient when the tool it names is absent from the runtime. Comment-only. Records the evidence rather than the conclusion so the next reader can falsify it against the same artifact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(memory): close the pin-skew loophole on the missing cycle tool The prior comment cited the deployed gateway image only, which leaves the obvious objection open: maybe the submodule pins a tree that has the tool and the image is stale. It does not. _external/clawdbot pins openclaw 0082147920, and that ref's extensions/commonly/src/tools.ts has zero occurrences of log_cycle against a post_message control of 2. Pinned tree, repo tip (read by @ux-lead) and deployed image all agree. Comment-only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(memory): the cycle tool exists on the declared branch; the pin tracks another lineage Correcting my own correction from e8a4368, which said 'not a pin skew, the tool was never there'. It is there. .gitmodules declares branch = rebase-2026.3.29 for _external/clawdbot; commonly_log_cycle landed on that branch at a67f0df6 on 2026-05-09 -- the exact day this trailer started naming it. The pin recorded on main is 0082147920, a different lineage, and that is what builds the gateway. So the original comment was TRUE when written and was invalidated underneath by a pin move. Nothing about it had to change to become false, which is why it survived 87 days. Operational conclusion is unchanged: no live moltbot can call the tool. Adds the warning the remedy needs -- bumping to the declared branch gains five tools and LOSES react_to_message, so it owes a diff of both sets rather than a version bump. Found by @ux-lead. Comment-only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: CLAUDE.md was wrong about the same tool block in both directions Three entries described the openclaw extension's commonly_* block. Each named a tool without naming a ref, so each was checkable only by someone willing to exec into the gateway -- and none of them had been. Verified against the RUNNING image, not the source tree (/app/extensions/commonly/src/tools.ts, 38,086 bytes, byte-identical to the pin; grepped with a positive control, because my first attempt pointed at /app/dist and returned a clean-looking zero for every term including the control): commonly_react_to_message PRESENT, live handler -- documented as absent commonly_open_dm ABSENT -- documented as live commonly_log_cycle ABSENT -- already corrected Two authoritative claims about one 25-tool block, wrong in opposite directions. Same root cause: .gitmodules declares branch = rebase-2026.3.29, main records pin 0082147920, and nothing in the build reads that branch field. The declared branch has the five memory/DM tools and lacks react_to_message; the pin is the mirror image. They have disagreed since 2026-05-09. So a pin bump is not a free fix -- it gains five tools and loses react_to_message. New entry carries that table so the next reader does not propose the bump as a one-liner. presets.ts carries the same table beside the trailer. Reactions: the moltbot/MCP split is real and the general rule stands, but reactions are not an instance of it. The 2026-05-16 smoke that saw moltbots post emoji as message content has not been re-run since the tool became reachable, so behaviour stays unverified and the entry says so rather than declaring the loop closed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: the declared openclaw branch is 48 days STALE, not ahead — do not bump to it Both artifacts said a pin bump "gains five tools and loses react_to_message, so it owes a diff of both tool sets." That rule was scoped to the surface that raised the question, and it is dangerous. The pin IS openclaw main -- compare/main...0082147920 returns `identical`, dated 2026-06-26. The branch .gitmodules declares heads at a67f0df6, 2026-05-09: 48 days OLDER, diverged, ahead 14 / behind 7. It is a stale fork, not a forward target. The 7 commits it is missing are load-bearing: fc6a2231 commonly_react_to_message 2ce923b6 remove direct OAuth rotation from acpx_run, route via LiteLLM only 78a6d174 treat acpx timeout as rate-limit so rotation triggers 16a62bc4 honor OPENCLAW_INSTALL_GH_CLI to install the GitHub CLI eda5e1d4 install officecli + bake commonly-bundled-skills So switching lineages reintroduces direct OAuth rotation inside acpx_run -- against the single-rotator invariant and the IP-bound-ChatGPT-session rule -- and breaks --build-arg OPENCLAW_INSTALL_GH_CLI=1, which CLAUDE.md's own documented gateway build passes and the dev-agent GitHub PAT flow depends on. A tool-set diff surfaces NONE of those. The rule I shipped 20 minutes ago would have waved through both regressions. The check is a diff of the commit RANGE. Remedy corrected in both places: cherry-pick a67f0df6 (plus any of open_dm / read_attachment / read_my_memory / save_my_memory still wanted) onto openclaw main, then move the pin to that new main. Never point the submodule at the branch. Lineage facts from @ux-lead (52565 + the follow-up closing the deployed-image question); the divergence and commit range verified here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(agents): the Docker provisioner never wrote the cycle trailer either agentProvisionerServiceK8s.ts:489 wraps the default template in withCyclesDirective; agentProvisionerService.ts did not. So an agent provisioned on the Docker / self-host path with no matching preset got a HEARTBEAT.md that structurally could not carry the directive -- the same defect this PR fixes on the k8s path, on the sibling file, and the PR title claims the default heartbeat path generally. Scoped deliberately. ensureHeartbeatTemplate only writes the default when HEARTBEAT.md is absent or effectively empty, so this repairs FRESH workspaces only. The k8s path additionally treats "existing file missing CYCLES_DIRECTIVE_MARKER" as stale and rewrites it; that clause is safe there because skipHeartbeat short-circuits on customizations.heartbeat and routes/registry/files.ts sets the flag for hand-authored files. This function takes no customizations argument and its call site passes none, so porting the clause would silently overwrite hand-edited files. Comment records the gap rather than leaving it implied. Left alone on purpose: writeOpenClawHeartbeatFileLocal writes caller-supplied content (the hand-authored path), and must not have a directive injected into what a human wrote. Test asserts the file on disk, not the exported constant -- the defect was in delivery, and pinning the constant would not have seen it. It clears its own workspace because the suite's beforeEach clears the two config files but NOT OPENCLAW_WORKSPACE_ROOT: a HEARTBEAT.md survives between runs, which is what made the first version of this test fail against a file written 40 minutes earlier. Mutation-checked green/red/green -- dropping withCyclesDirective reds exactly this test, 13 others unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: porting the openclaw branch wholesale collides twice, one of them silently The entry said "cherry-pick a67f0df6 ... onto main" but left a 14-commit port open as an equally valid reading. It is not. branch 6c99dc31 tools.ts +11/-2 route acpx_run through LiteLLM via opencode main 2ce923b6 tools.ts +9/-73 remove direct OAuth rotation from acpx_run Main deleted 73 lines; the branch added to that region nine days later, solving the same problem differently. Porting re-introduces what main removed. branch 8b50281b +125 tools.ts +43 client.ts +9 src/plugin-sdk/index.ts main 00821479 +22 tools.ts +68 client.ts commonly_attach_file exists on BOTH lineages as independent implementations. A wholesale port duplicates the registration, in different regions of different files, so git may not conflict at all -- the failure surfaces at runtime, not in review. By contrast a67f0df6 touches one file, +36/-0, pure addition. It cannot collide. Also records why this survived four months: a submodule bump never touches .gitmodules. `git -C _external/clawdbot checkout <sha> && git add _external/clawdbot` leaves the declaration out of the diff, the command and the review. The pin was deliberately moved as recently as 2026-06-26 to gain commonly_attach_file, by someone with no reason to open the file contradicting them. Not neglect -- a field positioned to look like configuration in a workflow that cannot surface it. Lineage collisions raised as open questions by @ux-lead; diffstats read here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: the pin doesn't sit still — it ALTERNATES between the two lineages Fourth rewrite of this comment in one afternoon, and the reason is the finding: the pin is not a stale pointer nobody moved. It has moved 15+ times and has crossed lineages repeatedly. 2026-05-09 f4b7a48 a67f0df6 BRANCH log_cycle ARRIVES 2026-05-17 b6a811b fc6a2231 main LOST (bump was for react_to_message) 2026-05-21 0168f01 a67f0df6 BRANCH RESTORED (#418, explicitly) 2026-05-24 d6e63b2 84549161 main LOST (bump was for bundled-skills) 2026-06-26 a3de6d0 00821479 main current Three corrections to what this file said an hour ago: - not "never pinned" -- pinned twice, and it worked twice - not "nobody looked at the gitlink" -- #418's subject is literally `bump _external/clawdbot fc6a22319 -> a67f0df63`. Somebody caught this exact regression on 05-21. A bundled-skills bump undid it three days later. - not "a stale fork to avoid" -- the branch was a deliberate target twice Nobody was negligent. A submodule bump surfaces the tool it was made for and says nothing about the five it trades away; the diff is one line of hex. Cross-validated by @ux-lead against per-agent last-cycles-append timestamps: writes cluster at 05-09..05-13 and 05-21..05-23, both strictly inside a branch-pinned window, nothing outside them. Mongo and the submodule log agree to the day. Remedy restated: not a bump in either direction, but ending the divergence. Anything less leaves the next unrelated bump free to swap the set back -- which is what happened twice after #418 had already fixed it. Gitlink history read here from `git ls-tree` at each commit that touched _external/clawdbot; windows and cycles correlation from @ux-lead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(agents): a check that reads the openclaw ref, instead of a fifth paragraph The comment in presets.ts describing the openclaw lineage skew was rewritten FOUR times in one afternoon, each version confidently wrong in a different direction. Not carelessness at any one step -- every version was verified before it shipped. A claim about another repo's state does not get fixed; it decays, and prose has no mechanism to notice. scripts/verify-moltbot-tool-contract.js resolves the pinned submodule tree, parses the tool DECLARATIONS out of extensions/commonly/src/tools.ts, and fails when a tool the cycles trailer instructs moltbots to call is not among them. Required tools are derived from CYCLES_REFLECTION_TRAILER itself rather than restated, so editing the trailer to name a different tool is covered without touching the script. Would have fired on 2026-05-17 and again on 2026-05-24 -- the two unrelated bumps that swapped the lineage back after #418 had fixed it by hand. Design notes that are load-bearing: - Declarations, not mentions. `name: "commonly_x"` counts; the string appearing in a description does not. The original defect was a name in prose asserting a capability, so a parser that accepts prose reproduces that defect inside the guard against it. Mutation-checked: loosening the regex reds exactly the prose test. - Exit 2 for "cannot verify" (submodule absent, or zero declarations parsed from a non-empty file), never 0. Four instruments returned clean zeros today whose controls also returned zero; an unrun check must not look like a passing one. - Scoped to the trailer, not the agentMentionService cues, because #818 is changing those and a guard straddling an open PR is a merge conflict rather than a safeguard. Widening point documented in the header. NOT WIRED TO A WORKFLOW YET, deliberately. Only deploy-dev.yml checks out submodules, and the check fails there today because the regression is live -- so wiring it now means either a blocked deploy or a non-blocking check, and a check that cannot fail is exactly the decorative-config defect this whole investigation is about, one layer up. It belongs in the reconciliation PR, where it goes green the moment it goes live. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(adr): five ADR claims about the openclaw extension, all wrong at the pin Verified against the RUNNING gateway, not the source tree (/app/extensions/commonly/src/tools.ts, 25 declared tools): commonly_log_cycle 0 ADR-010:125, ADR-012:408, ADR-012:504 commonly_open_dm 0 ADR-013:750 commonly_read_my_memory 0 ADR-003:178 commonly_save_my_memory 0 ADR-003:178 commonly_read_agent_memory 1 <- control, and the correction below commonly_write_agent_memory 1 ADR-003:178 is backwards rather than merely wrong: the two tools it calls primary are absent and the two it describes as "v1-compatible wrappers retained" are the only memory tools the shipped extension has. ADR-012:504 is the one worth reading. It is not a documentation error -- it was TRUE when written. commonly#307 (f4b7a48, 2026-05-09) really did pin a67f0df6, and moltbots really did log cycles. Then the pin ALTERNATED: 2026-05-09 f4b7a48 a67f0df6 BRANCH ARRIVES <- commonly#307 2026-05-17 b6a811b fc6a2231 main LOST (bump was for react_to_message) 2026-05-21 0168f01 a67f0df6 BRANCH RESTORED (commonly#418) 2026-05-24 d6e63b2 84549161 main LOST (bump was for bundled-skills) 2026-06-26 a3de6d0 00821479 main current Three authors adding three unrelated features, each silently trading away five tools, in a diff that is one line of hex and names none of them. So the fix for these five is not better proofreading -- the claims decayed rather than being written wrong, and nothing in review could see it. That is what scripts/verify-moltbot-tool-contract.js exists for; each correction points at it. Claim locations surfaced by @ux-lead; every one re-verified here against the live image before editing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(ci): the smoke gate's clawdbot glob has never matched a submodule bump ADR-009 Phase 2 added `_external/clawdbot/**` to the smoke gate's pull_request paths so a gateway bump would get the kind smoke before merging. It cannot match. A submodule bump changes the gitlink entry itself, and GitHub reports that changed path as exactly `_external/clawdbot` — nothing is ever under it, because the parent repo tracks no files inside the submodule. Verified against the two gitlink-only PRs rather than by reasoning about glob semantics: #418 and #443 both report `files: ["_external/clawdbot"]` from the PR files API, and neither PR head has a `kind cluster smoke test` check run. Both DID get smoked after merge, via the `workflow_run` trigger on main — so this is not "never ran", it is "never ran while it could still block the merge", which is the entire purpose of a PR gate. That matters here specifically: the clawdbot-gateway image is built from this submodule, five bumps have landed, and three of them silently swapped the extension's whole `commonly_*` tool set between two diverged openclaw lineages. Keep both entries. The bare path catches the bump; the glob still covers vendored files if any are ever tracked directly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- 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.
Summary
The
_external/clawdbotsubmodule pointer is out of date relative to the openclaw fork'srebase-2026.3.29branch by 25+ commits. Most critically, it's missing thecommonly_attach_fileextension tool — the kernel-level affordance that the backend's pod-context cue actively tells agents to call.Root-cause story (smoke 2026-05-20 cycle 6)
Aria reported the error:
Investigation today: there is no
commonly_attach_filetool definition anywhere in our currently-pinned submodule SHA (fc6a22319). The pod-context cue inagentMentionService.formatPodContextFrametells agents to callcommonly_attach_file({ podId, filePath, message })— which at fc6a22319 has no matching tool. Aria's model fell back tocommonly_post_messageand strippedmessage(assuming alias forcontent), producing the schema-validation error.CLAUDE.md memory + ADR-013 Phase 0b BOTH document
commonly_attach_fileas shipped (Nova nailed e2e attach 2026-05-08 deployd7a68926). The submodule SHA appears to have regressed since.What this bump brings in (selected; full list in commit message)
commonly_attach_fileextension tool (8b50281b8) + workspace-resolution fix (18c2967c7)commonly_read_attachmentone-shot file read (ae3235c12)commonly_log_cycleADR-012 Phase 2 append-only cycles writer (a67f0df63)commonly_open_dm— autonomous agent-to-agent DM initiation (11878b43c)commonly_read_my_memory+commonly_save_my_memory— ADR-003 Phase 2b (826d46475)acpx_runLiteLLM routing (6c99dc312)[[reply_to]]strip incommonly_post_message(480298e30)TOOL_ROUTING_HINT,SELF_IDENTITY_NOTEinjection, gh CLI, ripgrep (519cdb664)git log fc6a22319..a67f0df63 -- extensions/commonly/Why now
Multiple bugs from yesterday's smoke trace back to these missing tools:
content requirederror → nocommonly_attach_file(this PR)commonly_open_dmfor cross-runtime collab → nocommonly_open_dm(this PR also fixes that — combined with PR feat(agentMentionService): inline cross-runtime consultation cue #416 the consultation cue)Risk
The submodule range includes 25+ commits. Some may be behavior-changing. CI on this PR should catch obvious regressions. Recommend deploy to dev first via Deploy Dev workflow before any prod-like rollout.
Test plan
commonly_attach_file({podId, filePath, message})produces a real File row +[[upload:fileName]]directive in chatcommonly_open_dmmid-turn🤖 Generated with Claude Code