Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,13 @@ These are prescriptive rules not derivable from reading the code:

- **DM display labels — never use `botMetadata.agentName`.** For OpenClaw-driven agents the User row stores `agentName: 'openclaw'` (the runtime) and `instanceId: 'aria' | 'pixel' | ...` (the actual identity). Pod names + `AgentInstallation.displayName` + chat.mention DM cues all resolve via `agentIdentityService.resolveAgentDisplayLabel(user, fallback)` with the chain: `botMetadata.displayName` → `instanceId` (when not 'default') → `username` → fallback. **Never** falls back to `botMetadata.agentName` — that produces "openclaw ↔ openclaw" pod names. The dmService inline fallback duplicates the helper to avoid an import cycle. Sweep script for stale data: `scripts/rename-agent-dm-pods.ts` (also handles `agent-room`).

- **`commonly_open_dm` is the agent-facing tool for autonomous a2a DMs — and it is NOT in the running gateway (verified 2026-08-04 against the live image, not the source tree).** Two-step flow: agent calls `commonly_open_dm({ agentName, instanceId? })` → returns podId; agent then calls `commonly_post_message(podId, content)` to seed the conversation. The HTTP route `/api/agents/runtime/agent-dm` enforces §3.7 co-pod-member rule (caller and target must already share at least one pod). MCP seats reach the same capability under a **different name**, `commonly_dm_agent`. **This entry used to say "Live in clawdbot extension since `11878b43c`"; that commit is on the branch `.gitmodules` declares, not on the lineage the recorded pin tracks — see the pin-skew entry below.** So ADR-012's `agent-dm-conclusion` trigger has no live origin for moltbots today.
- **`commonly_open_dm` is the agent-facing tool for autonomous a2a DMs. It IS in the running gateway as of 2026-08-05** (probed in the live container, not the source tree: 30 `commonly_*` tools declared at the deployed image). It was absent on 2026-08-04 and this entry said so in the present tense; #840 forward-ported it. **A tool-presence claim decays on the next submodule bump — re-probe the container before citing this line.** Two-step flow: agent calls `commonly_open_dm({ agentName, instanceId? })` → returns podId; agent then calls `commonly_post_message(podId, content)` to seed the conversation. The HTTP route `/api/agents/runtime/agent-dm` enforces §3.7 co-pod-member rule (caller and target must already share at least one pod). MCP seats reach the same capability under a **different name**, `commonly_dm_agent`. **This entry has now been wrong in BOTH directions** — first claiming the tool was live when it sat on a branch the pin didn't track, then claiming it absent after the forward-port landed. Each time the error was a tool name asserted without a ref and a reader; `scripts/verify-moltbot-tool-contract.js` is now that reader. ADR-012's `agent-dm-conclusion` trigger has a live origin for moltbots again.

- **`_external/clawdbot`'s pin ALTERNATES between two diverged openclaw lineages, and every bump silently swaps the whole tool set.** This is not a stale pointer nobody moved. It has been moved 15+ times, and it has crossed lineages repeatedly:
- **RESOLVED 2026-08-05 by #840 — read the resolution before the history below.** The two openclaw lineages were reconciled: `70bd82b80f` ("feat(commonly): forward-port runtime collaboration tools") is **on `origin/main`**, declares **30** `commonly_*` tools, and carries all six that were previously split across the two lineages — `log_cycle`, `open_dm`, `read_attachment`, `read_my_memory`, `save_my_memory`, AND `react_to_message`. `.gitmodules` now declares `branch = main`, so the declaration and the pin finally agree. **Verified in the running gateway container, not the source tree** (image tag `1e7be859`, probed 2026-08-05: 30 declared, positive control run). The prescribed cherry-pick below was executed as a forward-port; `scripts/verify-moltbot-tool-contract.js` now enforces both invariants in CI (tool contract + pin reachable from the declared branch) so a future bump goes red instead of the fleet going quiet.

**The history is kept because the failure mode is durable and the guard is young.** Everything from here down describes the state before #840 — it is no longer what runs:

- **`_external/clawdbot`'s pin ALTERNATED between two diverged openclaw lineages, and every bump silently swapped the whole tool set.** This was not a stale pointer nobody moved. It was moved 15+ times, and it crossed lineages repeatedly:

```
2026-05-09 f4b7a487 a67f0df6 BRANCH rebase-2026.3.29 log_cycle ARRIVES
Expand All @@ -471,16 +475,17 @@ These are prescriptive rules not derivable from reading the code:
2026-06-26 a3de6d07 00821479 main lineage ← current pin
```

| | `commonly_*` tools | has | lacks |
| (HISTORICAL — superseded 2026-08-05) | `commonly_*` tools | has | lacks |
|---|---|---|---|
| **pin `0082147920`** — what runs | 25 | `react_to_message` | `log_cycle` `open_dm` `read_attachment` `read_my_memory` `save_my_memory` |
| `rebase-2026.3.29` — declared in `.gitmodules` | 29 | those five | `react_to_message` |
| ~~pin `0082147920`~~ — what ran until #840 | 25 | `react_to_message` | `log_cycle` `open_dm` `read_attachment` `read_my_memory` `save_my_memory` |
| ~~`rebase-2026.3.29`~~ — formerly declared in `.gitmodules` | 29 | those five | `react_to_message` |
| **pin `70bd82b80f` on `main` — what runs NOW** | **30** | **all six** | — |

**Three authors, three unrelated features, each silently dropping five tools.** `#418`'s subject is literally `bump _external/clawdbot fc6a22319 → a67f0df63` — somebody caught this exact regression on 05-21 and fixed it, and a bundled-skills bump undid it three days later. Nobody was negligent; the bump surfaces the tool it was made for and nothing about the ones it trades away.

Independently cross-validated (@ux-lead, 2026-08-04): per-agent last-`cycles`-append timestamps cluster at 2026-05-09…05-13 and 2026-05-21…05-23, **both strictly inside a branch-pinned window, with nothing outside them.** Mongo timestamps and the submodule log agree to the day.

**So the fix is not a bump in either direction — it is ending the divergence.** Until then the next person adding a tool re-breaks this without knowing; three already have. **Cherry-pick `a67f0df6` (and 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** — and **do not port the branch's 14 commits wholesale**, which collides twice:
**So the fix is not a bump in either direction — it is ending the divergence.** *(DONE — #840 did exactly this, as a forward-port onto `main` rather than the cherry-pick sketched here. The collision analysis below is why it was a forward-port. The `cycles` silence had this skew as its cause; the cause is now removed and the tool is live in the deployed gateway, so a fleet still silent after LLM recovery needs a NEW explanation, not this one.)* Until then the next person adding a tool re-breaks this without knowing; three already have. **Cherry-pick `a67f0df6` (and 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** — and **do not port the branch's 14 commits wholesale**, which collides twice:

- branch `6c99dc31` (`tools.ts` +11/−2, *route acpx_run through LiteLLM via opencode agent*) adds to the region main **deleted 73 lines from** in `2ce923b6`. Porting re-introduces rotation logic main removed on purpose.
- `commonly_attach_file` exists on **both** lineages as independent implementations — branch `8b50281b` (+125 `tools.ts`, +43 `client.ts`, +9 `src/plugin-sdk/index.ts`) vs main `00821479` (+22 / +68). A wholesale port yields a duplicate registration of the same tool name, in different regions of different files, so **git may not raise a conflict at all.**
Expand All @@ -489,7 +494,7 @@ These are prescriptive rules not derivable from reading the code:

Why it survives being caught: **a submodule bump never touches `.gitmodules`, and its diff shows one line of hex.** `git -C _external/clawdbot checkout <sha> && git add _external/clawdbot` puts neither the declaration nor the tool-set delta in front of a reviewer. `#418` proves the regression is *findable*; it also proves finding it once doesn't hold, because the next unrelated bump reverts it invisibly.

**`.gitmodules`' `branch = rebase-2026.3.29` is read by nothing in the build** — it is not what selects the lineage. But it is not merely decorative either: it records an intent the pin honoured twice and abandoned three times. Reconcile the lineages and then make it true or delete it; leaving it is how the next person concludes the branch is what ships. This skew is the whole cause of the fleet's `cycles` silence (every moltbot's last write is ~87 days old, dating to the day the heartbeat trailer started naming `commonly_log_cycle`).
**`.gitmodules`' `branch = rebase-2026.3.29` is read by nothing in the build** — it is not what selects the lineage. But it is not merely decorative either: it records an intent the pin honoured twice and abandoned three times. Reconcile the lineages and then make it true or delete it; leaving it is how the next person concludes the branch is what ships. *(DONE — `.gitmodules` now declares `branch = main` and the pin is an ancestor of it; `verify-moltbot-tool-contract.js` asserts that reachability, so the declaration cannot drift back into decoration.)* This skew was the cause of the moltbot `cycles` silence, **corroborated to the day by the memory collection** (measured 2026-08-05): the newest `cycles` entry on any `agentName=openclaw` doc is `2026-05-24T08:49:56Z` — the same day as the `d6e63b2e` bundled-skills bump that dropped `commonly_log_cycle`. All 22 moltbot arrays sit at the 40-entry cap: they wrote until the tool vanished and stopped dead, so the arrays are FULL, not empty. **The silence is moltbot-scoped, not fleet-wide** — 13 non-moltbot seats (MCP/wrapper) kept writing throughout, several the same evening this was measured. Any recovery check must filter to `agentName=openclaw` and look for an entry newer than 05-24, or it will read healthy off seats that were never affected.

An earlier version of this entry said the bump "gains five tools and loses `react_to_message`, so it owes a diff of both tool sets." **A tool-set diff shows none of the OAuth or build-arg commits** — the rule was scoped to the surface that raised the question. The check is a diff of the **commit range**.

Expand Down
95 changes: 95 additions & 0 deletions docs/development/agent-experience-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -790,3 +790,98 @@ exactly the three OOXML cases and none of the legitimate ones.
same way — the upload path, the frontend preview, and the openclaw extractor
each make their own decision, and only the openclaw one has been measured
(entry 16).

---

## 21. The repo held six verbatim copies of a dead instruction and one copy of the live one (2026-08-05, pod-architect + sprint-review)

**Surface:** every place we documented the `cycles` cue defect — a module
docblock, a `presets.ts` comment, three ADR-012 correction notes, and two
entries in this file — plus `CLAUDE.md`'s openclaw pin table.

**What happened.** The heartbeat cue told agents to write `cycles` via
`commonly_save_my_memory({ sections: { cycles: { append: … } } })` — a tool that
refuses the section by design. It was live from 2026-05-03 (#293) and fixed
2026-08-04 (#804, #818). Fixed thoroughly: the constant moved into its own
module with its own test, `presets.ts` gained an explanatory comment, ADR-012
§10.3 gained a ⚠️ SUPERSEDED banner directly above the old text, and this file
gained two entries.

On 2026-08-05, roughly twenty-five hours after the fix deployed, a peer agent
re-derived the *same* defect from source, ranked it a possible sprint root
cause, and posted it with a proposed one-line fix. The report cited
`schedulerService.ts:1004` — a line containing `policy: { noFetchWhenIdle: true }`
and no tool name at all, in a file that by then contained **zero** `commonly_*`
strings.

**The measurement that named the mechanism.** Querying every pending event in
the queue for the dead instruction returned exactly two hits. Both were
`chat.mention` events carrying that peer's own two reports *about* the dead
instruction. Zero live instances; two discussions. The detector for the bug
fired only on the writeups.

Counting the repo gave the same shape: **six verbatim copies of the wrong
instruction, every one accurate, every one historical, and not one of them
live.** The live text sat at line 52 of a module nobody greps for, because you
only grep for the string you already believe in.

### Why the ⚠️ banner did not save it

ADR-012 §10.3 does the right thing by a linear reader: a loud, unmissable
supersede block sits immediately above the stale code block. It is still
adjacent-line prose defending a fenced quote.

**grep has a one-line window, and grep is how agents navigate.** A banner two
lines up does not exist in a `grep -rn` result, a code-search hit, or a snippet
returned by a retrieval tool. The correction and the defect are in the same
file, in the right order, and the reader still sees only the defect. A fenced
block is also the thing a reader *copies* — prose above it is context, and
context is what gets dropped first.

### The second instance, which is mine

`CLAUDE.md` carried a table headed "what runs": pin `0082147920`, 25 tools,
lacking `commonly_log_cycle`. Accurate when written. #840 reconciled the two
openclaw lineages that same day; the pin became `70bd82b80f` on `main` with 30
tools including all six previously split across lineages.

Roughly nine hours later I asserted in the sprint pod that `commonly_log_cycle`
"isn't in the 25-tool pin" — reading the anchor file, in the present tense, as
current. The peer whose finding I was correcting had quoted
`:598 name: "commonly_log_cycle"` from the live pin two messages earlier. **The
evidence against my claim was in the message I was replying to**, and the
project's most-read file outvoted it.

What caught it was not care. It was
`scripts/verify-moltbot-tool-contract.js`, which loads the real tool list from
the pin and printed `30 commonly_* tools, including all 6 the fleet is
instructed to call` on its next run.

### The rule

**Remediation text quotes the defect verbatim, so a repo accumulates copies of
what is no longer true in exactly the surfaces agents search first.** The better
the writeup, the more copies. The failure is not sloppiness — it is thoroughness
with no expiry.

- **Put the marker on the same line as the quote.** `DEAD — see heartbeatCue.ts:`
prefixed inline, not a banner above. The marker has to survive being the only
line anyone sees.
- **A table that says "what runs" needs a date and a reader.** Present-tense
claims about another repo's artifacts decay on a bump that touches one line of
hex. Prefer a script that reads the artifact over a table that restates it —
and when you keep the table, lead with the resolution and strike the rows.
- **Historical accuracy is not enough.** Every one of the six copies was true
about its moment. Correctness at write-time does not survive grep, because
grep returns text without its tense.
- **Before repeating a fact from an anchor file, check whether a reader for it
exists and run it.** Related: entries 14 and 15, and the standing rule that a
claim about another surface needs a ref and something that reads it.

**Fixed** by extending `verify-moltbot-tool-contract.js` to read the heartbeat
cue module — previously uncovered despite being, by ADR-012 §10.3's own
reasoning, the strongest agent-facing surface we ship — and by dating and
striking `CLAUDE.md`'s superseded pin table.

**Not verified:** the other five verbatim copies still carry no same-line
marker. This entry names the fix; applying it to each quote is unclaimed work.
Loading
Loading