You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Meanwhile, structured channels for several of these exact states exist upstream and are not consumed. Claude Code's hook surface (verified against https://code.claude.com/docs/en/hooks, 2026-08-28) includes a Notification event with matcher types permission_prompt, idle_prompt, agent_needs_input, and quota_auto_resume_*, plus a dedicated PermissionRequest event. Our claude profile maps only SessionStart, Stop, SessionEnd, PreCompact.
Principle to adopt
When a structured channel exists for a semantic state, it outranks any pane-text heuristic. Pane regexes are the last resort and are documented as lossy — #617's byte-indistinguishability is not fixable by a better regex, only by moving off pane bytes.
Proposed legs
Canonical waiting-on-input event. Extend the canonical event set with a Notification-class event (name TBD; carrying the notification type), mapped in the claude profile's hooks.events; the relay (events.py / bmad_loop_hook.py) writes it to the events dir and SignalWatcher surfaces it. Optional per profile — CLIs whose hook maps end at Stop are unaffected.
SignalWatcher already polls a legacy events dir for older installed relays; the new event must degrade cleanly against an old relay that never emits it (absence of the event is not evidence of absence of the state).
Event availability differs per CLI (gemini/copilot/codex/antigravity have no mapping today); nothing may require the event.
Any new knobs land in src/bmad_loop/data/settings/core.toml (sync test).
Relation
This is the first line of defense for the #727/#739/#680 family: wherever the CLI emits the structured event, the ambiguity disappears deterministically. The residual margin — CLIs with Stop-only hook maps, and states no CLI exposes — is the subject of the act-gated classifier design issue, #746.
Problem
Everything the orchestrator knows about a live session between launch and Stop is inferred through a keyhole of mechanical observables:
(mtime_ns, size)viaBaseGenericAdapter._log_activity_key(stall detection),EnvFaultMixin(_env_fault_patterns/_env_fault_evidence),CANONICAL_EVENTS = {"SessionStart", "Stop", "SessionEnd", "PreCompact"}(adapters/profile.py).Semantic states the keyhole cannot express get inferred lossily, and the open bugs in this family are the margins where that inference fails:
crashedand burnsStoryTask.attempt(A dev session waiting on an interactive permission prompt is recorded ascrashed, andretryburns the last attempt #727);send_textdeliversSTALL_NUDGE_TEXT+ Enter into the very prompt it cannot see (The stall wake-nudge types text and presses Enter into the pane, so a session parked on a permission prompt gets that prompt answered #739);_log_activity_key, so "making progress" is inferred from an observable idle sessions also produce ([BUG] Stall detection cannot see a session idling inside a tool call: pane-log re-arm keeps the grace alive throughsleep#680);Meanwhile, structured channels for several of these exact states exist upstream and are not consumed. Claude Code's hook surface (verified against https://code.claude.com/docs/en/hooks, 2026-08-28) includes a
Notificationevent with matcher typespermission_prompt,idle_prompt,agent_needs_input, andquota_auto_resume_*, plus a dedicatedPermissionRequestevent. Our claude profile maps onlySessionStart, Stop, SessionEnd, PreCompact.Principle to adopt
When a structured channel exists for a semantic state, it outranks any pane-text heuristic. Pane regexes are the last resort and are documented as lossy — #617's byte-indistinguishability is not fixable by a better regex, only by moving off pane bytes.
Proposed legs
Notification-class event (name TBD; carrying the notification type), mapped in the claude profile'shooks.events; the relay (events.py/bmad_loop_hook.py) writes it to the events dir andSignalWatchersurfaces it. Optional per profile — CLIs whose hook maps end at Stop are unaffected.BaseGenericAdapter.wait_for_completion. While a waiting-on-input event is the freshest signal: suppressSTALL_NUDGE_TEXT, never verdictcrashed, and surface the park as a pause/escalation instead of an attempt burn (relates to awaiting-operator: terminal state for stories owing human external actions #335 awaiting-operator). This settles the claude-profile leg of A dev session waiting on an interactive permission prompt is recorded ascrashed, andretryburns the last attempt #727 and The stall wake-nudge types text and presses Enter into the pane, so a session parked on a permission prompt gets that prompt answered #739 with no heuristics at all.quota_auto_resume_*/ usage-limit notification types give Seed a Claude Code usage-limit/quota env-fault pattern once a real line is captured #610 the structured signal it was parked waiting for — no pane regex, no vocabulary collision with story output.tokens.read_usage,generic._sample_weighted_usage) already reads the transcripts.Constraints / adjacents
SignalWatcheralready polls a legacy events dir for older installed relays; the new event must degrade cleanly against an old relay that never emits it (absence of the event is not evidence of absence of the state).src/bmad_loop/data/settings/core.toml(sync test).Relation
This is the first line of defense for the #727/#739/#680 family: wherever the CLI emits the structured event, the ambiguity disappears deterministically. The residual margin — CLIs with Stop-only hook maps, and states no CLI exposes — is the subject of the act-gated classifier design issue, #746.