Skip to content

Adopt structured session-state hook events (permission_prompt, idle_prompt, quota) ahead of pane heuristics #745

Description

@pbean

Problem

Everything the orchestrator knows about a live session between launch and Stop is inferred through a keyhole of mechanical observables:

  • pane-log (mtime_ns, size) via BaseGenericAdapter._log_activity_key (stall detection),
  • pane captures regex-scanned by EnvFaultMixin (_env_fault_patterns / _env_fault_evidence),
  • window death, and
  • the four canonical hook events — 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:

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

  1. 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.
  2. Consume permission_prompt in BaseGenericAdapter.wait_for_completion. While a waiting-on-input event is the freshest signal: suppress STALL_NUDGE_TEXT, never verdict crashed, 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 as crashed, and retry burns 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.
  3. Consume quota notifications as env-fault evidence. 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.
  4. Zero-token session timeout (Classify a zero-token session timeout as an environment fault (#323 suggestion 4) #611) stays the adapter-agnostic, vocabulary-free floor for CLIs with no such hooks — the tally infrastructure (tokens.read_usage, generic._sample_weighted_usage) already reads the transcripts.
  5. Docs: a signal-precedence table (structured event > transcript-derived > pane heuristic) in the adapter authoring guide / FEATURES.md.

Constraints / adjacents

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Real defect - workaround exists or blast radius is narrowarea:adaptersCoding-CLI adapters and profilesarea:engineOrchestrator engine and run lifecycleenhancementNew feature or requestneeds-designAwaiting a maintainer design decision before code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions