Skip to content

AgentTUI: separate pane reachability from Codex turn activity and expose actionable delivery outcomes #13

Description

@Yangtze-Seventh

Context

Follow-up to #12.

The queue-vs-submit rule adopted there is directionally correct, but downstream dogfood exposed a second state-model bug: recent transcript activity is a reachability/liveness signal, not proof that a Codex turn is currently running.

A target can have a completed turn while its transcript is still inside the freshness window. If the adapter treats that effective active value as current turn activity, it sends Tab to an idle Codex composer. The envelope is written into the input box but is not submitted to the transcript.

The inverse failure also occurs: for a genuinely busy turn, a successful Tab queue may remain absent from the transcript until the turn finishes. An early grep miss is therefore not non-delivery evidence. Retrying at that point can produce duplicate decisions when the original queue later lands.

Root cause

Two independent axes were collapsed into one state:

  1. Reachability/liveness — registry declaration plus transcript freshness; answers whether to use a live pane or stopped-session resume.
  2. Current submit activity — whether the Codex TUI is currently executing a turn; answers whether to send Tab or Enter.

A second collapse happened in the result model: one queued-unverified value covered actions with opposite caller behavior (wait, inspect, recover an existing composer, or safely retry).

Proposed contract

1. Separate reachability from submit activity

For a reachable Codex pane, derive submit activity from the latest complete Codex turn-boundary event:

  • latest task_started → active → send Tab once;
  • latest task_complete → idle → send Enter;
  • no trustworthy boundary → do not guess.

A non-empty, unterminated JSONL tail must be treated as a concurrent-write/unknown state. Do not skip it and reuse an older boundary.

Refresh submit activity after any pane settle delay, immediately before the key. If an unverified Enter would be retried, refresh again and retry only while still idle.

2. Report outcomes by observed action

Do not use names such as will-land or stranded, which predict a future result without evidence. Return an explicit caller action instead:

delivery Meaning Caller action
pre-injection-rejected turn state unknown; zero pane commands retry after state becomes readable (retry_safe=true)
delivered this envelope nonce appears after the pre-send transcript boundary await semantic peer ACK
queued-for-next-turn active Codex Tab command executed; nonce not yet present wait for turn boundary; do not resend
submit-unverified Enter executed; nonce not observed inspect target or await ACK; do not blindly resend
composer-unsubmitted pane text written; refreshed state unknown; no key sent recover the existing composer; do not rewrite envelope
write-unverified pane write command returned non-zero or timed out inspect target; do not resend
submit-command-unverified submit-key command returned non-zero or timed out inspect target or await ACK; do not blindly resend

Every structured pane result should include:

  • submit_action
  • recommended_action
  • retry_safe
  • verification_guidance
  • the unique envelope nonce
  • message-specific evidence (envelope-nonce-found or none)
  • semantic acknowledged=false until a peer reply

Only a mechanically proven zero-pane-command path may set retry_safe=true.

For queued-for-next-turn, explicitly state that an early transcript miss is not non-delivery evidence. A caller must not use one early grep miss to justify reinjection.

3. Define fail-closed behavior by phase

fail closed is ambiguous unless the transport side effect is stated:

  • unknown before pane write → zero pane commands, structured pre-injection-rejected, non-zero exit;
  • unknown after pane write → no submit key, structured composer-unsubmitted, non-zero exit;
  • target becomes active/unknown after an Enter attempt → no second Enter, submit-unverified;
  • pane command non-zero/timeout → preserve nonce and attempted stage, structured uncertain result, non-zero exit, explicit do-not-resend action.

Dogfood evidence

The downstream adopter reproduced both directions:

  1. A recently completed target was still derived as reachable/active by freshness. Tab left the envelope in the idle composer. One Enter submitted the existing envelope and immediately unblocked the waiting workflow.
  2. A truly busy target had no nonce in an early transcript check, but the original Tab queue appeared after the turn ended. A premature reinjection produced duplicate content.

The adopter implementation now has 34 focused AgentTUI tests and passed an independent challenge review covering:

  • effective active + latest task_complete → Enter;
  • latest task_started → Tab;
  • active→idle transition during settle;
  • partial JSONL tail → unknown;
  • no second Enter after active/unknown transition;
  • nonce-only delivery evidence;
  • every outcome and caller action above;
  • pane write/submit non-zero and timeout paths.

Acceptance criteria

  • Core schema/guidance distinguishes reachability from current submit activity.
  • Reference adapter derives Codex activity from complete turn-boundary events, not transcript freshness.
  • Submit activity is refreshed immediately before the key and before any Enter retry.
  • Unknown pre-write and post-write phases have distinct structured behavior.
  • Delivery outcomes expose explicit caller actions and retry safety.
  • Early transcript miss is documented and tested as insufficient non-delivery evidence.
  • Non-zero/timeout pane commands still return nonce-bearing structured outcomes and prohibit blind resend.
  • Tests cover the full routing and outcome matrix for Codex and preserve Claude Code's Enter/receiver-native queue behavior.
  • Downstream templates/generators propagate the corrected contract without product-specific paths or assumptions.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions