Skip to content

fix(bin): accept Codex follow-up queue submissions - #15

Open
andrew-kim-techtorch wants to merge 2 commits into
mainfrom
fm/fmsend-codex-ship-r7
Open

fix(bin): accept Codex follow-up queue submissions#15
andrew-kim-techtorch wants to merge 2 commits into
mainfrom
fm/fmsend-codex-ship-r7

Conversation

@andrew-kim-techtorch

@andrew-kim-techtorch andrew-kim-techtorch commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Requirement: Steering a Codex crewmate that is mid tool-call must not produce a false fm-send "text not submitted" error when the steer was actually delivered into Codex's follow-up queue.

What changed

When Codex is busy in a tool call, a steer typed into its composer is accepted into the visible "Messages to be submitted after next tool call" queue, and Codex leaves the queued row sitting at the cursor. The tmux submit verifier read that lingering row as a still-pending composer, retried Enter up to the retry limit, and then returned pending, which fm-send surfaces as a false "Enter swallowed" error even though the message was delivered. This change teaches the verifier a second success signal: fm-send sets an opt-in FM_TMUX_CODEX_QUEUE_VERIFY=1 for meta-confirmed Codex targets only, the verifier snapshots the queue depth before Enter, and a queue that grew after Enter is accepted as the new queued verdict instead of retrying. Every other harness keeps the exact prior composer-cleared contract, so verification is never weakened for non-Codex crews.

flowchart LR
  send["fm-send codex target"] -->|"queue verify opt-in"| core["submit enter core"]
  core -->|"composer cleared"| ok["verdict empty sent"]
  core -->|"queue grew"| queued["verdict queued sent"]
  core -->|"composer pending queue unchanged"| fail["verdict pending error"]
  core -->|"pane unreadable"| unknown["verdict unknown inconclusive"]
Loading

How it works

Given a Codex crewmate fm-fix-login running a tool call, firstmate steers it with bin/fm-send.sh fm-fix-login 'also update the tests'. Codex accepts the text into its follow-up queue, so the composer does not clear. The verifier counts rows under the queue heading before Enter (say 0) and after Enter (now 1); because 1 > 0 it returns queued and fm-send exits 0 with the steer delivered, instead of hammering Enter three more times and reporting a false failure. If instead the composer still held the text and the queue count had not moved, the verifier returns pending and fm-send still reports the genuine swallow as an error, exactly as before.

Evidence

Testing suite:

Suite What it guards Result Command
tests/fm-send-codex-verification.test.sh Codex landed / grown-queue / unchanged-queue-with-pending / pre-existing-queue / non-Codex-unaffected verification paths pass (5/5) bash tests/fm-send-codex-verification.test.sh
tests/*.test.sh (full e2e tmux suite) no regression across the 53 colocated tmux tests pass (no-mistakes test step, .no-mistakes.yaml commands.test)
shellcheck bin/.sh bin/backends/.sh tests/*.sh scripts stay shellcheck-clean (CI-exact invocation) pass (exit 0) shellcheck bin/.sh bin/backends/.sh tests/*.sh

Risks

Low and contained. Queue recognition is gated behind both TARGET_HARNESS=codex (in fm-send.sh) and the explicit FM_TMUX_CODEX_QUEUE_VERIFY=1 opt-in, so no non-Codex path changes behavior. The queue count is derived from a best-effort tmux capture-pane; a capture failure is treated as inconclusive and falls back to the existing composer-cleared logic, never a false success. The stale-queue and genuine-swallow cases are covered by the regression suite above so a real swallow still fails closed.

Links

Firstmate-internal change to bin/fm-send.sh, bin/fm-tmux-lib.sh, and the harness-adapters skill; no related PR or tracking issue.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"
  • Provided successful baseline: `command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"
  • bash tests/fm-send-codex-verification.test.sh
  • bash tests/fm-send-popup-settle.test.sh
  • bash tests/fm-send-settle.test.sh
  • bash tests/fm-daemon.test.sh
  • Ran bin/fm-send.sh fm-task 'fix verification' against a real tmux pane simulating Codex’s visible follow-up queue; verified exit 0, queue growth from 0 to 1, and exactly one Enter.
  • Ran the same fm-send flow against a metadata-confirmed Claude target; verified exit 1 and three Enter attempts, preserving the non-Codex pending-composer contract.
  • Exercised fm_tmux_submit_enter_core target 3 0 0 with pane reads failing; verified verdict unknown remains non-error under fm-send policy.
  • Verified git status --short remained clean at target commit ea1a303a3a71fd93536ed511382ca2c8ee78ae8e.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant