Skip to content

Show waiting-for-input state in factory visualization #290

Description

@williamthorsen

Problem

When an orchestrated run pauses waiting for user input (permission prompts, AskUserQuestion dialogs, wrap-up confirmation), the factory visualization has no way to show this. A user watching the vis just sees stalled activity with no indication the orchestrator is blocked on them.

Proposed solution

Use Claude Code hooks to detect input-waiting states, emit new events to run-log.jsonl, and have the factory vis render the waiting state.

Architecture

Claude Code hooks                    run-log.jsonl                Factory vis
─────────────────                    ─────────────                ──────────
Notification hook  ──> append ──>  waiting_for_input event  ──>  orchestrator shows waiting
UserPromptSubmit   ──> append ──>  input_received event     ──>  orchestrator resumes

Key design decisions

  1. Breadcrumb file (.claude/tmp/active-run-dir): The orchestrate skill writes the runDir path here after init_run. Hook scripts read it to discover which run-log.jsonl to append to. tmp/ is already gitignored. Only one orchestration runs per project directory, so cwd is a natural key.

  2. Two new event types in run-core: waiting_for_input (with reason: permission_prompt | elicitation_dialog | idle_prompt) and input_received. These fold into a new waitingForInput field on CanonicalRunStatus.

  3. Hook scripts (.claude/hooks/): Shell scripts triggered by Claude Code's Notification and UserPromptSubmit hooks. They read the breadcrumb, append JSONL directly (bypassing MCP since hooks run outside MCP context). Atomic single-line appends are safe on POSIX.

  4. Visual treatment: Orchestrator sprite switches to concerned animation at 0.6 opacity (between idle 0.35 and active 1.0) when waiting. Clears when input is received. No new sprite assets needed.

  5. Backward compatible: Old run-log.jsonl files without these events fold with waitingForInput: undefined. The mapper treats undefined as "not waiting."

Considerations

  • Hook scripts must silently no-op when no orchestration is active (breadcrumb guard)
  • The UserPromptSubmit hook fires on every user prompt, but the breadcrumb + idempotent folder make this harmless
  • The exact JSON field name for the notification type in hook input needs verification during implementation (could be matcher, hook_event_name, or similar)
  • JSONL concurrent writes from hooks and MCP are safe under POSIX PIPE_BUF for small lines

Scope

  • run-core: 2 new event types, Zod schemas, event folder, exports
  • factory: mapper, differ, OrchestratorActor, CatwalkScene, types
  • agents: orchestrate skill breadcrumb write/cleanup
  • .claude/: hook scripts + settings.json configuration
  • Tests for schema parsing and event folding

Acceptance criteria

  • waiting_for_input and input_received events parse and validate via Zod schema
  • foldEvents() correctly sets/clears waitingForInput on CanonicalRunStatus
  • Factory mapper derives orchestrator.waiting from waitingForInput status
  • Orchestrator sprite visually changes when waiting (concerned animation, 0.6 opacity)
  • Hook scripts silently exit when no orchestration is active
  • Hook scripts correctly append JSONL to the active run's log file
  • Breadcrumb written after init_run, cleaned up after complete_run
  • pnpm run check passes (typecheck, format, lint, tests)
  • Manual verification: factory vis shows waiting state during permission prompt

Metadata

Metadata

Labels

featureAdded or improved external functionalityscope:factory

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions