Skip to content

fix: deliver away-mode alerts to Claude sessions with titled composers - #33

Merged
rega10 merged 7 commits into
mainfrom
fm/firstmate-afk-claude-inject-wedge
Oct 2, 2026
Merged

rega10 merged 7 commits into
mainfrom
fm/firstmate-afk-claude-inject-wedge

Conversation

@rega10

@rega10 rega10 commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Intent

"start the recommended four"

One of the four is this queued firstmate item: fix away-mode alerts that never reach the first mate while the captain is away. While away on 2026-09-30 and again on 2026-10-01, escalations were never delivered to the Claude primary session running on Herdr for about 7 and about 10.5 hours; the supervising process logged every attempt as deferred and raised its stuck alarm.

What Changed

  • Recognize Claude’s titled composer rules so idle resumed or backgrounded sessions can receive away-mode escalations.
  • Require positive harness-process proof before Herdr injection, preserving buffered alerts when the pane is a shell or its process state is unconfirmed.
  • Add regression coverage for titled composers and shell-glyph ambiguity, update the Herdr E2E fixture, and document the injection guards and live reproduction.

Risk Assessment

✅ Low: The change addresses the documented titled-composer failure at the shared classifier while preserving refusal of drafts, dead shells, and unproven blank regions.

Testing

Focused composer and daemon tests passed. Real Claude on isolated Herdr labs proved delivery and six safety scenarios after setup retries. Terminal evidence was saved, all labs were torn down, and the worktree is clean. Claude's later processing was blocked by unavailable login; visual evidence renders captured terminal output rather than a native screenshot.

  • Live validation: ✅ go - 6 of 7 scenarios driven live against the product
Scenario Result Live Evidence
While away, a queued escalation reaches Claude's titled composer exactly once ✅ pass live Submitted alert in real Claude TUI; Delivered operational alert record; r2-baseline.log
A captain's draft blocks injection, preserves the alert and raises the delay alarm; clearing it delivers the alert and clears the alarm ✅ pass live Live observations and setup retries; r2-daemon-pending.log; r2-draft-after.txt
Opening Claude's agents view defers escalation without typing and retains the buffered alert ✅ pass live Live observations and setup retries; r2-agents-view.ansi
Leaving away mode prevents escalation injection ✅ pass live Live observations and setup retries; identical r2-attended-before.txt and r2-attended-after.txt
After Claude exits to a shell using the same prompt glyph, escalation is refused before transport and remains buffered ✅ pass live Exited-shell and closed-pane guards; r2-exit-shell-process.json; identical shell captures
Closing the supervisor pane refuses escalation without attempting transport ✅ pass live Exited-shell and closed-pane guards
Claude processes the received operational alert ⏸️ untested no The real CLI received the alert but reported Login expired. The machine's normal claude auth status also returned loggedIn=false. Disposable configuration cannot supply valid credentials, and this g…
Evidence: Live observations and setup retries

Source: Live observations and setup retries

Real-product observations, Claude 2.1.284 / Herdr 0.9.1

titled composer: target=empty process=agent
PASS: draft preserved, escalation retained, wedge alarm raised without injection
PASS: titled idle received one daemon alert; buffer and wedge cleared
PASS: agents view refusal: deferred: supervisor composer not confirmed-empty (state=pending: pending input, dead-shell prompt, or unreadable pane); submit-attempt=0
PASS: attended refusal: deferred: afk inactive; submit-attempt=0

exit submit verdict=empty
after Claude exit: process=shell composer=empty
[2026-10-02T07:01:04-0500] inject deferred: supervisor harness not confirmed-live (process=shell)
PASS: shell refusal: deferred: supervisor harness not confirmed-live (process=shell); submit-attempt=0
PASS: closed pane refused: supervisor target fm-lab-afk-r2-86585-14711:w2:p1 not found on herdr; submit-attempt=0
COMPLETE

Setup retries: provision performs prepare internally. The standalone setup shell stopped its background processes at exit, so the checks ran with lab lifetime owned by one shell. Disposable onboarding was configured locally. Direct /exit while still in the agents menu did not exit Claude; a fresh primary using the real adapter slash-settle path exited successfully. No product failure remained.
Normal claude auth status: loggedIn=false. Alert entered Claude, which reported Login expired; subsequent operational handling remains untested.
All named labs were torn down by the guarded helper with its default-session tripwire intact.
Evidence: Rendered terminal captures

Source: Rendered terminal captures

<!doctype html><html><meta charset="utf-8"><title>Live Herdr alert validation</title><style>body{margin:24px;background:#121212;color:#ddd;font:15px system-ui}h1{font-size:24px}p{max-width:90ch;color:#bbb}section{margin:28px 0}h2{font-size:18px}pre{padding:20px;background:#191919;border:1px solid #444;overflow:auto;font:13px/1.4 ui-monospace,Menlo,monospace;white-space:pre}</style><h1>Live Herdr alert validation</h1><p>Claude Code 2.1.284 on Herdr 0.9.1, in disposable named lab sessions. These panels render the real captured terminal viewports as text. They are not native screenshots. Alert submission was confirmed; subsequent model processing was blocked by the machine's unavailable Claude login.</p><section><h2>Titled Claude composer</h2><pre>bash &#x27;~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-va
lidation-r2/start-claude.sh&#x27;
01M3Y5SF2JZ46B611HN6SRDQFX on  HEAD [?]
❯ bash &#x27;~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-
validation-r2/start-claude.sh&#x27;
 ▐▛███▛█   Claude Code v2.1.284
▝▜██████▀  Opus 5.5 · API Usage Billing
 ▝▝   ▝▝   ~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX

⚠ Safe mode: all customizations are disabled (CLAUDE.md, skills, plugins, hooks, MCP, agents,
  and more)
  Restart without --safe-mode to re-enable

─────────────────────────────────────────────────────────────── Firstmate operational input ─
❯
─────────────────────────────────────────────────────────────────────────────────────────────
  ⏵⏵ auto mode on (shift+tab to cycle) · ← for agents            Not logged in · Run /login
                                                                         ◐ medium · /effort
</pre></section><section><h2>Captain draft retained during repeated escalation deferral</h2><pre>bash &#x27;~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-va
lidation-r2/start-claude.sh&#x27;
01M3Y5SF2JZ46B611HN6SRDQFX on  HEAD [?]
❯ bash &#x27;~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-
validation-r2/start-claude.sh&#x27;
 ▐▛███▛█   Claude Code v2.1.284
▝▜██████▀  Opus 5.5 · API Usage Billing
 ▝▝   ▝▝   ~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX

⚠ Safe mode: all customizations are disabled (CLAUDE.md, skills, plugins, hooks, MCP, agents,
  and more)
  Restart without --safe-mode to re-enable

⏺ Auto mode lets Claude handle permission prompts automatically — Claude checks each
  tool call for risky actions and prompt injection before executing. Actions Claude
  identifies as safe are executed, while actions Claude identifies as risky are
  blocked and Claude may try a different approach. Ideal for long-running tasks.
  Sessions are slightly more expensive. Claude can make mistakes that allow harmful
  commands to run, it&#x27;s recommended to only use in isolated environments. Shift+Tab
  to change mode.

─────────────────────────────────────────────────────────────── Firstmate operational input ─
❯ CAPTAIN_DRAFT_DO_NOT_SUBMIT
─────────────────────────────────────────────────────────────────────────────────────────────
  ⏵⏵ auto mode on (shift+tab to cycle)
                                                                 Not logged in · Run /login
                                     Update available! Run: brew upgrade claude-code@latest
</pre></section><section><h2>Daemon alert submitted to Claude</h2><pre>bash &#x27;~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-va
lidation-r2/start-claude.sh&#x27;
01M3Y5SF2JZ46B611HN6SRDQFX on  HEAD [?]
❯ bash &#x27;~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-
validation-r2/start-claude.sh&#x27;
 ▐▛███▛█   Claude Code v2.1.284
▝▜██████▀  Opus 5.5 · API Usage Billing
 ▝▝   ▝▝   ~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX

⚠ Safe mode: all customizations are disabled (CLAUDE.md, skills, plugins, hooks, MCP, agents,
  and more)
  Restart without --safe-mode to re-enable

⏺ Auto mode lets Claude handle permission prompts automatically — Claude checks each
  tool call for risky actions and prompt injection before executing. Actions Claude
  identifies as safe are executed, while actions Claude identifies as risky are
  blocked and Claude may try a different approach. Ideal for long-running tasks.
  Sessions are slightly more expensive. Claude can make mistakes that allow harmful
  commands to run, it&#x27;s recommended to only use in isolated environments. Shift+Tab
  to change mode.

❯ : Firstmate operational input waiting: read
  &#x27;~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-vali
  dation-r2/home.9oE9OK/state/operational-inbox/1790942381-dd08c402c7ee040c.msg&#x27; and handle
  its contents as Firstmate operational input.

⏺ Login expired · Please run /login

✻ Cooked for 0s · done 6:59 AM

─────────────────────────────────────────────────────────────── Firstmate operational input ─
❯
─────────────────────────────────────────────────────────────────────────────────────────────
  ⏵⏵ auto mode on (shift+tab to cycle) · ← for agents
                                                                 Not logged in · Run /login
                                     Update available! Run: brew upgrade claude-code@latest
</pre></section><section><h2>Agents view rejects alert injection</h2><pre>
 ▐▛███▛█   Claude Code v2.1.284
▝▜██████▀  Opus 5.5 · ~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX
 ▝▝   ▝▝   1 awaiting input · 0 working · 0 completed

Your conversation moved to the background — enter opens it · esc returns to it · ctrl+c twice
quits

Needs input
 Sessions that have a question or need your decision land here
✻ Firstmate operational input  Login expired · Please run /login                           1s

Working
 Sessions Claude is actively working on — they keep running even if you close the terminal

Completed
 Finished sessions wait here for you to review














 A different way to work with Claude: hand off a bigger task than you would chat through, and
 Claude organizes it in the sections above so you know when it needs you.

─────────────────────────────────────────────────────────────────────────────────────────────
❯ describe a task for a new session
─────────────────────────────────────────────────────────────────────────────────────────────
  ⏵⏵ auto mode · enter to return · space to reply · ctrl+x to delete · ? for shortcuts
Update available! Run: brew upgrade claude-code@latest
</pre></section><section><h2>Exited Claude leaves an empty shell prompt; process guard refuses</h2><pre>⚠ Safe mode: all customizations are disabled (CLAUDE.md, skills, plugins, hooks, MCP, agents,
  and more)
  Restart without --safe-mode to re-enable

⏺ Auto mode lets Claude handle permission prompts automatically — Claude checks each
  tool call for risky actions and prompt injection before executing. Actions Claude
  identifies as safe are executed, while actions Claude identifies as risky are
  blocked and Claude may try a different approach. Ideal for long-running tasks.
  Sessions are slightly more expensive. Claude can make mistakes that allow harmful
  commands to run, it&#x27;s recommended to only use in isolated environments. Shift+Tab
  to change mode.

─────────────────────────────────────────────────────────────── Firstmate operational input ─
❯ /exit
─────────────────────────────────────────────────────────────────────────────────────────────
  /exit                         Exit the CLI
  /context                      Visualize current context usage as a colored grid
  /doctor                       Health-check the user&#x27;s Claude Code setup and fix issues:
                                diagnose installation health — what the `claude doctor` te…
  /verify                       Verify that a code change actually does what it&#x27;s supposed
                                to by exercising it end-to-end and observing behavior — dr…
  /memory                       Edit CLAUDE.md files and memory settings
  /autocompact                  Set how full the context gets before auto-summarizing
  /subtask                      Send a subagent off with your full context; its result
                                comes back here
  /clear                        Start a new session with empty context; previous session
                                stays on disk (resumable with /resume)
  /compact                      Free up context by summarizing the conversation so far







Resume this session with:
claude --resume &quot;Firstmate operational input&quot;
01M3Y5SF2JZ46B611HN6SRDQFX on  HEAD [?] 3s
❯
</pre></section><h2>Observed injection guards</h2><pre>exit submit verdict=empty
after Claude exit: process=shell composer=empty
[2026-10-02T07:01:04-0500] inject deferred: supervisor harness not confirmed-live (process=shell)
PASS: shell refusal: deferred: supervisor harness not confirmed-live (process=shell); submit-attempt=0
PASS: closed pane refused: supervisor target fm-lab-afk-r2-86585-14711:w2:p1 not found on herdr; submit-attempt=0
COMPLETE
</pre></html>
![Screenshot of rendered captures, not a native TUI screenshot](https://github.com/user-attachments/assets/317b4b6e-6236-4c19-88f5-616615f11b6c) - Evidence: [Screenshot of rendered captures, not a native TUI screenshot](https://github.com/rega10/firstmate/blob/f1e413b3400bc91c945c08f457f49d52f6c6411f/.no-mistakes/evidence/fm/firstmate-afk-claude-inject-wedge/r2-terminal-render.png)
Evidence: Submitted alert in real Claude TUI

Source: Submitted alert in real Claude TUI

bash '~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-va
lidation-r2/start-claude.sh'
^[[0m^[[1m^[[38;5;6m01M3Y5SF2JZ46B611HN6SRDQFX^[[0m on ^[[0m^[[1m^[[38;5;5m HEAD^[[0m ^[[0m^[[1m^[[38;5;1m[?]^[[0m 
^[[0m^[[38;5;5m❯^[[0m ^[[0m^[[38;5;2mbash^[[0m ^[[0m^[[38;5;3m'~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-^[[0m
^[[0m^[[38;5;3mvalidation-r2/start-claude.sh'^[[0m
 ▐▛███▛█   Claude Code v2.1.284
▝▜██████▀  Opus 5.5 · API Usage Billing
 ▝▝   ▝▝   ~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX

⚠ Safe mode: all customizations are disabled (CLAUDE.md, skills, plugins, hooks, MCP, agents,
  and more)
  Restart without --safe-mode to re-enable

⏺ Auto mode lets Claude handle permission prompts automatically — Claude checks each
  tool call for risky actions and prompt injection before executing. Actions Claude
  identifies as safe are executed, while actions Claude identifies as risky are
  blocked and Claude may try a different approach. Ideal for long-running tasks.
  Sessions are slightly more expensive. Claude can make mistakes that allow harmful
  commands to run, it's recommended to only use in isolated environments. Shift+Tab
  to change mode.

❯ : Firstmate operational input waiting: read
  '~/.no-mistakes/worktrees/422718388d14/01M3Y5SF2JZ46B611HN6SRDQFX/.live-vali
  dation-r2/home.9oE9OK/state/operational-inbox/1790942381-dd08c402c7ee040c.msg' and handle
  its contents as Firstmate operational input.

⏺ Login expired · Please run /login

✻ Cooked for 0s · done 6:59 AM

─────────────────────────────────────────────────────────────── Firstmate operational input ─
❯ ^[[0m^[[7m ^[[0m
─────────────────────────────────────────────────────────────────────────────────────────────
  ⏵⏵ auto mode on (shift+tab to cycle) · ← for agents
                                                                 Not logged in · Run /login
                                     Update available! Run: brew upgrade claude-code@latest
Evidence: Exited-shell and closed-pane guards

Source: Exited-shell and closed-pane guards

exit submit verdict=empty
after Claude exit: process=shell composer=empty
[2026-10-02T07:01:04-0500] inject deferred: supervisor harness not confirmed-live (process=shell)
PASS: shell refusal: deferred: supervisor harness not confirmed-live (process=shell); submit-attempt=0
PASS: closed pane refused: supervisor target fm-lab-afk-r2-86585-14711:w2:p1 not found on herdr; submit-attempt=0
COMPLETE
Evidence: Delivered operational alert record

Source: Delivered operational alert record

⁣FIRSTMATE_OP: v1 away-supervisor: Supervisor escalate (1 event(s)): lab-event.status: needs-decision: R2_LIVE_AFK_DELIVERY choose proceed or wait (pre-read; re-arm not needed — watcher daemon-managed)
Evidence: Normal Claude authentication status

Source: Normal Claude authentication status

{
  "loggedIn": false,
  "authMethod": "none",
  "apiProvider": "firstParty",
  "analyticsDisabled": false,
  "projectsDirectory": "~/.claude/projects",
  "configDirectory": "~/.claude"
}
- Outcome: 🔧 2 issues found → auto-fixed ✅ across 2 runs (36m44s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

🔧 **Test** - 2 issues found → auto-fixed ✅
  • 🚨 bin/fm-supervise-daemon.sh:1454 - The dead-session guard allows injection into this machine's zsh prompt, which uses ❯. In the isolated lab, process-info confirmed only zsh remained after Claude exited, yet the composer classified empty and inject_msg returned success. The captured shell shows the operational doorbell was submitted as shell input. Both base and target exhibit this pre-existing safety failure. Require positive live-harness verification before typing an escalation.
  • 🚨 live validation verdict: no-go (6 of 7 scenarios were driven live against the product); failed: After Claude exits, the remaining shell refuses escalation injection
  • Live validation: ❌ no-go - 6 of 7 scenarios driven live against the product
Scenario Result Live Evidence
An idle titled Claude session accepts the composer guard instead of deferring indefinitely ✅ pass live baseline-comparison.log; titled-idle-render.png
A queued away-mode decision alert reaches the real Claude input transcript through Herdr ✅ pass live daemon-delivery-render.png; daemon.log
The authenticated Claude primary reads and handles the delivered escalation ⏸️ untested no The real daemon submitted the alert, but Claude responded 'Login expired'. Normal claude auth status reports loggedIn=false. A lab-local configuration using the normal credential-store selection also…
A captain's typed draft blocks escalation injection without altering the draft or losing the buffered alert ✅ pass live draft-protection.log; draft-after-refusal.txt
Claude's agents screen blocks escalation injection and retains the alert ✅ pass live agents-protection.log; agents-view.ansi
After Claude exits, the remaining shell refuses escalation injection ❌ fail live dead-shell-process.json; dead-shell-guard.log; dead-shell-render.png
A closed, unreadable supervisor pane refuses escalation injection ✅ pass live closed-pane-guard.log
  • bash tests/fm-composer-lib.test.sh
  • herdr --version, claude --version, and claude auth status
  • bin/fm-lab-home.sh create with a worktree-local disposable home
  • bin/fm-herdr-lab.sh provision/viewer start/run on named session fm-lab-afk-gate-3607, using a drained 40x120 PTY
  • Executed base and target composer guards against the same real Claude titled viewport
  • Drove typed-draft and agents-view refusals through escalate_flush
  • Ran the real watcher and away daemon against a disposable decision event and captured its delivery to Claude
  • Confirmed a zsh-only target using Herdr process-info, then exercised the dead-shell and closed-pane injection guards
  • Rendered captured ANSI viewports and saved reviewer-visible screenshots
  • Stopped the scoped Claude background session and browser, completed guarded Herdr cleanup with the default-session tripwire passing, removed temporary files, and confirmed a clean worktree

🔧 Fix applied.
✅ Re-checked - no issues remain.

  • Live validation: ✅ go - 6 of 7 scenarios driven live against the product
Scenario Result Live Evidence
While away, a queued escalation reaches Claude's titled composer exactly once ✅ pass live Submitted alert in real Claude TUI; Delivered operational alert record; r2-baseline.log
A captain's draft blocks injection, preserves the alert and raises the delay alarm; clearing it delivers the alert and clears the alarm ✅ pass live Live observations and setup retries; r2-daemon-pending.log; r2-draft-after.txt
Opening Claude's agents view defers escalation without typing and retains the buffered alert ✅ pass live Live observations and setup retries; r2-agents-view.ansi
Leaving away mode prevents escalation injection ✅ pass live Live observations and setup retries; identical r2-attended-before.txt and r2-attended-after.txt
After Claude exits to a shell using the same prompt glyph, escalation is refused before transport and remains buffered ✅ pass live Exited-shell and closed-pane guards; r2-exit-shell-process.json; identical shell captures
Closing the supervisor pane refuses escalation without attempting transport ✅ pass live Exited-shell and closed-pane guards
Claude processes the received operational alert ⏸️ untested no The real CLI received the alert but reported Login expired. The machine's normal claude auth status also returned loggedIn=false. Disposable configuration cannot supply valid credentials, and this g…
  • TMPDIR="$PWD/.live-validation-r2/tmp" bash tests/fm-composer-lib.test.sh
  • TMPDIR="$PWD/.live-validation-r2/tmp" bash tests/fm-daemon.test.sh
  • claude --version, herdr --version, and claude auth status
  • bash .live-validation-r2/run-live.sh: real Claude TUI, daemon delivery, draft retention, delay alarm, agents-view and attended guards
  • bash .live-validation-r2/run-exit.sh: real adapter /exit submission, shell process proof, retained escalation and closed-pane refusal
  • Compared base and target composer classifiers against the live titled viewport
  • Captured ANSI viewports, rendered HTML and a browser screenshot; inspected the screenshot
  • Guarded Herdr teardown, browser shutdown, disposable-file removal and final clean git status --short
✅ **Document** - passed

✅ No issues found.

🔧 **Lint** - 1 issue found → auto-fixed ✅
  • ⚠️ linter found issues (exit code 1)

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Push** - passed

✅ No issues found.

Rene Garza Jr. added 7 commits October 2, 2026 06:23
…way escalations deliver

Claude Code writes a session title into the composer's top rule once the
session has one (a resumed or backgrounded primary). The rule stopped being a
solid separator, no pair formed, and an idle composer classified unknown on
every cursorless backend, so the away daemon deferred every escalation.

A titled rule now opens a pair only when an agent-glyph row sits between it
and the next solid rule; a typed draft, a dead shell prompt, and a blank or
unreadable region still defer.
…ontributions test’s wall-clock race, and the Herdr mock’s indirect-use lint annotation. Targeted lint, contributions, daemon, and available calm-mode checks passed. Interactive Pi E2E verification remains blocked because tmux is absent from PATH
…public expansion state instead of a transient startup hint. Available Calm tests, targeted lint, and diff checks passed. Interactive E2E verification remains blocked because tmux is absent from PATH
…emote trace-context fixture commit to prevent repacking from racing both seed clones. The full regression, clone integrity checks, targeted lint, and diff checks passed
@rega10
rega10 merged commit b98714a into main Oct 2, 2026
19 checks passed
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