Skip to content

codex-companion task path: missing-cwd misread as 'not installed', prompt fragments parsed as --model (400 as result), dropped turn errors #393

Description

@CRGDan

Three of these break the codex:codex-rescuecodex-companion.mjs task path that tools build review/delegation gates on. Found while debugging a downstream pipeline; all three reproduce on v1.0.4. I have small, isolated fixes with node --test coverage and am happy to open a PR if useful.

1. A missing cwd is misreported as "Codex CLI is not installed"

binaryAvailable() (scripts/lib/process.mjs) treats any spawnSync ENOENT as a missing binary. But spawnSync also raises ENOENT when the cwd does not exist. So when a caller passes a cwd that has been removed (e.g. a cleaned-up git worktree in a parallel/fleet runner), getCodexAvailability(cwd) returns "not found" and every entrypoint throws "Codex CLI is not installed or is missing required runtime support" — even though Codex is installed and authenticated.

Repro: binaryAvailable(process.execPath, ["--version"], { cwd: "/does/not/exist" }){ available: false, detail: "not found" }.

Fix: a --version/--help probe never depends on the caller's cwd. If a requested cwd doesn't exist, drop it for the probe so availability reflects the binary itself.

2. Prompt fragments are parsed as --model, returning a 400 as the result

The documented forward is codex-companion.mjs task "<raw arguments>" (one quoted string). normalizeArgv() re-splits that single arg with splitRawArgumentString(), and parseArgs() (scripts/lib/args.mjs) then treats any --prefixed token as an option. A prompt that mentions running -m pytest, or a module path like conflict_scan.cli, gets a fragment consumed as --model (alias -m). Codex then 400s — "The 'pytest' model is not supported when using Codex with a ChatGPT account." — and that error envelope is returned as the task/review output.

Repro: task "--write review the conflict_scan.cli module and run -m pytest to verify" → turn started with model: "pytest".

Fix: an opt-in parseArgs option (stopAtFirstPositional), used by handleTask, so option parsing ends once the free-text prompt begins (routing flags precede the prompt). Default behavior unchanged for all other commands.

3. Turn-level errors are dropped from the result status

buildResultStatus() (scripts/lib/codex.mjs) returns success purely on finalTurn.status === "completed", ignoring turnState.error. An app-server error notification can coexist with the inferred/synthetic "completed" turn produced when the main thread drains without a real turn/completed — so an errored run is reported as a clean pass.

Fix: treat any captured turnState.error as failure.

Bonus (docs/contract): silent failures from the rescue subagent

agents/codex-rescue.md / skills/codex-cli-runtime/SKILL.md instruct: "If the Bash call fails or Codex cannot be invoked, return nothing." Callers then can't distinguish a clean "no findings" result from a silent abort. Consider emitting a structured failure envelope so callers can branch deterministically.


Environment: codex-plugin-cc v1.0.4, codex-cli 0.139.0, Node 24, macOS. Happy to send a PR with the fixes + tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions