Found by hands-on QA of the Codex path, 2026-08-21.
What happened
$ oh-my-graph --runtime codex auto "…" --plan-only --no-web
Codex runtime: Claude agent mapping and skill activation are unavailable; …
Planning a graph for goal "…"...
planning failed after spending unknown — a planner call is paid for whether or not its graph loads.
oh-my-graph: graph planning failed: planner exited with code 1
The account had simply run out of quota. Reproduced directly against the CLI
with the same argv shape oh-my-graph builds:
{"type":"error","message":"You've hit your usage limit. Upgrade to Plus to continue using Codex (…), or try again at Sep 13th, 2026 10:04 PM."}
{"type":"turn.failed","error":{"message":"You've hit your usage limit. …"}}
So a limit was reported to the user as planner exited with code 1 —
indistinguishable from a broken planner, a bad goal, or a malformed reply.
Why this is worth more than a nicer message
ADR 0009 decided that a session limit is a pause, not a failure: the run
drains, records the limited node nowhere, and resume --retry-failed picks it up
later. It scoped that to Claude on purpose (§Scope, settled 2026-08-15 closing
#171), and gave a precise reason: detection is prose matching against Claude's
wording, so there is nothing for another runtime's message to match. The
classification is gated at internal/runner/cli.go:294
(if r.protocol.runtime() == RuntimeClaude).
That reasoning was right when it was written and it does not hold for Codex,
because Codex's limit is not prose. It is a typed JSONL record —
{"type":"turn.failed","error":{"message":…}} — arriving on the same stream
parseCodexJSONL already decodes.
ADR 0009 wrote its own revisit condition, at docs/adr/0009-*.md:70:
Should a future runtime expose a structured limit signal, that is a reason
to revisit — and it would be a better foundation than this one, since the whole
mitigation list below exists to survive matching prose.
That condition is now met. The runtime the pause was withheld from is the one
with the better signal.
The asymmetry, stated plainly
|
Claude |
Codex |
| limit signal |
prose only |
typed JSONL record |
| engine treats it as |
pause, resumable |
ordinary failure |
The runtime that is harder to detect gets the good behaviour; the one that is
easy to detect gets none.
Not proposing the fix here
Two things worth deciding separately, and neither is obvious:
- Whether the pause should extend to Codex at all, or whether ADR 0009's
narrowing should simply be re-argued now that its stated premise has changed.
- Whether a planner/assessor limit deserves the same treatment as a node
limit. Today the pause covers scheduled nodes; this failure was in the
planner, before any node existed, and there is no run to resume.
Related: #214 gave the assessor a bounded retry for spawn failures on exactly
the grounds that no model was reached. A limit is the mirror case — the model
was reached and refused — and it is equally not a verdict.
Also observed in the same session (not defects)
- The
auto pre-run disclosure does name the gap: "Codex runtime: Claude
agent mapping and skill activation are unavailable". Good, and it matches
chat's.
internal/runner/codex_protocol.go carries no PluginDirs and no Agent
handling at all, so skills and agents cannot reach a Codex node even by
accident. The disclosure above is therefore accurate rather than aspirational.
Blocked
Further hands-on Codex QA is blocked until the quota resets (the CLI says
Sep 13), which is itself a finding worth writing down: this project cannot
smoke-test its second runtime on demand.
Found by hands-on QA of the Codex path, 2026-08-21.
What happened
The account had simply run out of quota. Reproduced directly against the CLI
with the same argv shape oh-my-graph builds:
{"type":"error","message":"You've hit your usage limit. Upgrade to Plus to continue using Codex (…), or try again at Sep 13th, 2026 10:04 PM."} {"type":"turn.failed","error":{"message":"You've hit your usage limit. …"}}So a limit was reported to the user as
planner exited with code 1—indistinguishable from a broken planner, a bad goal, or a malformed reply.
Why this is worth more than a nicer message
ADR 0009 decided that a session limit is a pause, not a failure: the run
drains, records the limited node nowhere, and
resume --retry-failedpicks it uplater. It scoped that to Claude on purpose (§Scope, settled 2026-08-15 closing
#171), and gave a precise reason: detection is prose matching against Claude's
wording, so there is nothing for another runtime's message to match. The
classification is gated at
internal/runner/cli.go:294(
if r.protocol.runtime() == RuntimeClaude).That reasoning was right when it was written and it does not hold for Codex,
because Codex's limit is not prose. It is a typed JSONL record —
{"type":"turn.failed","error":{"message":…}}— arriving on the same streamparseCodexJSONLalready decodes.ADR 0009 wrote its own revisit condition, at
docs/adr/0009-*.md:70:That condition is now met. The runtime the pause was withheld from is the one
with the better signal.
The asymmetry, stated plainly
The runtime that is harder to detect gets the good behaviour; the one that is
easy to detect gets none.
Not proposing the fix here
Two things worth deciding separately, and neither is obvious:
narrowing should simply be re-argued now that its stated premise has changed.
limit. Today the pause covers scheduled nodes; this failure was in the
planner, before any node existed, and there is no run to resume.
Related: #214 gave the assessor a bounded retry for spawn failures on exactly
the grounds that no model was reached. A limit is the mirror case — the model
was reached and refused — and it is equally not a verdict.
Also observed in the same session (not defects)
autopre-run disclosure does name the gap: "Codex runtime: Claudeagent mapping and skill activation are unavailable". Good, and it matches
chat's.internal/runner/codex_protocol.gocarries noPluginDirsand noAgenthandling at all, so skills and agents cannot reach a Codex node even by
accident. The disclosure above is therefore accurate rather than aspirational.
Blocked
Further hands-on Codex QA is blocked until the quota resets (the CLI says
Sep 13), which is itself a finding worth writing down: this project cannot
smoke-test its second runtime on demand.