Skip to content

feat(FAFF-593): codex spawn family for faff engine call — codex exec --json child transport#495

Merged
alechill merged 7 commits into
mainfrom
faff-593-faff-engine-call-gains-a-codex-engine-spawn-codex-exec-json
Jul 25, 2026
Merged

feat(FAFF-593): codex spawn family for faff engine call — codex exec --json child transport#495
alechill merged 7 commits into
mainfrom
faff-593-faff-engine-call-gains-a-codex-engine-spawn-codex-exec-json

Conversation

@alechill

Copy link
Copy Markdown
Contributor

Adds the codex spawn family to faff engine call: a backends: entry with provider: codex dispatches a producer through a spawned codex exec --json child process — the first non-HTTP transport family, extending ADR-0054's per-lane transport fork (recorded as ADR-0090). Spec: docs/specs/2026-07-25-FAFF-593-codex-engine-for-faff-engine-call-design.md (full spec with rationale on Linear FAFF-593).

What ships:

  • engine-codex.js — pure buildCodexArgv / parseCodexEvents / classifyCodexFailure + runCodexCall (injectable spawn/env/mkdtemp; synchronous int return; fail-loud on every path, no retry, no fallback to the session model).
  • Config guards: codex in the provider family map; provider-conditional validateEngineRef (no host, no reasoning_off, no auth: none); keyless codex derives subscription-seat; portableMatrixAdmits admits a codex seat on any harness; checkRealizable no longer silently skips hostless codex refs; new optional bin_path backend field.
  • Spawn posture: exec --json --ephemeral --skip-git-repo-check --sandbox read-only -m <model> -, prompt on stdin (system + blank line + user), fresh temp cwd removed after, OPENAI_API_KEY injected only in api-key mode.
  • Selftest table (32 codex rows) folded into faff engine --selftest; 15 new tests in test/engine-call.test.mjs; docs/architecture/harness-coupling.md row updated.

Acceptance Criteria

  • AC 1 — Given backends: declares a codex engine, the fork runs codex exec --json with the full posture flag set
    Verified: selftest argv-shape row + test/engine-call.test.mjs happy path (deepEqual on argv, exactly one exec spawn)
  • AC 2 — Fails loud (non-zero, named remedy) on missing auth
    Verified: tests — unset api_key_env → exit 6 before any spawn; seat-probe fail → exit 6 naming codex login; auth-shaped child failure → exit 6
  • AC 3 — The returned block validates against the same faff contract path as a Claude-engine fork
    Verified: happy-path test asserts stdout is the final agent message verbatim beyond trim (downstream faff-contract:* parsing unchanged)
  • Invariant — a codex backend in a requires: local chain refuses (egress external)
    Verified: selftest rows (refusal + deliberate deriveEgress → external)
  • Follow-up — live-validate the five codex-CLI assumptions (event shapes, login status exit codes, OPENAI_API_KEY honouring, exit-on-fatal, flag availability) against an installed codex binary
    Needs human verification: no codex binary on the build machine; shapes follow the spec's codex-rs-sourced pins verbatim (with a commented item_type/type drift tolerance to narrow at re-pin). The code is inert until an operator configures a codex backend, and every assumption-failure mode lands on a named loud exit. Flagged in the module banner + ADR-0090.

Review: Phase 1 (structural, five-pass) pass with zero findings; Phase 2 adversarial second opinion (nvidia/z-ai/glm-5.2) raised 3 major / 2 minor — one valid (mkdtemp throw escaping the int-return contract, fixed in 210d17d with a new selftest row), the rest dispositioned with evidence on the tracker issue.

Gates: validate-adapters PASS (19 skills), full suite 2476/2476, engine selftest PASS.

🤖 Generated with Claude Code

https://claude.ai/code/session_016jMqrXT3E21jv8vrqLYJa2

alechill and others added 7 commits July 25, 2026 15:17
…spawn codex exec --json, parse the event stream, backends: entry shape
…--json child transport

Adds the third engine transport family: provider codex dispatches a producer
through a spawned codex exec --json child (prompt on stdin, JSONL event stream
on stdout, final agent message as the producer block) instead of an HTTP POST.

- config.js: codex in ENGINE_PROVIDER_FAMILY; provider-conditional required
  fields in validateEngineRef (codex: provider+model, host/reasoning_off/auth
  none each refused with a named error); effort refusal message generalised to
  "engine backend"; resolveEngineForLane returns the codex-shaped record
  (binPath, no host).
- backends.js: bin_path joins the Backend record; keyless codex derives
  subscription-seat; the codex seat admits on any harness (the seat travels
  with the codex CLI's own login state, not the harness); checkRealizable
  skips the host-presence test for codex only.
- engine-codex.js (new): buildCodexArgv / parseCodexEvents (fail-loud JSONL,
  events kept for the FAFF-604 spend read) / classifyCodexFailure /
  runCodexCall with injectable spawnFn, plus the codex selftest table folded
  into faff engine --selftest.
- engine.js: cmdEngine forks to the codex path on resolved family, with its
  own failure tags (no HTTP host tag).

No codex binary was installed on the build machine: parser/argv shapes follow
the spec's codex-rs-sourced assumptions verbatim; live re-pinning is a flagged
needs-human item.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jMqrXT3E21jv8vrqLYJa2
…spawn

Read-time: engine:codex-seat resolves on an allowlisted lane; host-present /
reasoning_off / auth-none each exit 2 with the named error; the non-allowlisted
lane guard is unchanged. Dispatch: happy path, seat-probe fail (6), ENOENT (5),
malformed JSONL (7), no agent message (7), auth-shaped child failure (6),
api_key_env unset before any spawn (6) — all through the injected spawnFn,
zero real spawns or network.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jMqrXT3E21jv8vrqLYJa2
…is HTTP one-shot plus codex CLI spawn

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jMqrXT3E21jv8vrqLYJa2
…anch of ADR-0054's fork (FAFF-593)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jMqrXT3E21jv8vrqLYJa2
@alechill
alechill force-pushed the faff-593-faff-engine-call-gains-a-codex-engine-spawn-codex-exec-json branch from d94cf25 to 7a859bd Compare July 25, 2026 15:17
@alechill
alechill merged commit 65a4f91 into main Jul 25, 2026
6 checks passed
@alechill
alechill deleted the faff-593-faff-engine-call-gains-a-codex-engine-spawn-codex-exec-json branch July 25, 2026 15:21
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