Symptom
buddy_react fires, buddy_stats confirms Source: tool with the model-written text — and the statusline bubble shows ....
Evidence
State dir after one buddy_react call, same wall-clock minute:
reaction.4758cc43.json source: tool "*ears twitch* thirty-one hours of insisting..."
reaction.d26d5a00.json source: fallback "..."
CLAUDE_CODE_SESSION_ID in the shell is d26d5a00-..., which is what scripts/paths.sh derives BUDDY_SID from — so the statusline and the Stop hook read d26d5a00. The MCP server resolved 4758cc43 and wrote there.
Consequence
Two failures stack:
- The model-authored reaction is written to a file nothing renders.
- The Stop hook looks in its session's file, finds no fresh
source: "tool" reaction, and correctly falls back to the canned pool — writing the literal "..." pool entry over what the user sees.
So the freshness/sentinel logic added in #159 cannot help: it is comparing files that belong to different sessions. From the outside this is indistinguishable from "reactions are canned", which is the exact complaint #154/#159 set out to fix.
Why the MCP server disagrees
The MCP server process is long-lived and started with whatever environment existed at launch. If it was spawned before/outside the session that Claude Code later reports — or with a different CLAUDE_CODE_SESSION_ID — its BUDDY_SID is pinned to that stale value for the process lifetime, while every statusline invocation resolves the current one.
Proposed fix
The session id must not be captured per-process when producer and consumer are different processes. Options, roughly in order of preference:
- Have
buddy_react (and other MCP writers) resolve the session id per call from the request context rather than from the process environment at startup.
- Failing that, write the reaction to a session-agnostic "latest" file that the statusline falls back to when its own session file is older — with the session file still preferred so multiple concurrent sessions do not bleed into each other.
- At minimum: make the Stop hook's fallback not overwrite a
source: "tool" reaction that is newer, regardless of which session file it lives in.
Note
This was only diagnosable because of the source provenance field from #159 — tool in one file, fallback in the other. Without it the symptom reads as "still canned" and points at entirely the wrong code.
🤖 This content was generated with AI assistance using Claude Opus 5.
Symptom
buddy_reactfires,buddy_statsconfirmsSource: toolwith the model-written text — and the statusline bubble shows....Evidence
State dir after one
buddy_reactcall, same wall-clock minute:CLAUDE_CODE_SESSION_IDin the shell isd26d5a00-..., which is whatscripts/paths.shderivesBUDDY_SIDfrom — so the statusline and the Stop hook readd26d5a00. The MCP server resolved4758cc43and wrote there.Consequence
Two failures stack:
source: "tool"reaction, and correctly falls back to the canned pool — writing the literal"..."pool entry over what the user sees.So the freshness/sentinel logic added in #159 cannot help: it is comparing files that belong to different sessions. From the outside this is indistinguishable from "reactions are canned", which is the exact complaint #154/#159 set out to fix.
Why the MCP server disagrees
The MCP server process is long-lived and started with whatever environment existed at launch. If it was spawned before/outside the session that Claude Code later reports — or with a different
CLAUDE_CODE_SESSION_ID— itsBUDDY_SIDis pinned to that stale value for the process lifetime, while every statusline invocation resolves the current one.Proposed fix
The session id must not be captured per-process when producer and consumer are different processes. Options, roughly in order of preference:
buddy_react(and other MCP writers) resolve the session id per call from the request context rather than from the process environment at startup.source: "tool"reaction that is newer, regardless of which session file it lives in.Note
This was only diagnosable because of the
sourceprovenance field from #159 —toolin one file,fallbackin the other. Without it the symptom reads as "still canned" and points at entirely the wrong code.🤖 This content was generated with AI assistance using Claude Opus 5.