State Reference Guide¶
This guide catalogs every public state value exposed by the houmao-server TUI terminal tracking system. It is designed as a standalone reference: each value entry provides an intuitive meaning, its technical derivation, and the operational implications for consumers polling GET /houmao/terminals/{terminal_id}/state.
Source of truth: The canonical type definitions live in
src/houmao/shared_tui_tracking/models.py. The authoritative mapping functions live insrc/houmao/shared_tui_tracking/public_state.py. The server's Pydantic response models insrc/houmao/server/models.pyre-export these types for the HTTP API surface. This guide is a human-readable companion, not a replacement for those modules.
Architecture Note¶
The public state contract is now centered on one shared tracker engine plus host adapters:
TuiTrackerSessioninsrc/houmao/shared_tui_tracking/session.pyowns raw-snapshot signal detection, internal Rx settle timing, and tracker-ownedsurface/turn/last_turnreduction.LiveSessionTrackerinsrc/houmao/server/tui/tracking.pyis the live server host adapter. It feeds raw captured TUI text and explicit input events into the shared session, then merges the resulting tracker state with server-owned diagnostics, lifecycle timing, authority, and visible-stability metadata.StreamStateReducerinsrc/houmao/shared_tui_tracking/reducer.pyis now a compatibility wrapper for replay and offline consumers; it no longer owns the core state machine.
ManagedAgentTurnPhase alias: The server models import TurnPhase from shared_tui_tracking.models as ManagedAgentTurnPhase. This means TUI terminals and managed headless agents share the same turn vocabulary — ready, active, and unknown carry identical semantics regardless of the transport path.
Detector Families¶
Surface observables (accepting_input, editing_input, ready_posture) and signal evidence (active_evidence, interrupted, known_failure, success_candidate) are produced by tool-specific tracked-TUI profiles. src/houmao/shared_tui_tracking/detectors.py now defines the shared contract and compatibility exports, while concrete implementations live under app-owned modules such as src/houmao/shared_tui_tracking/apps/codex_tui/.
Three detector families exist:
| Family | Class | Tool | Selection |
|---|---|---|---|
claude_code (version 2.1.x) |
ClaudeCodeSignalDetectorV2_1_X |
Claude Code | Best-match by observed CLI version; highest score for 2.1.*, fallback for other 2.* |
codex_tui |
CodexTuiSignalDetector |
Codex interactive TUI | Selected when tool == "codex" for tracked raw-snapshot TUI sessions |
unsupported_tool |
FallbackTrackedTurnSignalDetector |
All others | Default fallback for unrecognized tools |
The selection entry point remains select_tracked_turn_signal_detector(tool=..., observed_version=...), but it now resolves through the tracker-local app/profile registry with closest-compatible semver-floor matching rather than ad hoc scoring. For codex_tui, the resolved profile may also contribute temporal hints from a recent sliding window while keeping the single-snapshot DetectedTurnSignals contract intact.
The shipped Codex tracker contract is currently one codex_tui v1 profile. Profile-specific frame details such as latest-turn-region signatures remain private to that profile rather than widening the shared normalized signal contract.
For Codex, current activity is intentionally derived from the live edge of the visible surface rather than from arbitrary full scrollback. Prompt-scoped latest-turn reasoning still drives interruption matching, success context, and temporal transcript-growth hints.
Why this matters: Different tools yield different unknown vs yes/no distributions for the same underlying conditions. The unsupported_tool fallback is intentionally conservative — it produces more unknown values because it lacks tool-specific prompt and activity patterns. A ready_posture=unknown from an unsupported tool does not mean the same thing as ready_posture=unknown from the Claude detector.
diagnostics.availability¶
Type: TrackedDiagnosticsAvailability — one of available, unavailable, tui_down, error, unknown.
This field answers: "Is the current observation sample usable for normal state interpretation?" Only when availability is available do the surface, turn, and last_turn groups carry meaningful values.
See state-transitions.md § Diagnostics Availability for the full statechart diagram with all transitions.
stateDiagram-v2
[*] --> unknown
unknown --> available : parse success +<br/>surface present
unknown --> unavailable : tmux missing
unknown --> tui_down : tmux up,<br/>TUI process absent
unknown --> error : probe/parse failure
available --> tui_down : TUI process exits
available --> unavailable : tmux disappears
available --> error : probe/parse failure
tui_down --> available : TUI process starts +<br/>parse success
tui_down --> unavailable : tmux disappears
unavailable --> unknown : tmux reappears
error --> available : next probe succeeds
error --> unknown : probe recovers<br/>partially
available¶
Intuitive meaning: The tracked terminal is fully reachable and the parser produced a usable surface. This is the normal operating state.
Derivation: diagnostics_availability() returns available when parse_status == "parsed" and parsed_surface_available is true — meaning tmux is up, the TUI process is running, and the parser successfully interpreted the captured pane text.
Operational implications: All surface, turn, and last_turn values are meaningful. It is safe to poll for turn state and act on the results. Sending input via POST /houmao/terminals/{terminal_id}/input is viable (subject to turn.phase).
unavailable¶
Intuitive meaning: The tracked tmux session does not exist. The terminal may have been destroyed or was never created.
Derivation: Returns unavailable when transport_state == "tmux_missing".
Operational implications: No tracking is possible. Surface and turn values will be unknown/none. Do not send input — there is no terminal to receive it. Wait for the tmux session to appear, or investigate whether the session was terminated.
tui_down¶
Intuitive meaning: The tmux session is reachable, but the supported TUI process (e.g., Claude, Codex) is not running inside the tracked pane.
Derivation: Returns tui_down when process_state == "tui_down" and transport is healthy.
Operational implications: The pane exists but contains stale scrollback, not a live TUI. Surface and turn values reflect the last captured state before the process exited — treat them as stale. Do not send input. Monitor for the TUI process to restart.
error¶
Intuitive meaning: Something went wrong during the current observation cycle — a probe failure, a parse failure, or an unexpected runtime error.
Derivation: Returns error when any of probe_error_present, parse_error_present are true, or when transport_state, process_state, or parse_status indicate a probe or parse error.
Operational implications: The current sample is untrustworthy. Surface and turn values may not reflect reality. This is often transient — a single bad cycle. Wait for the next poll cycle and check if the error persists. If it does, inspect diagnostics.probe_error and diagnostics.parse_error for details.
unknown¶
Intuitive meaning: The server is still watching, but the current sample cannot be confidently classified. This includes situations like unsupported tools where the server has no parser, or partially recovered states.
Derivation: Returns unknown as the default when none of the more specific conditions match — typically when process_state == "unsupported_tool" or parse_status == "unsupported_tool", or when the parse succeeded but no surface was produced.
Operational implications: Do not assume the terminal is ready or broken. Surface and turn values are at best approximate. Wait for a more definitive state before taking action.
surface Observables¶
The surface group contains three foundational observables that describe what is directly visible in the TUI right now. All three are Tristate values: yes, no, or unknown.
These values are profile-produced — they come from tool-specific tracked-TUI profile logic, not from the reducer or mapping helpers. Different profile families may produce different tristate distributions for the same underlying TUI conditions.
surface.accepting_input¶
Whether typed input would currently land in the TUI's prompt area.
yes¶
Intuitive meaning: The TUI has a visible, active prompt area that will accept typed characters.
Derivation: These values are now derived from raw snapshot text by the selected detector profile. For Claude: the detector found a ❯ prompt line. For Codex: the detector found a live › prompt without an approval overlay. Fallback detectors remain conservative and may leave the value as unknown.
Operational implications: Text sent to the tmux pane will reach the prompt. This does not necessarily mean the TUI is ready for submission — it may be in an editing state or showing a menu.
no¶
Intuitive meaning: The TUI's input area is closed or blocked. Typed characters will not reach a prompt.
Derivation: Raw-text detector profiles emit no when the visible surface clearly blocks prompt input, for example approval prompts, trust dialogs, or other modal overlays. If the detector cannot prove that block from raw text alone, it stays conservative and returns unknown.
Operational implications: Do not send input. It will not reach the intended prompt. Wait for the input mode to change.
unknown¶
Intuitive meaning: The detector cannot confidently determine whether input would reach a prompt.
Derivation: No prompt was found by the detector, or no parsed surface is available. Common with unsupported tools.
Operational implications: Treat as uncertain. Do not assume input will be received. Wait for a more definitive reading.
surface.editing_input¶
Whether prompt-area input is actively being edited — i.e., there is user-entered text in the prompt.
yes¶
Intuitive meaning: The prompt area contains user-entered text that has not been submitted.
Derivation: For Claude: a ❯ prompt line exists and its text content is non-empty. For Codex: a › prompt line exists with non-empty text.
Operational implications: A human or automation has typed into the prompt but not yet submitted. Sending additional input may append to or conflict with the existing text.
no¶
Intuitive meaning: The prompt area is visible but empty — ready for new input.
Derivation: For Claude: a ❯ prompt line exists but its text content is empty. For Codex: a › prompt line exists with empty text.
Operational implications: The prompt is clean and ready for input. This is the expected state before sending a new prompt.
unknown¶
Intuitive meaning: The detector cannot determine whether text is being edited.
Derivation: No prompt was found, the visible surface is ambiguous, or the tool/profile is unsupported.
Operational implications: Treat as uncertain.
surface.ready_posture¶
Whether the visible surface looks ready for an immediate submit — the prompt is visible, the TUI is not actively working, and no ambiguous interactive UI is blocking.
yes¶
Intuitive meaning: The TUI appears idle with a clean prompt, ready to accept and process a new submission.
Derivation: Raw-text detector profiles emit yes when a prompt is visible, the surface is not actively working, and no ambiguous overlay is present. Claude and Codex use their own prompt/activity heuristics; fallback profiles stay conservative.
In the live server contract, ready_posture=yes may also appear after host-owned stale-active recovery for an unanchored session that stayed submit-ready through the configured recovery window, or after final stable-active recovery for a stable false-active surface with independent idle/freeform prompt-ready evidence. These recoveries correct stuck active posture; they are not success verdicts.
Operational implications: The terminal is ready to receive and begin processing a new prompt. This is the ideal state for sending input via POST /houmao/terminals/{terminal_id}/input.
no¶
Intuitive meaning: The TUI is actively working or its input is closed.
Derivation: Detector profiles emit no when the raw surface clearly shows active work or a blocked prompt. For Codex this can come from a current working status row, in-flight tool cell, or temporal transcript growth. The Claude profile still prefers unknown over no when the posture is not confidently classifiable.
Operational implications: Do not send new prompts. The TUI is either processing a turn or in a state that cannot accept submissions.
unknown¶
Intuitive meaning: The detector cannot confidently determine whether the TUI is ready for submission.
Derivation: Common causes: no prompt is visible, the surface is blocked by ambiguous interactive UI (menus, selection boxes, permission prompts), or the tool is unsupported.
Operational implications: Do not assume readiness. Wait for ready_posture=yes before sending input.
turn.phase¶
Type: TurnPhase (aliased as ManagedAgentTurnPhase in server models) — one of ready, active, unknown.
This field answers: "What is the current turn posture?" It is intentionally narrower than the internal reducer graph — ambiguous states collapse into unknown rather than exposing implementation-specific phases.
See state-transitions.md § Turn Phase for the full statechart diagram with all transitions.
stateDiagram-v2
[*] --> unknown
unknown --> ready : surface ready posture
unknown --> active : active evidence /<br/>anchor armed
ready --> active : anchor armed /<br/>active evidence
active --> ready : completion settled /<br/>interrupted / failure
active --> unknown : diagnostics degraded
ready --> unknown : diagnostics degraded /<br/>ambiguous surface
ready¶
Intuitive meaning: The TUI appears ready for another turn — no active work is in progress, and the surface looks ready for submission.
Derivation: The standalone tracker session returns ready when the current raw surface is classified as ready and there is no stronger active or ambiguous evidence. The live server can also publish ready after stale-active recovery when an unanchored session has remained submit-ready for the configured recovery window without stronger live evidence, or after the final stable-active recovery window when raw TUI evidence and published state are unchanged while parser evidence remains idle/freeform and input-ready.
Operational implications: It is safe to send a new prompt via POST /houmao/terminals/{terminal_id}/input. The terminal is idle and waiting for the next instruction.
active¶
Intuitive meaning: A turn is currently in flight — the agent is working on a prompt.
Derivation: The standalone tracker session returns active when explicit input has armed tracker authority or when the detector reports active evidence. For Codex that evidence can include a current • Working (... esc to interrupt) status row, an in-flight tool cell, or temporal transcript growth even when no spinner is visible.
Operational implications: Do not send new input — the agent is processing the current prompt. Poll for turn completion. You may observe last_turn.result changing to success, interrupted, or known_failure when the turn ends.
unknown¶
Intuitive meaning: The system cannot confidently classify the posture as ready or active.
Derivation: Returns unknown when: (1) diagnostics availability is error, tui_down, or unavailable; (2) the detector reports ambiguous_interactive_surface (menus, permission dialogs, slash commands); or (3) neither ready nor active evidence is present.
Operational implications: Do not assume the terminal is ready. Wait for the state to resolve to ready or active. If it persists, check diagnostics.availability for underlying issues.
last_turn.result¶
Type: TrackedLastTurnResult — one of success, interrupted, known_failure, none.
This field answers: "What was the outcome of the most recent completed turn?" It is sticky — it only updates when a tracked turn reaches a terminal outcome, not on every poll cycle.
See state-transitions.md § Last-Turn Result for the full statechart diagram with all transitions.
stateDiagram-v2
[*] --> none
none --> success : settle timer fires
none --> interrupted : interruption signal
none --> known_failure : failure signal
success --> interrupted : new turn interrupted
success --> known_failure : new turn failed
success --> success : new turn succeeds
success --> none : premature success<br/>retracted
interrupted --> success : new turn succeeds
interrupted --> interrupted : new turn interrupted
interrupted --> known_failure : new turn failed
known_failure --> success : new turn succeeds
known_failure --> interrupted : new turn interrupted
known_failure --> known_failure : new turn failed
success¶
Intuitive meaning: The most recent turn completed successfully — the agent produced an answer and the surface returned to a ready posture.
Derivation: The reducer arms a success timer when the detector reports success_candidate. If the surface remains stable through the settle window (no further changes, no errors, no interruptions), the timer fires and promotes the result to success. The signature of the success surface is recorded so a later change to the same surface can retract a premature success.
Operational implications: The turn is done. Check stability.stable_for_seconds to confirm the result is settled. You can send a new prompt. Note: a premature success may be retracted if the surface proves to still be evolving within the same anchor — always verify stability before acting.
interrupted¶
Intuitive meaning: The most recent turn was interrupted — the user or system halted the agent's work before completion.
Derivation: The detector saw a tool-specific interruption signature (e.g., Claude's ⎿ Interrupted · What should Claude do instead? pattern followed by a prompt, or Codex's interruption text). The reducer immediately promotes to interrupted and cancels any pending success timer.
Operational implications: The agent stopped before completing. The prompt is typically ready for a new instruction. Previous partial work may be visible in the TUI output.
known_failure¶
Intuitive meaning: The most recent turn ended with a recognized failure — the agent encountered a supported error pattern.
Derivation: The detector saw a tool-specific failure signature (e.g., Claude's colored error status line followed by a prompt). The reducer immediately promotes to known_failure and cancels any pending success timer.
Operational implications: The turn failed. Inspect the TUI output for error details. The prompt is typically ready for a retry or a different instruction.
none¶
Intuitive meaning: No terminal turn result has been recorded yet — the tracker is in its initial state or a premature success was retracted.
Derivation: This is the initial value. It also reappears if a previously recorded success is invalidated because the surface changed after the settle window (the success was premature).
Operational implications: No completed turn to act on. Wait for a turn to complete.
last_turn.source¶
Type: TrackedLastTurnSource — one of explicit_input, surface_inference, none.
This field answers: "How was the most recent completed turn initiated?"
explicit_input¶
Intuitive meaning: The turn was initiated through the server API — a consumer sent input via POST /houmao/terminals/{terminal_id}/input and the server armed an explicit turn anchor.
Derivation: The standalone tracker returns explicit_input when the most recent terminal outcome is still attributed to an explicit on_input_submitted() event.
Operational implications: The server has full lifecycle authority over this turn. Settle guarantees are tighter because the exact submission timestamp is known.
surface_inference¶
Intuitive meaning: The turn was inferred from direct tmux interaction — someone typed into the TUI directly (bypassing the server API), and the tracker inferred a new turn from surface changes.
Derivation: The standalone tracker returns surface_inference when it had to infer turn ownership from raw-surface activity rather than from an explicit input event.
Operational implications: The server did not initiate this turn, so it has less precise timing. Settle times may be slightly longer because the exact submission moment is estimated from surface changes rather than known precisely.
none¶
Intuitive meaning: No turn source has been recorded — the tracker is in its initial state.
Derivation: This is the initial value, paired with last_turn.result=none.
Operational implications: No completed turn to attribute. Wait for a turn to complete.