feat: add opt-in codex app-server probe#243
Open
e-jung wants to merge 4 commits into
Open
Conversation
4 tasks
a0c5f79 to
6f0bd82
Compare
…-server probe error matching
6f0bd82 to
51ffd76
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Implement a contained Codex app-server / Codex-managed-worktree spike for firstmate. Add the smallest safe, opt-in probe/scaffold that can inspect the installed Codex app-server command/schema and run stdio-only app-server basics without touching real captain projects or exposing unauthenticated non-loopback listeners. Document the evidence, GUI visibility uncertainty, metadata needed for codex_thread_id/active turn/worktree/archive/read semantics, Codex-managed worktree lifecycle differences, and recommend when a future backend=codex-app should start ship/scout-only before secondmates. Do not register codex-app as a spawn-capable backend or destabilize Herdr/tmux.
What Changed
bin/fm-codex-appserver-probe.sh, an opt-in, stdio-only probe that inspects the installed Codex app-server command/schema and exercises basic create/read/archive-thread lifecycle without registeringcodex-appas a spawn-capable backend or destabilizing the existing tmux/herdr/zellij/orca backends.docs/codex-app-backend-spike.mdrecording the spike evidence and recommendations: metadata gaps for thread/turn/worktree/archive semantics, Codex-managed worktree lifecycle differences, and guidance that a futurebackend=codex-appshould start ship/scout-only before secondmates.tests/fm-codex-appserver-probe.test.shcovering dry-run, schema lifecycle, and live stdio paths, and fixes node-on-PATH flakiness intests/fm-session-start.test.sh.Risk Assessment
✅ Low: The change is a well-bounded, opt-in, standalone experimental probe with safe dry-run defaults, stdio-only live mode, explicit refusal of unauthenticated non-loopback listeners, matching tests, and no modification to any existing firstmate spawn/backend machinery; round-1 issues are fixed and no new material issues remain.
Testing
Ran the two affected test files (fm-codex-appserver-probe, fm-session-start) and the full safety/non-registration matrix end-to-end against a fake codex CLI: default dry-run, help, non-loopback WS refusal, loopback + stdio-only gating, unsupported-listen refusal, schema generation/summary incl. failure exit-code propagation and missing-method detection, live stdio create/read/archive incl. keep-thread and graceful no-rollout archive degradation, and the
fm-spawn.sh --backend codex-apprejection proving codex-app is not registered while tmux/herdr/zellij/orca remain. All targeted tests pass; working tree left clean; no UI surface exists for this CLI-only spike so the captured CLI transcripts are the reviewer-facing evidence.Evidence: Probe test results (fm-codex-appserver-probe.test.sh)
ok - fm-codex-appserver-probe: default invocation is safe dry-run ok - fm-codex-appserver-probe: refuses unauthenticated non-loopback WebSocket listener ok - fm-codex-appserver-probe: schema generation summarizes lifecycle methods ok - fm-codex-appserver-probe: live stdio probe drives create/read/archive protocol pathEvidence: Default dry-run + help (end-user experience)
## default invocation mode: dry-run would_create_thread: no (pass --create-thread ...) safety: no listener opened; no Codex thread created; no project worktree modifiedEvidence: Safety guards: non-loopback WS refused, loopback+stdio-only, unsupported listen
refusing unauthenticated non-loopback WebSocket listener 'ws://0.0.0.0:4500' (exit 1) live probe currently supports stdio:// only (exit 1) unsupported --listen URL: tcp://example.com:1234 (exit 1)Evidence: codex-app NOT a spawn-capable backend; Herdr/tmux untouched
error: unknown backend 'codex-app' (known: tmux herdr zellij orca) bin/fm-backend.sh:58:FM_BACKEND_SPAWN="tmux herdr zellij orca"Evidence: Schema generation + live create/read/archive + keep-thread output
schema_required_lifecycle: present schema_turn_methods: turn/interrupt,turn/start,turn/steer live_initialize: ok created_thread_id: 00000000-0000-4000-8000-000000000001 codex_thread_url: codex://threads/00000000-0000-4000-8000-000000000001 archived_created_thread: yesEvidence: Archive no-rollout graceful degradation
archived_created_thread: unavailable-no-rolloutEvidence: Schema exit-code propagation (gen failure exit 3; missing-method exit 2)
schema gen failed -> exit=3 schema_required_lifecycle: missing thread/list,thread/read,thread/archive,... -> exit=2Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 3 issues found → auto-fixed ✅
bin/fm-codex-appserver-probe.sh:351- Schema-generation failures are masked by exit 0. The script uses onlyset -u(noset -e), so whencodex app-server generate-json-schema(line 351) orsummarize_schema(line 352) fails, the exit codes are discarded and execution falls through to the trailingif [ "$LIVE_HANDSHAKE" -eq 1 ](lines 355-357). When no live handshake runs (or it succeeds), the script exits 0 even though schema generation failed - I confirmed this with a trace: a failing codex+summarize pair yieldsexit=0. Automated callers/CI relying on the exit code would silently miss schema failures. Propagate the schema command/function exit codes (e.g. capture and return them, or gate the trailing block on success).bin/fm-codex-appserver-probe.sh:249- Graceful-degradation paths hinge on substring-matching external Codex error text:includes("includeTurns")(line 249) andincludes("no rollout")(line 260). The spike docs explicitly position this probe as the tool to re-run as Codex versions change, so if Codex rewords these messages the probe will throw instead of reporting the documentedunavailable-no-rollout/includeTurns=falsefallbacks. Unexpected errors are still surfaced loudly (good), but the known-state handling is version-fragile. Consider matching on error codes if Codex exposes them, lowercasing the comparison, or tolerating any failure for a no-turn thread rather than a literal phrase.bin/fm-codex-appserver-probe.sh:54- The WebSocket loopback/auth validation is advisory only: the live probe unconditionally rejects any non-stdio transport (lines 323, 325), sois_loopback_ws(line 54, whosews://127.*glob would also accept crafted hosts likews://127.0.0.1.evil.com) andhas_ws_auth_material(which treats a single capability-token field as sufficient) never gate an actual listener. No current security impact, but when a futurebackend=codex-appwires real ws these loose checks should be tightened (parse the host; require both token + verifier).🔧 Fix: propagate schema exit codes and harden codex app-server probe error matching
✅ Re-checked - no issues remain.
🔧 **Test** - 1 issue found → auto-fixed ✅
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"🔧 Fix: fix node-on-PATH test flakiness in fm-session-start tests
✅ Re-checked - no issues remain.
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-codex-appserver-probe.test.sh(4/4 assertions: dry-run default, non-loopback WS refusal, schema lifecycle summary, live stdio create/read/archive)bash tests/fm-session-start.test.sh(all session-start composition/ordering/liveness assertions, incl. the lavish-axi MISSING-line determinism fix)bash bin/fm-codex-appserver-probe.sh(no args) -> dry-run safety bannerbash bin/fm-codex-appserver-probe.sh --help-> usage/options/examplesbash bin/fm-codex-appserver-probe.sh --live-handshake --listen ws://0.0.0.0:4500-> refuses unauthenticated non-loopback WS (exit 1)bash bin/fm-codex-appserver-probe.sh --live-handshake --listen ws://127.0.0.1:4500-> loopback passes auth gate, still refuses live (stdio-only) (exit 1)bash bin/fm-codex-appserver-probe.sh --listen tcp://example.com:1234-> unsupported listen refused (exit 1)schema generation + lifecycle summary against fake codex (8 methods,schema_required_lifecycle: present)live stdio create/read/archive against fake codex (live_initialize: ok,created_thread_id,codex_thread_url,archived_created_thread: yes)live stdio--create-thread --keep-threadagainst fake codex (archived_created_thread: no, temp cwd still cleaned up)archive-error-with-rolloutmatching ->archived_created_thread: unavailable-no-rollout(exit 0, graceful degradation)schema-generation failure propagates codex exit 3; schema-present-but-missing-lifecycle -> summarize exit 2FM_HOME=/tmp/... bash bin/fm-spawn.sh ... --backend codex-app-> rejected as unknown backend (known: tmux herdr zellij orca); grep confirms no codex-app references in bin/fm-backend.sh or bin/backends/post-testgit status --porcelainclean (no transient artifacts left in worktree)✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.