feat(firstmate-calm): show supervision notes in Claude Code - #6039
Merged
Merged
Conversation
The Calm mod follows a bounded display tail copy of the outcome store, which bin/fm-branch-outcome.sh append now refreshes, and the supervision host's latch, and appends one dim transcript line per visible routine outcome, captain outcome, and latch change, replaying unread and unprocessed outcomes at session start. It shows them whenever the mod is active, regardless of config/calm, and never marks anything read.
Claude Code 2.1.283 stores ui.log lines in the session and restores them on --continue, so the mod records how far each session has followed the outcome store and a resume replays only newer outcomes. It also checks file existence before reads so absent files do not log debug errors. The live guard gains the supervision-notes scenario and the dated 2.1.283 record documents the observed behavior.
|
… complete store rows and write it through the existing byte- and row-limited tail writer. Added a regression test with malformed history outside that window and updated the script header. Outcome tests and shellcheck passed; the full session-start suite timed out after 240 seconds
knowttl
pushed a commit
to knowttl/firstmate
that referenced
this pull request
Sep 29, 2026
…uid#6039) * feat(calm): show supervision sailboat and anchor notes on Claude Code The Calm mod follows a bounded display tail copy of the outcome store, which bin/fm-branch-outcome.sh append now refreshes, and the supervision host's latch, and appends one dim transcript line per visible routine outcome, captain outcome, and latch change, replaying unread and unprocessed outcomes at session start. It shows them whenever the mod is active, regardless of config/calm, and never marks anything read. * fix(calm): show each supervision note once per session on Claude Code Claude Code 2.1.283 stores ui.log lines in the session and restores them on --continue, so the mod records how far each session has followed the outcome store and a resume replays only newer outcomes. It also checks file existence before reads so absent files do not log debug errors. The live guard gains the supervision-notes scenario and the dated 2.1.283 record documents the observed behavior. * docs: name the Claude supervision note row as the engine draws it * no-mistakes(review): Seed outcome tail on present and anchor first tail on markers * no-mistakes(review): Seed outcome tail at session start; replay against start markers * no-mistakes(review): Bound outcome tail by bytes; reread recently changed files * no-mistakes(review): Skip store validation when outcome tail already exists * no-mistakes(document): Clarify bounded Claude supervision note replay * no-mistakes(ci): Fixed seed-tail to validate only a bounded suffix of complete store rows and write it through the existing byte- and row-limited tail writer. Added a regression test with malformed history outside that window and updated the script header. Outcome tests and shellcheck passed; the full session-start suite timed out after 240 seconds
RooseveltAdvisors
pushed a commit
to RooseveltAdvisors/firstmate
that referenced
this pull request
Sep 29, 2026
…uid#6039) * feat(calm): show supervision sailboat and anchor notes on Claude Code The Calm mod follows a bounded display tail copy of the outcome store, which bin/fm-branch-outcome.sh append now refreshes, and the supervision host's latch, and appends one dim transcript line per visible routine outcome, captain outcome, and latch change, replaying unread and unprocessed outcomes at session start. It shows them whenever the mod is active, regardless of config/calm, and never marks anything read. * fix(calm): show each supervision note once per session on Claude Code Claude Code 2.1.283 stores ui.log lines in the session and restores them on --continue, so the mod records how far each session has followed the outcome store and a resume replays only newer outcomes. It also checks file existence before reads so absent files do not log debug errors. The live guard gains the supervision-notes scenario and the dated 2.1.283 record documents the observed behavior. * docs: name the Claude supervision note row as the engine draws it * no-mistakes(review): Seed outcome tail on present and anchor first tail on markers * no-mistakes(review): Seed outcome tail at session start; replay against start markers * no-mistakes(review): Bound outcome tail by bytes; reread recently changed files * no-mistakes(review): Skip store validation when outcome tail already exists * no-mistakes(document): Clarify bounded Claude supervision note replay * no-mistakes(ci): Fixed seed-tail to validate only a bounded suffix of complete store rows and write it through the existing byte- and row-limited tail writer. Added a regression test with malformed history outside that window and updated the script header. Outcome tests and shellcheck passed; the full session-start suite timed out after 240 seconds
andrewesweet
pushed a commit
to andrewesweet/firstmate
that referenced
this pull request
Sep 30, 2026
…uid#6039) * feat(calm): show supervision sailboat and anchor notes on Claude Code The Calm mod follows a bounded display tail copy of the outcome store, which bin/fm-branch-outcome.sh append now refreshes, and the supervision host's latch, and appends one dim transcript line per visible routine outcome, captain outcome, and latch change, replaying unread and unprocessed outcomes at session start. It shows them whenever the mod is active, regardless of config/calm, and never marks anything read. * fix(calm): show each supervision note once per session on Claude Code Claude Code 2.1.283 stores ui.log lines in the session and restores them on --continue, so the mod records how far each session has followed the outcome store and a resume replays only newer outcomes. It also checks file existence before reads so absent files do not log debug errors. The live guard gains the supervision-notes scenario and the dated 2.1.283 record documents the observed behavior. * docs: name the Claude supervision note row as the engine draws it * no-mistakes(review): Seed outcome tail on present and anchor first tail on markers * no-mistakes(review): Seed outcome tail at session start; replay against start markers * no-mistakes(review): Bound outcome tail by bytes; reread recently changed files * no-mistakes(review): Skip store validation when outcome tail already exists * no-mistakes(document): Clarify bounded Claude supervision note replay * no-mistakes(ci): Fixed seed-tail to validate only a bounded suffix of complete store rows and write it through the existing byte- and row-limited tail writer. Added a regression test with malformed history outside that window and updated the script header. Outcome tests and shellcheck passed; the full session-start suite timed out after 240 seconds
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.
Intent
convert decided plan to an implementation plan in md file and pass that back to firstmate for implementation
Context: that closed the captain's review of the AFK revamp audit, in which the captain selected "Sequence and 3d scope: approve-claude-only-3d1" (the audit's proposed execution sequence approved; the first default-on flip is Claude-only), "3e non-Claude scope: build-3d2-first", all twelve backlog dispositions, and "Sailboat and anchor on Claude: build-now-always-on". The resulting plan is data/fm-afk-revamp-audit-s1/implementation-plan.md in the Firstmate home ~/fm-homes/fmdev-f1, with its evidence in report.md beside it. The AFK revamp's standing words also apply: "2 should be done by opus crewmates" and "this is a major architectural revamp so i want it to do very careful live validation including regression in isolated live environments with some real complex sessions before calling it done. it's ok to use my real llm tokens here".
This task is the sailboat and anchor lane in that plan: render Pi's supervision notes on Claude through the Calm mod - a routine note line (⛵ :
Substance of the referenced decision for this lane (call 4, build-now-always-on): build the Claude sailboat and anchor notes now, in parallel with the other AFK revamp work, and show them whenever the Calm mod is active, as Pi shows them regardless of Calm. On Pi, the sailboat is the dim note Pi renders for a routine supervision outcome that is not silent (plus the two supervision health notes when the branch pauses after repeated provider errors and when it recovers after a successful cooldown probe), and the anchor is the sequence-keyed entry Pi renders for a captain outcome; the outcome store is state/branch-outcomes.jsonl owned by bin/fm-branch-outcome.sh.
What Changed
Limitations
$.ui.lognote in the session file as a display-only system entry ("type":"system","subtype":"informational"), andclaude --continuerestores it. The mod therefore records in its own plugin store how far each session has followed the outcomes, so a resumed session replays only outcomes it has not shown. The notes never reach the model; a live Haiku turn asked to quote them quoted none.⏺bullet and thefirstmate-calm:prefix; the glyph cannot take its own color as on Pi.Stop hook feedbackrow that wakes main for a captain outcome stays (it fires no hookable drawing); the anchor line appears beside it.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1, which is unset on the main home; enabling it there is a separate captain step.Risk Assessment
Testing
Targeted Calm, plugin, and outcome-store checks passed; the broader session-start check did not finish within 600 seconds. Real Claude panes showed replay and new notes with Calm off, routine and health notes with Calm on, and no note with function hooks off. A real outcome-store append kept its verbatim tail under 1 MiB. The UI evidence is tmux captures rather than screenshots because this is a terminal surface.
Evidence: Claude supervision notes with Calm off
Source: Claude supervision notes with Calm off
Evidence: Claude with function hooks off
Source: Claude with function hooks off
Evidence: Claude health notes with Calm on
Source: Claude health notes with Calm on
Evidence: Verbatim outcome tail within byte budget
Source: Verbatim outcome tail within byte budget
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
.claude/mods/firstmate-calm/hooks/register.ts:266- An existing home can have unprocessed captain outcomes in branch-outcomes.jsonl but no display tail, because bin/fm-branch-outcome.sh:518 creates that copy only on a future append. Session start then replays nothing, and a later append does not recover those older outcomes because fm-branch-notes.ts:123 filters by the session-start epoch. This contradicts the required anchor line for each captain outcome from the outcome store. Ask how to authorize migration or another way to read existing outcomes; correcting this may require backfilling the new durable copy..claude/mods/firstmate-calm/lib/fm-branch-notes.ts:102- The intent requires a line ‘for each non-silent routine outcome’ and ‘for each captain outcome,’ but this added replay cap replaces the first of 21 due notes with a count. Remove the unrequired 20-note replay cap if that per-outcome requirement applies to startup replay. The sibling 200-row display cap in bin/fm-branch-outcome.sh:143, consumed at register.ts:324, also silently loses notes if more than 200 outcomes arrive between polls; its bound needs a lossless catch-up path to preserve the same invariant..claude/mods/firstmate-calm/lib/fm-branch-notes.ts:123- When the tail does not exist at session start, an append later in that same second creates it with earlier rows. The epoch >= sinceEpoch fallback treats those earlier rows as new, including already-read routine or processed captain outcomes, and logs them again. Anchor the first tail against the existing store or markers rather than using second-resolution timestamps; register.ts:280 supplies the ambiguous timestamp.🔧 Fix applied.
3 issues (2 errors, 1 warning) still open:
.claude/mods/firstmate-calm/hooks/register.ts:266- An existing home can have unprocessed captain outcomes in branch-outcomes.jsonl but no display tail, because bin/fm-branch-outcome.sh:518 creates that copy only on a future append. Session start then replays nothing, and a later append does not recover those older outcomes because fm-branch-notes.ts:123 filters by the session-start epoch. This contradicts the required anchor line for each captain outcome from the outcome store. Ask how to authorize migration or another way to read existing outcomes; correcting this may require backfilling the new durable copy.bin/fm-branch-outcome.sh:595- The round-1 fix left the existing-home path unresolved: an absent tail is seeded only bypresent, butbin/fm-wake-drain.sh:621skips presentation while.afk-contractexists, and the mod can start without a drain. In either case, an existing unprocessed captain row has no tail forregister.ts:291to read and gets no anchor until a later append or eligible drain. The accepted decision requires an existing home to have a display tail before the mod needs it. Choose a single seeding point that runs in those cases..claude/mods/firstmate-calm/hooks/register.ts:297- The round-1 first-tail fix moved a loss case: start with no tail, append a visible routine outcome, then drain it before the mod's three-second poll. The drain advances the cursor; the first-tail replay reads that new cursor at lines 297–299 and omits the outcome, even though this session never showed its sailboat. Anchor the initial replay to the session-start markers while retaining the protection against replaying rows already read at start.🔧 Fix applied.
5 issues (2 errors, 3 warnings) still open:
.claude/mods/firstmate-calm/hooks/register.ts:266- An existing home can have unprocessed captain outcomes in branch-outcomes.jsonl but no display tail, because bin/fm-branch-outcome.sh:518 creates that copy only on a future append. Session start then replays nothing, and a later append does not recover those older outcomes because fm-branch-notes.ts:123 filters by the session-start epoch. This contradicts the required anchor line for each captain outcome from the outcome store. Ask how to authorize migration or another way to read existing outcomes; correcting this may require backfilling the new durable copy.bin/fm-branch-outcome.sh:595- The round-1 fix left the existing-home path unresolved: an absent tail is seeded only bypresent, butbin/fm-wake-drain.sh:621skips presentation while.afk-contractexists, and the mod can start without a drain. In either case, an existing unprocessed captain row has no tail forregister.ts:291to read and gets no anchor until a later append or eligible drain. The accepted decision requires an existing home to have a display tail before the mod needs it. Choose a single seeding point that runs in those cases..claude/mods/firstmate-calm/hooks/register.ts:297- The round-1 first-tail fix moved a loss case: start with no tail, append a visible routine outcome, then drain it before the mod's three-second poll. The drain advances the cursor; the first-tail replay reads that new cursor at lines 297–299 and omits the outcome, even though this session never showed its sailboat. Anchor the initial replay to the session-start markers while retaining the protection against replaying rows already read at start.bin/fm-branch-outcome.sh:348- The display tail is bounded to 200 rows, not 4 MiB. The store accepts summaries without a length limit, so 200 valid rows with roughly 21 KiB summaries produce a tail the Claude mod cannot read; subsequent polls show no outcomes until enough large rows leave the tail. The same byte limit must hold when append refreshes the tail (line 525) and when seed-tail creates it (line 766). Resolving how to preserve exact notes while bounding the copy needs a product decision..claude/mods/firstmate-calm/hooks/register.ts:250- A full 200-row tail can be replaced with a same-size copy within one filesystem timestamp tick. If the mod polls between two such appends, the second copy has the same size and mtimeMs as the recorded stamp, so every later poll skips it and its new outcome never gets a note. Use a change check that cannot permanently discard a same-stamp replacement.🔧 Fix applied.
3 issues (1 error, 2 warnings) still open:
bin/fm-branch-outcome.sh:595- The round-1 fix left the existing-home path unresolved: an absent tail is seeded only bypresent, butbin/fm-wake-drain.sh:621skips presentation while.afk-contractexists, and the mod can start without a drain. In either case, an existing unprocessed captain row has no tail forregister.ts:291to read and gets no anchor until a later append or eligible drain. The accepted decision requires an existing home to have a display tail before the mod needs it. Choose a single seeding point that runs in those cases.bin/fm-branch-outcome.sh:348- The display tail is bounded to 200 rows, not 4 MiB. The store accepts summaries without a length limit, so 200 valid rows with roughly 21 KiB summaries produce a tail the Claude mod cannot read; subsequent polls show no outcomes until enough large rows leave the tail. The same byte limit must hold when append refreshes the tail (line 525) and when seed-tail creates it (line 766). Resolving how to preserve exact notes while bounding the copy needs a product decision.bin/fm-branch-outcome.sh:773- Round 2 introduced a full-storelast_seqvalidation before checking whether the display tail already exists. Every locked session start now parses the unbounded outcome history even when seeding has nothing to do, delaying startup as the store grows. Check for an existing tail under the lock first; validate the store only when a copy must be created.🔧 Fix applied.
1 warning still open:
.claude/mods/firstmate-calm/hooks/register.ts:311- Round 3's byte-bound fix left a first-appearance gap: append an outcome larger than 1 MiB to an empty store, start the mod (the tail is empty), then append a small outcome. The first nonempty tail takes the replay path, sonewOutcomeNotesnever counts the oversized outcome. The same invariant must hold at.claude/mods/firstmate-calm/lib/fm-branch-notes.ts:99(startup replay) andbin/fm-branch-outcome.sh:353(the writer that can produce an empty tail). The later decision says an oversized row is omitted and “the mod's existing gap count line covers it”; that count is absent here. Ask how first-appearance gaps should be counted without treating previously read history as newly missed.✅ **Test** - passed
✅ No issues found.
tests/fm-calm-claude-mod.test.sh(direct invocation was not executable; reran withbash)bash tests/fm-calm-claude-mod.test.sh && bash tests/fm-calm-claude-mod-plugin.test.sh && bash tests/fm-branch-supervision.test.sh && bash tests/fm-session-start.test.sh(the final suite timed out after 600 seconds)Launched real Claude Code 2.1.284 primaries on privatefm-labtmux sockets with disposable homes; appended outcomes throughbin/fm-branch-outcome.shand captured the rendered panesAppended 100 large outcomes throughbin/fm-branch-outcome.sh; compared the display tail byte-for-byte with the newest whole store rows✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.