Problem / motivation
PR #14 finished the Claude Code adapter with two riders: (a) structured error
classification from stream-json's error category, and (b) a CLI version floor.
When the Codex (codex exec) and Antigravity (agy) adapters are built
(deferred to a later phase — PROJECT.md §15, Appendix A.2/A.3), we should
deliberately reuse or adapt these rather than reinvent them. This issue captures
how each rider maps, so nothing is lost between now and then.
Rider (b) — CLI version floor: reuse as-is
parse_cli_version, CliAdapter._minimum_version(), and check_availability()
live in the base CliAdapter (src/atlas/ai/cli/base.py). A new adapter gets
them for free and only needs:
- override
_minimum_version() → its own floor tuple;
- override
_version_argv() iff codex --version / agy --version print a
format the leading-semver regex doesn't already handle (it tolerates vX.Y.Z
and trailing text, so it likely just works).
That's a 1–3 line override per adapter; atlas doctor already surfaces the reason.
Rider (a) — structured error classification: same idea, per-adapter mechanics
The principle carries over (classify from a structured signal; keep the stderr
heuristic as a fallback), but the signal and plumbing differ — neither is a copy
of Claude's stream-json rewrite:
|
Claude Code (done) |
Antigravity (agy) |
Codex (codex exec) |
| Structured happy path |
stream-json terminal result event |
--output-format json envelope (structured_output) |
--output-schema file / final stdout msg |
| Error signal |
stream-json error category (authentication_failed, rate_limit, …) |
status field in the plain JSON envelope (SUCCESS/ERROR/CANCELED/…) |
--json JSONL error event |
| Needs a stream-json switch for errors? |
Yes (was required) |
No — status is already in the plain envelope |
No — parse the --json JSONL stream |
Notes:
- Antigravity is simpler than Claude here: key off
status != "SUCCESS" in the
plain envelope. But its statuses are coarse (not fine-grained auth-vs-rate-limit),
so auth detection may still lean on the "authentication required" text.
- Codex is the most different: file-based structured output (
-o out.json) and
a JSONL error event.
- Event names/shapes differ across all three, so field extraction stays per-adapter.
A shared "scan NDJSON events for a structured field" helper could be lifted into
the base once ≥2 adapters exist.
Proposed solution
When implementing each adapter, override _minimum_version() (+ _version_argv()
if needed) for the floor, and implement _classify_error/_parse_response against
that CLI's own error signal (status for agy, JSONL error event for codex),
keeping the stderr-substring fallback. Consider extracting a shared NDJSON-scan
helper at that point.
Alternatives considered
Generalizing the classification now (before either adapter exists) — rejected: the
signals differ enough that a premature abstraction would be guesswork; better to
build the second adapter, then lift the commonality.
References
Problem / motivation
PR #14 finished the Claude Code adapter with two riders: (a) structured error
classification from stream-json's
errorcategory, and (b) a CLI version floor.When the Codex (
codex exec) and Antigravity (agy) adapters are built(deferred to a later phase — PROJECT.md §15, Appendix A.2/A.3), we should
deliberately reuse or adapt these rather than reinvent them. This issue captures
how each rider maps, so nothing is lost between now and then.
Rider (b) — CLI version floor: reuse as-is
parse_cli_version,CliAdapter._minimum_version(), andcheck_availability()live in the base
CliAdapter(src/atlas/ai/cli/base.py). A new adapter getsthem for free and only needs:
_minimum_version()→ its own floor tuple;_version_argv()iffcodex --version/agy --versionprint aformat the leading-semver regex doesn't already handle (it tolerates
vX.Y.Zand trailing text, so it likely just works).
That's a 1–3 line override per adapter;
atlas doctoralready surfaces the reason.Rider (a) — structured error classification: same idea, per-adapter mechanics
The principle carries over (classify from a structured signal; keep the stderr
heuristic as a fallback), but the signal and plumbing differ — neither is a copy
of Claude's stream-json rewrite:
agy)codex exec)resultevent--output-format jsonenvelope (structured_output)--output-schemafile / final stdout msgerrorcategory (authentication_failed,rate_limit, …)statusfield in the plain JSON envelope (SUCCESS/ERROR/CANCELED/…)--jsonJSONLerroreventstatusis already in the plain envelope--jsonJSONL streamNotes:
status != "SUCCESS"in theplain envelope. But its statuses are coarse (not fine-grained auth-vs-rate-limit),
so auth detection may still lean on the "authentication required" text.
-o out.json) anda JSONL
errorevent.A shared "scan NDJSON events for a structured field" helper could be lifted into
the base once ≥2 adapters exist.
Proposed solution
When implementing each adapter, override
_minimum_version()(+_version_argv()if needed) for the floor, and implement
_classify_error/_parse_responseagainstthat CLI's own error signal (
statusfor agy, JSONLerrorevent for codex),keeping the stderr-substring fallback. Consider extracting a shared NDJSON-scan
helper at that point.
Alternatives considered
Generalizing the classification now (before either adapter exists) — rejected: the
signals differ enough that a premature abstraction would be guesswork; better to
build the second adapter, then lift the commonality.
References
src/atlas/ai/cli/base.py(parse_cli_version,_minimum_version,check_availability)src/atlas/ai/cli/claude_code.py(reference implementation of both riders)