Skip to content
Readiness report

openai/codex

A deep, specific AGENTS.md and a serious CI gate; nothing written down about why, and no single command to prove a change. Below is the run the agent did and the AINE-REPORT.md it wrote.

  • 30 passed
  • 16 failed
  • 0 n/a
  • Rust · Cargo + Bazel · just · Python · GitHub Actions

Run 13 August 2026 · main at a7b8c07

65OUT OF 100

The agent run

You
/aine-readiness-analyzer on the openai/codex repo, downloaded in a new temp folder
Agent

I cloned openai/codex into a temp folder, audited it against all 46 checks and wrote the full report to AINE-REPORT.md in the repo.

AI-Native Readiness — openai/codex
30 passed · 16 failed · 0 not applicable

High priority — fix these first

  1. Fix the four stale paths in AGENTS.mdmcp_connection_manager.rsconnection_manager.rs (line 35), protocol/v2.rsprotocol/v2/ (lines 276, 287), core/suitecore/tests/suite (line 114), core/contextcore/src/context (line 100). Why: an agent told to route MCP tool-call changes through a file that does not exist will invent its own place to put them.
  2. Clear the stale exception so the repo's own verifier is green on main — drop the codex-rs/code-mode entry from MANIFEST_FEATURE_EXCEPTIONS in .github/scripts/verify_cargo_workspace_manifests.py. Why: that check is merge-blocking and it exits 1 on an untouched checkout, so an agent cannot tell its own damage from the baseline.
  3. Add a "requires a human" block to AGENTS.md naming the release tag pushes (rust-v*, python-v*), pushes under .github/**, and the full just test sweep. Why: .github/workflows/rust-release.yml hands out git push origin rust-v0.1.0 with no fence around it, and that command ships signed binaries to the public.
  4. Add a secret-scan job to blocking-ci.yml next to codespell, allowlisting the three files that hold key-shaped test fixtures. Why: nothing scans today, and the codebase deliberately contains key-shaped fixtures, so a real key would not stand out in a diff.
  5. Add a check target to justfile chaining fmt-check, clippy, test, bazel-lock-check and the three .github/scripts/verify_*.py verifiers. Why: AGENTS.md currently spells out seven commands and four conditionals an agent has to carry itself before pushing.
  6. Add a layout section to AGENTS.md — one line per top-level area, including sdk/, scripts/, tools/, codex-cli/ and third_party/, none of which the instruction set mentions at all. Why: 5,755 files under codex-rs and five unmentioned sibling trees; an agent landing in sdk/ has no guidance.
  7. Start docs/adr/ with a TEMPLATE.md, seeding it with the four decisions that exist today only as lint reasons and CI-script docstrings. Why: this one root fix unblocks six failing checks — specs freshness, spec scaffolding, machine-checkable acceptance criteria, failure-and-rollback criteria, non-goals and traceability.

Low priority — worth doing

  1. Add npm, uv and tools/argument-comment-lint blocks to .github/dependabot.yaml — six of nine lockfiles are unwatched, including the tree published to PyPI.
  2. Pick one convention for in-flight state (for example plans/<pr-number>.md) and name it in AGENTS.md, so a fresh session can resume without re-reading the diff.
  3. Split the TUI styling, app-server API and Python sections out of AGENTS.md — it is 22,519 bytes (~5.6k tokens) loaded in full every session, and roughly 40% of it is irrelevant to any given task.

One blind spot, stated in the affected proofs: the machine running the audit had no cargo, just, bazel or pnpm, so the Rust and Bazel suites and clippy could not be executed. The Python suites and the three CI verifiers were run, and their results are in the file.

The report it wrote

AINE-REPORT.md

AI-Native Readiness Report

Repo: openai/codex, a fresh clone of main at commit a7b8c07 (2026-08-13) Date: 2026-08-13 Stack: Rust monorepo — a ~170-crate Cargo workspace under codex-rs/ (edition pinned to 1.95.0 via rust-toolchain.toml), built by both Cargo and Bazel 9.0.0 (MODULE.bazel, BUILD.bazel); just as the task runner; Python 3 for repo tooling (scripts/, .github/scripts/, uv-locked) and for the SDK under sdk/; pnpm 10.33.0 workspace for the npm packaging and formatting layer; Nix flake and devcontainer for environments; GitHub Actions for CI. Test runners: cargo nextest, bazel test, insta snapshots, unittest.

Result: 30 passed, 16 failed, 0 not applicable


How to read this. Every item is a numbered question about this repo, answered from evidence in this repo. Proof is what was actually found — a path, a file, a command and its output. An item with no proof is not an answer. The italic line under each question says how to check it; it stays in the report so the next run checks the same way.

FAIL vs N/A. FAIL means the practice would help here and it is missing — including when a whole artifact is missing, so several questions about it fail together with a one-line proof pointing at the item that found it missing. N/A means the question could never apply to a project of this kind, and the proof says why. "It does not exist" is a FAIL, never an N/A.

Priority is fixed by the template, not by the person filling it in. High means an agent working here will hit this on day one, or the risk is real. Low means it makes things better but nothing breaks without it.


1. Context

  • 1. Does an agent instruction file exist (AGENTS.md, CLAUDE.md, .cursorrules, .github/copilot-instructions.md, or this ecosystem's equivalent), and what does it point at? Look for a file whose purpose is to be loaded into a coding agent's context at the start of every session. That file is the entry point. Then follow its pointers: instructions are commonly spread across several files, with the entry point routing to them — "commands are in docs/development.md", "conventions in .claude/rules/". The entry point plus every file it points at is the instruction set, and every other agent-instruction question is answered against that whole set, so list it here in the proof. Follow pointers as deep as they go, but a pointer only counts when it names a target and says what is in it: "test commands are in CONTRIBUTING.md" counts, a bare "see the docs" does not, and a file nothing points at is outside the set however good it is. A README is not an entry point, however good — it gets its credit at the README item and the specs-and-architecture item — but a README the entry point explicitly routes to is inside the set. Vendored skill libraries are not instructions either.

    • Status: PASS
    • Proof: Entry point is AGENTS.md at the repo root (22,519 bytes). A second, directory-scoped file exists at codex-rs/tui/src/bottom_pane/AGENTS.md (paste-burst and chat-composer doc-sync rules). Pointers out of the entry point that name a target and say what is in it: codex-rs/tui/styles.md ("TUI style conventions"), the $remote-tests skill resolving to .codex/skills/remote-tests/SKILL.md (cross-OS app-server/exec-server integration testing), codex-rs/app-server/README.md (app-server API docs to update when behaviour changes), and codex-rs/core/config.schema.json (regenerated via just write-config-schema). Instruction set = those five files. Outside the set: the root README.md, docs/contributing.md and docs/install.md are never routed to from AGENTS.md, and the 13 skills under .codex/skills/ other than remote-tests are not pointed at either. Note .gitignore lines 33-35 ignore CLAUDE.md, .claude/ and AGENTS.override.md, so a Claude-family agent inherits none of this harness from a clean clone.
    • Recommendation: —
    • Priority: High
  • 2. Do the agent instructions name the commands to build, test and check this project? Answer this against the instruction set mapped at the entry-point item and nothing outside it. If no entry point exists, FAIL with a one-line proof pointing at that item. The commands may sit in a file the entry point routes to rather than in the entry point itself — that is progressive disclosure working as intended, and it passes. What fails is a command an agent would have to guess its way to: if the commands live only in the README or the manifest and nothing in the set points at them, that is a FAIL, because the question measures what an agent can reach without being told where to look. Name the file each command was found in.

    • Status: PASS
    • Proof: All in AGENTS.md, all verified against justfile: just fmt (line 64), just test and just test -p codex-tui (lines 66-68), just fix -p <project> (line 70), just argument-comment-lint (line 20), just write-config-schema (line 34), just write-app-server-schema (line 303), just bazel-lock-update (line 37), just bench / just bench-smoke (lines 206-208), plus cargo insta pending-snapshots/show/accept -p codex-tui (lines 194-198). AGENTS.md:66 also carries the negative rule that matters most here: "Do not run cargo test directly. Use just test". Gap worth naming: no build command is in the set — cargo build and the from-source setup live only in docs/install.md, which nothing in the set points at.
    • Recommendation: —
    • Priority: High
  • 3. Does everything the agent instructions name still exist — the commands, the paths, the libraries, and the files they point at? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. Verify, do not trust: check every named command against the manifest or build file, spot-check the paths, run the cheap read-only ones. Then resolve every pointer in every file of the set — a link to a moved or deleted file is the most common rot in a multi-file instruction set, and it fails silently: the agent reads the entry point, follows nothing, and carries on without the rules.

    • Status: FAIL
    • Proof: Commands verified: all 9 just targets named in AGENTS.md exist in justfile (checked target by target). Cheap read-only runs that pass: python3 .github/scripts/verify_bazel_clippy_lints.py → exit 0. Paths mostly resolve (codex-rs/tui/styles.md, codex-rs/core/config.schema.json, codex-rs/app-server/README.md, codex-rs/app-server-protocol/src/protocol/common.rs, codex-rs/tui/src/app.rs, chatwidget.rs, bottom_pane/chat_composer.rs, footer.rs, bottom_pane/mod.rs, tui/src/wrapping.rs, .codex/skills/remote-tests/SKILL.md all present), but four are stale: AGENTS.md:35 names codex-rs/codex-mcp/src/mcp_connection_manager.rs, which does not exist — the real file is codex-rs/codex-mcp/src/connection_manager.rs; AGENTS.md:276 and :287 name app-server-protocol/src/protocol/v2.rs, which is now a directory, codex-rs/app-server-protocol/src/protocol/v2/; AGENTS.md:114 says integration tests are "under core/suite", but the real path is codex-rs/core/tests/suite/; AGENTS.md:100 requires fragments be "defined as structs in core/context", real path codex-rs/core/src/context/.
    • Recommendation: Fix the four paths in AGENTS.md: mcp_connection_manager.rsconnection_manager.rs (line 35), protocol/v2.rsprotocol/v2/ (lines 276, 287), core/suitecore/tests/suite (line 114), core/contextcore/src/context (line 100).
    • Priority: High
  • 4. Are the agent instructions specific to this repo, rather than advice that would read the same in any codebase? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. The test: could this be pasted into another project unchanged? "Write clean code" and "add tests for new features" would fit anywhere and count for nothing. Judge the whole set, but weigh the files differently: an entry point that is mostly a routing table is fine, even good, when what it routes to is specific — while generic filler in the entry point costs more than generic filler three hops down, because it is loaded into every session whether it is needed or not. Say which files carried the specifics.

    • Status: PASS
    • Proof: Almost nothing here would survive a copy-paste into another repo. From AGENTS.md: the CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR / CODEX_SANDBOX_ENV_VAR prohibition and the Seatbelt early-exit rationale (lines 8-10); "resist adding code to codex-core" with the reason it got bloated (lines 72-83); the /*param_name*/ argument_comment_lint convention with its exemptions (lines 15-20); the 500/800-LoC module ceiling naming six specific high-touch files including codex-rs/tui/src/chatwidget.rs (lines 49-61); the app-server v2 rules (#[ts(optional = nullable)] only on *Params, never skip_serializing_if on v2 payloads, #[ts(export_to = "v2/")], camelCase-on-the-wire with the config-RPC snake_case exception) (lines 270-296); the Bazel compile_data rule for include_str! / sqlx::migrate! (lines 40-43). The specifics are carried by the entry point itself and by codex-rs/tui/styles.md. The only generic filler is a handful of test-hygiene lines such as "prefer comparing the equality of entire objects" — and even those are tied to pretty_assertions and named helpers.
    • Recommendation: —
    • Priority: High
  • 5. Do the agent instructions explain where things live and how this project is laid out? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. The map may live in a routed-to file. Judge coverage against the real tree, not against what the files mention: if the set maps one package well but is silent about sibling packages or directories an agent would land in, that is a FAIL with the omission named. In a multi-file set, check the routing too — a layout document nothing points at is a document the agent never opens.

    • Status: FAIL
    • Proof: AGENTS.md names individual files and crates as it needs them (codex-rs/core/, codex-rs/tui/, codex-rs/app-server-protocol/, codex-rs/codex-mcp/) but never maps the tree, and coverage against the real tree is thin: git ls-files counts 5,755 tracked files under codex-rs across roughly 170 crates, plus five sibling top-level areas the set is completely silent about — sdk/ (117 files, published to PyPI by .github/workflows/python-sdk-release.yml), scripts/ (50 files, a Python package with its own pyproject.toml and uv.lock), tools/ (31 files, the custom Dylint linter), codex-cli/ and third_party/. grep -iE 'sdk|codex-cli|third_party' AGENTS.md returns nothing. The obvious candidate for a map, codex-rs/README.md, is three lines long and only links to the public docs site, and nothing in the set points at it anyway.
    • Recommendation: Add a short layout section to AGENTS.md — one line per top-level area (codex-rs/ Rust workspace, sdk/ Python and TS SDKs, scripts/ repo tooling, tools/argument-comment-lint/ custom lint, .github/ CI, docs/ user docs) — or write it into codex-rs/README.md and route to it from AGENTS.md.
    • Priority: High
  • 6. Do the agent instructions state the rules that are not obvious from the code — the things never to do here? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. These are the tribal-knowledge traps: the flag that must be exactly this string, the import that breaks the build, the directory that is generated and must not be edited. Rules in a routed-to file count. Two extra checks in a multi-file set: that the entry point signposts the rules clearly enough for an agent to open them before it needs them, since a trap found afterwards has already been sprung; and that the files do not contradict each other, because nothing tells the agent which one wins.

    • Status: PASS
    • Proof: Real traps, not advice. From AGENTS.md: never touch CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR or CODEX_SANDBOX_ENV_VAR (line 8); "Do not run cargo test directly" (line 66); "Do not add general product or user-facing documentation to the docs/ folder" (line 32); if ConfigToml changes, run just write-config-schema (line 34); if Cargo.toml/Cargo.lock change, run just bazel-lock-update or CI fails on lockfile drift (lines 37-39); Bazel will not see compile-time file reads unless the crate's BUILD.bazel is updated (lines 40-43); "never try to kill them using the PID" for slow Rust commands (line 62). Signposting is adequate because all of it sits in the single top-level bullet list an agent reads before doing anything. No contradictions found between AGENTS.md, codex-rs/tui/styles.md and codex-rs/tui/src/bottom_pane/AGENTS.md; the styles file and the entry point's TUI section agree (both ban .white(), and codex-rs/clippy.toml enforces it via disallowed-methods). One trap that is written down but sits outside the instruction set: .codex/skills/pushing-ci-changes/SKILL.md documents that pushes touching .github/**/*.yml are rejected without an out-of-band approval.
    • Recommendation: —
    • Priority: Low
  • 7. Has the agent instruction file been updated recently enough to still be true, given how active the repo is? If no agent instruction file exists, FAIL with a one-line proof pointing at the entry-point item. Compare the last commit touching the file against the repo's tempo, then spot-check two or three of its claims against the code — a recently touched file can still lie.

    • Status: PASS
    • Proof: git log -1 --format=%ad -- AGENTS.md → 2026-08-12, one day before HEAD (2026-08-13, commit a7b8c07), against a tempo of 30 commits in the last 14 days. Spot-checks of its claims: just fmt / just test / just fix all exist in justfile; the ban on .white() is really enforced in codex-rs/clippy.toml (ratatui::style::Stylize::white, with a reason string); codex-rs/tui/styles.md exists as claimed; insta snapshot tests exist under codex-rs/tui/tests/. Third spot-check failed — the codex-mcp path is stale — which is recorded at the everything-still-exists item rather than here.
    • Recommendation: —
    • Priority: Low
  • 8. Is the agent instruction file small enough to load into every session without crowding out the actual task? If no agent instruction file exists, FAIL with a one-line proof pointing at the entry-point item. Measure it: wc -c, and bytes divided by four is a fair token estimate. Under ~5k tokens is comfortable; past ~10k it is eating the context window.

    • Status: FAIL
    • Proof: wc -c AGENTS.md → 22,519 bytes, roughly 5,600 tokens, past the ~5k comfortable line. It is one flat file with almost no progressive disclosure, so every session loads the ratatui Stylize styling rules (lines 133-163), the app-server serde/ts-rs annotation rules (lines 260-306) and the Python 3 section (lines 308-316) whether the task touches those areas or not — that is roughly 40% of the file. The repo already knows how to do this properly: codex-rs/tui/styles.md and .codex/skills/remote-tests/SKILL.md are exactly the pattern the rest of the file is not following.
    • Recommendation: Move the three domain sections out of AGENTS.md into routed-to files — the TUI code/styling conventions into the existing codex-rs/tui/styles.md, the app-server API rules into codex-rs/app-server/README.md (already referenced), and the Python section into sdk/ — leaving one-line pointers behind. That gets the always-loaded file back under ~3k tokens.
    • Priority: Low
  • 9. Can a fresh session pick up a half-finished task — is there a file or convention where progress, decisions and what is left get written down? This is not about the agent instruction file. Any durable place in-flight state gets written counts: a TODO.md, a plans or notes directory, task files, linked issues, a specs folder whose entries carry progress and open questions, an agent memory file or directory, a scratchpad or working-notes convention. The test is whether a fresh session could read it and know what was decided and what is left — not what the place is called. Git history alone does not count: it records what happened, not what was decided or what remains.

    • Status: FAIL
    • Proof: No durable place for in-flight state. git ls-files finds no TODO.md, no plans, notes or tasks directory, no specs folder carrying progress, and no agent memory file (the codex-rs/memories/ and codex-rs/core/src/tasks/ hits are product source, not repo state). CHANGELOG.md is a single line pointing at the GitHub releases page. What exists records what happened, not what was decided or what is left: every commit carries a PR number (Add the Guardian V2 Luna sampler (#38368)) and .github/pull_request_template.md asks for a linked issue. Closest near-miss is .codex/skills/babysit-pr/, which watches a PR through CI via scripts/gh_pr_watch.py but persists nothing a fresh session could resume from.
    • Recommendation: Adopt one convention and name it in AGENTS.md — for example a plans/<pr-number>.md file per in-flight change holding the decision, what is done and what is left — so a fresh session can resume without re-reading the diff.
    • Priority: Low
  • 10. Is there a README that says what this project is and how to run it? This is where a good README earns its credit. It needs three things: what the project is, how to run it, and how to check a change. Judge what is on the page, not the file's existence.

    • Status: PASS
    • Proof: README.md says what it is ("Codex CLI is a coding agent from OpenAI that runs locally on your computer", line 1) and how to run it (install via curl/PowerShell one-liners, npm install -g @openai/codex, or brew install --cask codex, then "simply run codex", lines 16-50), including how to pick a platform binary from a GitHub Release. How to check a change is not on the page itself; it is one clearly-named link away under a ## Docs heading — [**Contributing**](./docs/contributing.md) and [**Installing & building**](./docs/install.md) (lines 77-78), and docs/install.md carries the full build-and-test sequence (cargo build, just fmt, just fix -p <crate>, just test -p codex-tui). The README is written for users, and it routes contributors correctly rather than guessing.
    • Recommendation: —
    • Priority: High

2. Specs

  • 11. Is the thinking behind this system written down somewhere durable — a specs, RFC, proposals, design-doc or ADR directory, or architecture notes that record not just what but why? Look for the place and for the convention: specs/, docs/adr/, rfcs/, proposals/, .specify/, a docs or design folder, architecture notes anywhere in the tree, or this ecosystem's equivalent. Judge substance, not location: "we use X because Y" is a decision, a list of technologies is not, an essay about specs is not a spec, and a docs folder of usage guides with no reasoning is a FAIL whose proof says what was in there instead. Other items are answered against whatever this item finds, so name it precisely — and where forward-looking specs and after-the-fact architecture records live in different places, name both, since a decision log cannot answer a question about acceptance criteria.

    • Status: FAIL
    • Proof: No decision record of any kind. There is no specs/, rfcs/, docs/adr/, proposals/ or .specify/ directory — git ls-files | grep -Ei '(^|/)(specs?|rfcs?|adr|proposals?|design)(/|s/)' returns nothing. What is in docs/ instead: 15 files that are usage guides and policy, not reasoning — config.md, example-config.md, exec.md, execpolicy.md, sandbox.md, authentication.md, getting-started.md, slash_commands.md, agents_md.md, skills.md (three lines, a link to the docs site), plus contributing.md, install.md, CLA.md, license.md, open-source-fund.md. codex-rs/docs/ holds three interface references (bazel.md, codex_mcp_interface.md, protocol_v1.md) — what, not why. Real "X because Y" reasoning does exist in this repo, but it is scattered into places that cannot serve as a decision log: AGENTS.md:72-83 (why to resist codex-core), .github/scripts/verify_cargo_workspace_manifests.py ("Workspace crate features are disallowed because our Bazel build setup does not honor them today"), a reason = string on every entry in codex-rs/clippy.toml, and codex-rs/.config/nextest.toml comments. No forward-looking spec convention and no after-the-fact architecture record exists anywhere in the tree.
    • Recommendation: Create docs/adr/ with one file per decision already made and re-litigated in code comments — start with the four that are load-bearing today: why workspace crate features are banned, why codex-tui must not import codex-core, why Bazel and Cargo clippy flags must stay in lockstep, and why new code stays out of codex-core. Each one currently lives only as a lint reason or a CI script docstring.
    • Priority: High
  • 12. Does the specs directory hold recent entries, or is it an archive nobody has touched? If no specs directory exists, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise compare the newest entry's date against the repo's recent activity.

    • Status: FAIL
    • Proof: No specs directory — see the specs-and-architecture item.
    • Recommendation: Blocked by the missing specs directory.
    • Priority: Low
  • 13. Is there a spec template, or an SDD framework, so every spec comes out the same shape? Scaffolding can exist even where no specs directory does — look for a TEMPLATE.md, a .specify/ directory, or framework config. If neither a directory nor any scaffolding exists, FAIL.

    • Status: FAIL
    • Proof: No spec scaffolding of any kind: no TEMPLATE.md, no .specify/, no SDD framework config anywhere in git ls-files. Templates that do exist are intake forms, not spec shapes — .github/pull_request_template.md (three paragraphs, mostly the external-contribution policy) and six .github/ISSUE_TEMPLATE/*.yml forms. codex-rs/collaboration-mode-templates/templates/plan.md is product source the CLI ships to its own users, not repo scaffolding.
    • Recommendation: Add docs/adr/TEMPLATE.md with the sections a decision needs here — context, decision, why not the alternatives, what breaks if it is reverted — alongside the new docs/adr/ directory.
    • Priority: Low
  • 14. Do the specs state acceptance criteria a machine could check? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise open the two newest specs and quote a criterion: "the endpoint returns 403 for expired tokens" is checkable; "the feature works well" is not.

    • Status: FAIL
    • Proof: No specs — see the specs-and-architecture item.
    • Recommendation: Blocked by the missing specs directory.
    • Priority: High
  • 15. Open the newest spec: do its criteria go past the happy path — what happens when a step fails, and how the change gets undone? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Look for error cases, edge inputs, and a rollback or undo story, not just the success flow.

    • Status: FAIL
    • Proof: No specs — see the specs-and-architecture item.
    • Recommendation: Blocked by the missing specs directory.
    • Priority: Low
  • 16. Do the specs state non-goals, so an agent knows where to stop? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Non-goals written elsewhere (a README's "what this is not" list) are worth naming in the proof, but they do not turn this into a PASS — the question is whether specs carry them.

    • Status: FAIL
    • Proof: No specs — see the specs-and-architecture item. Non-goals are written elsewhere and worth naming: docs/contributing.md ("Pull requests that have not been explicitly invited by a member of the Codex team will be closed without review") and AGENTS.md:32 ("Do not add general product or user-facing documentation to the docs/ folder"). Neither is a spec.
    • Recommendation: Blocked by the missing specs directory.
    • Priority: Low
  • 17. Can recent shipped work be traced back to a spec? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise take the last few substantial commits or PRs and look for a reference to a spec, an issue, or a design doc in the message or description.

    • Status: FAIL
    • Proof: No specs — see the specs-and-architecture item.
    • Recommendation: Blocked by the missing specs directory. Worth knowing that the tracing habit is already there: all 15 most recent commits carry a PR number, e.g. 72fa74f Persist security risk scores in rollout history (#38363), and the PR template asks for a linked issue — so once decision records exist, referencing them from the PR body is a small step.
    • Priority: Low

3. Verification

  • 18. Does this project have an automated test suite, in whatever form this ecosystem uses? Work out this ecosystem's convention before concluding anything is missing — check the manifest, the build file, the CI config, the README. A shell script that diffs output files is a test suite. If you find one, run it and record the result.

    • Status: PASS
    • Proof: Several suites, in three ecosystems. Rust: cargo nextest via just test (justfile:81), configured by codex-rs/.config/nextest.toml (retries, slow-timeout, per-group thread caps); integration tests under codex-rs/core/tests/suite/ (60+ files) and codex-rs/app-server/tests/suite/; unit tests in *_tests.rs siblings; insta snapshot tests in codex-rs/tui/tests/. Bazel: just bazel-testbazel test --test_tag_filters=-argument-comment-lint //... --keep_going. Python: scripts/codex_package/test_*.py, scripts/install/test_install_sh.py, .github/scripts/test_*.py, scripts/mcp_conformance/test_*.py. Ran what this machine could: python3 -m unittest discover -s scripts/codex_package -p 'test_*.py' → 13 tests, OK; -s scripts/install → 17 tests, OK; -s .github/scripts → OK. The Rust and Bazel suites could not be run here — which cargo rustc just bazel all return not-found on this machine, so their result is unverified rather than assumed.
    • Recommendation: —
    • Priority: High
  • 19. Can the test command be discovered without guessing — is it written down where an agent reads? If no test suite exists, FAIL with a one-line proof pointing at the test-suite item. Otherwise check the places an agent looks: the agent instruction file, the README, the manifest's scripts or targets.

    • Status: PASS
    • Proof: just test is named in the instruction set (AGENTS.md:66-68, with the crate-scoped form just test -p codex-tui and the rule not to use cargo test), in docs/install.md ("just test runs the test suite via nextest"), in docs/contributing.md ("Run all checks locally... just test -p codex-tui or just test"), and discoverable from the tool itself — just help runs just -l, and every target in justfile carries a doc comment.
    • Recommendation: —
    • Priority: High
  • 20. Do the tests assert real behaviour, rather than asserting that a mock was called? If no test suite exists, FAIL with a one-line proof pointing at the test-suite item. Otherwise open the largest test files and read the assertions: calling real code on real inputs passes; expect(mock).toHaveBeenCalled() as the main dish fails.

    • Status: PASS
    • Proof: Real behaviour, and the mocking that exists is at the network edge rather than over the code under test. codex-rs/core/tests/suite/ drives a real Codex session built by TestCodexBuilder against a mock SSE /responses endpoint, then asserts on the payloads the real code actually produced — AGENTS.md:226-250 documents the pattern and the helpers (ResponseMock::single_request(), request.function_call_output(call_id), request.body_json()). Sampled codex-rs/core/tests/suite/approvals.rs: assertions compare real session state, e.g. assert_eq!(test.session_configured.active_permission_profile.as_ref().map(|profile| profile.id.as_str()), Some(BUILT_IN_PERMISSION_PROFILE_WORKSPACE)) at line 726. insta snapshots assert rendered TUI output. AGENTS.md:212-214 mandates pretty_assertions::assert_eq on whole objects and forbids mutating process environment in tests; AGENTS.md:114-121 requires integration tests over unit tests for agent changes and bans test-only functions in implementation files. No expect(mock).toHaveBeenCalled()-style pattern found as the main assertion.
    • Recommendation: —
    • Priority: High
  • 21. Is there a linter or static analysis configured for this language, and does it pass on a clean checkout? Configured is not enough — run it. A linter that exits non-zero on an untouched checkout is a FAIL with the error count in the proof, because an agent cannot tell its own damage from the baseline noise.

    • Status: FAIL
    • Proof: Static analysis is unusually thorough: codex-rs/clippy.toml (disallowed-methods with a reason on each, await-holding-invalid-types, large-error-threshold), [workspace.lints.clippy] in codex-rs/Cargo.toml, a custom Dylint pass at tools/argument-comment-lint/ with its own UI tests, cargo-shear and cargo-deny (codex-rs/deny.toml), codespell (.codespellrc), markdownlint-cli2, prettier, plus three bespoke verifiers in .github/scripts/. But one blocking check does not pass on this untouched checkout of main (a7b8c07): python3 .github/scripts/verify_cargo_workspace_manifests.py exits 1 with "codex-rs/code-mode/Cargo.toml: - remove the stale [features] exception from MANIFEST_FEATURE_EXCEPTIONS". That check is wired into the merge-blocking suite via repo-checks.yml, so an agent running the repo's own checks sees a red baseline it did not cause. Clippy itself could not be run here — no cargo on this machine — so its clean-checkout status is unverified; the failure above is reproducible with python3 alone.
    • Recommendation: Clear the stale exception: drop the codex-rs/code-mode entry from MANIFEST_FEATURE_EXCEPTIONS in .github/scripts/verify_cargo_workspace_manifests.py (or remove the [features] block from codex-rs/code-mode/Cargo.toml, whichever the migration intended) so the verifier is green on main.
    • Priority: High
  • 22. Is there a formatter, so an agent's diffs do not churn on style? Look for the config file and the dependency in this ecosystem's form — .prettierrc, rustfmt, gofmt, black, an .editorconfig doing real work. If the language ships one formatting standard with the toolchain, that is a PASS and the proof says so.

    • Status: PASS
    • Proof: just fmtscripts/format.py, which covers the justfile, Rust (rustfmt with --config imports_granularity=Item), Bazel/Starlark via buildifier, and Python; just fmt-check is the non-mutating form. .prettierrc.toml plus .prettierignore cover JSON, Markdown, YAML and JS, wired to pnpm format / format:fix in package.json. Formatting is enforced pre-merge: .github/workflows/rust-ci.yml runs cargo fmt -- --config imports_granularity=Item --check. AGENTS.md:64 tells the agent to run just fmt after every change without asking for approval.
    • Recommendation: —
    • Priority: Low
  • 23. Is there a compile-time or type-level gate, if this language offers one? N/A only when the language genuinely has no such gate. If the language offers one and the repo does not use it — no strict mode, no typecheck script, no compiler step — that is a FAIL. Run the gate if it exists and record the result.

    • Status: PASS
    • Proof: Rust's compiler is the gate and the repo pins and configures it: codex-rs/rust-toolchain.toml pins channel 1.95.0 with clippy, rustfmt, rust-src; [workspace.lints.clippy] in codex-rs/Cargo.toml is enforced workspace-wide, and .github/scripts/verify_cargo_workspace_manifests.py exists specifically to catch crates that forget [lints] workspace = true ("Without that opt-in, cargo clippy can miss violations that Bazel clippy catches"). The Bazel and Cargo lint sets are kept in lockstep by .github/scripts/verify_bazel_clippy_lints.py, which I ran: exit 0, "Bazel clippy flags in .bazelrc match codex-rs/Cargo.toml [workspace.lints.clippy]." On the wire side, ts-rs generates TypeScript from the Rust types with committed schema fixtures under codex-rs/app-server-protocol/schema/, regenerated by just write-app-server-schema. Compilation itself could not be run here (no cargo).
    • Recommendation: —
    • Priority: Low
  • 24. Can an agent prove its own work before it pushes — one command, task-runner target or commit hook that runs every check this project has? One command, not a list to remember: a check or verify target, a precommit script, a Makefile target that chains them. Separate commands documented side by side are close but FAIL — the question is whether the agent can run the whole gauntlet without knowing its parts.

    • Status: FAIL
    • Proof: No single entry point. justfile has 40+ targets but none of them is a check, verify or precommit that chains the others — grep -nE '^[a-z][a-z0-9-]*( \*args)?:' justfile shows fmt, fmt-check, fix, clippy, test, bazel-test, bazel-clippy, argument-comment-lint, bazel-lock-check all as separate leaves. There are no git hooks either: no .pre-commit-config.yaml, no husky, no .githooks anywhere in git ls-files. So AGENTS.md has to spell out a conditional sequence the agent must carry itself — just fmt always (line 64), then just test -p <crate> and possibly full just test (lines 66-68), then just fix -p <project> (line 70), plus just bazel-lock-update if Cargo deps changed (line 37), just write-config-schema if config types changed (line 34), just write-app-server-schema if API shapes changed (line 302), and just argument-comment-lint for positional literals (line 20). Documented side by side, but seven commands and four conditionals is exactly what this question is about.
    • Recommendation: Add a check target to justfile that chains fmt-check, clippy, test, bazel-lock-check and the three .github/scripts/verify_*.py verifiers, then point AGENTS.md at just check as the one command to run before pushing.
    • Priority: High
  • 25. Is there continuous integration, and does it run before a change lands rather than after? A deploy pipeline that runs on push to the default branch runs after the change has landed — that is a FAIL with the trigger quoted, not a PASS with a caveat. Look for a pre-merge trigger: pull_request events, merge trains, or this forge's equivalent.

    • Status: PASS
    • Proof: .github/workflows/blocking-ci.yml is the single merge gate and triggers pre-merge: on: pull_request: {} with push: branches: [main] alongside it, described in the file as "the single entrypoint for checks that block a PR merge". It fans out to seven reusable workflows — bazel, blob-size-policy, cargo-deny, codespell, repo-checks, rust-ci, sdk — and aggregates them into a required job carrying if: ${{ always() }} with the comment "Without always(), GitHub skips this job after a failed dependency and a required check can appear successful instead of reporting the failure." Post-merge work is deliberately separated into .github/workflows/postmerge-ci.yml (rust-ci-full, v8-canary) rather than being mistaken for the gate.
    • Recommendation: —
    • Priority: High
  • 26. Does CI actually run the tests and the checks that exist in this repo? If no pipeline of any kind exists, FAIL with a one-line proof pointing at the CI item. Otherwise list what the pipeline runs and diff it against every check found in the Verification section — name each check that exists in the repo but is missing from the pipeline.

    • Status: PASS
    • Proof: Diffed the pipeline against every check found in this section. Tests: bazel.yml runs bazel test //... across a Linux/macOS/Windows matrix plus a 4-way sharded x86_64-pc-windows-gnullvm job, and rust-ci-full.yml runs the full nextest matrix post-merge. Formatter: rust-ci.ymlcargo fmt -- --config imports_granularity=Item --check. Linters: cargo_shear job in rust-ci.yml, argument_comment_lint_prebuilt across all three platforms, cargo-deny.yml, codespell.yml. Type/lint parity and the bespoke verifiers: repo-checks.yml runs verify_cargo_workspace_manifests.py, verify_tui_core_boundary.py, verify_bazel_clippy_lints.py, the scripts/codex_package and scripts/install unittest suites, the macOS notarization tests, asciicheck.py README.md and the README ToC check. Lockfile drift: bazel.yml runs ./scripts/check-module-bazel-lock.sh and the rusty_v8 checksum check. Benchmarks: just bench-smoke in rust-ci.yml. Every job also ends with the check-clean-worktree action, which catches generated files a change forgot to commit. Nothing found in the repo is missing from the pipeline.
    • Recommendation: —
    • Priority: High
  • 27. Does anything here get an LLM to read a change and go looking for problems — a review skill, a saved review prompt, or an AI reviewer on the pull requests? Look for a committed review skill or slash command, a CI step calling an AI reviewer, or bot config. Human review rules are a Safety item; this one is specifically about machine review.

    • Status: PASS
    • Proof: Machine review is committed and structured. .codex/skills/code-review/SKILL.md is an orchestrator that fans out one subagent per dimension at xhigh reasoning and requires a file path and line number on every finding; the dimensions are .codex/skills/code-review-breaking-changes/, code-review-change-size/, code-review-context/ and code-review-testing/, and they mirror the "Code Review Rules" section of AGENTS.md:85-131. On the forge side, .github/codex/labels/codex-review.md, codex-rust-review.md, codex-attempt.md and codex-triage.md are saved review prompts wired to label-triggered workflows, with .github/workflows/issue-labeler.yml and issue-deduplicator.yml running Codex against issues from a protected issue-triage environment.
    • Recommendation: —
    • Priority: Low

4. Tooling

  • 28. Can an agent reach the outside systems this project depends on, with that access committed to the repo rather than set up per laptop? MCP servers are one form: look for a committed .mcp.json or this ecosystem's equivalent. A CLI is another and often the better one — aws, gh, psql, kubectl, stripe, a vendor's own tool — and it counts when the repo names which tools the work needs and how to authenticate, so an agent is not guessing at a tool it cannot see. Judge the axis, not the mechanism: access that exists only in someone's shell history or laptop config is a FAIL, and so is a committed config that turns out to be gitignored, with the distinction in the proof. Project task scripts have their own item; this one is about reaching past the repo's edge. If this project genuinely talks to nothing outside itself, N/A with that as the reason.

    • Status: PASS
    • Proof: GitHub is the outside system, the CLI is the mechanism, and it is committed rather than living in a shell history: .codex/skills/babysit-pr/scripts/gh_pr_watch.py and .codex/skills/codex-issue-digest/scripts/collect_issue_digest.py both drive gh, each with a sibling test file, and .codex/skills/babysit-pr/references/github-api-notes.md documents the API surface they rely on. Authentication is gh's own login locally and GH_TOKEN: ${{ github.token }} in repo-checks.yml. .codex/environments/environment.toml commits the cloud dev-environment run command. There is no committed .mcp.json or equivalent — git ls-files | grep -iE 'mcp.json|\.mcp' returns nothing, and .github/codex/home/config.toml is two lines: model = "gpt-5.1" and the comment "# Consider setting [mcp_servers] here!" — so the MCP route is an acknowledged blank rather than a hidden dependency. The CLI route covers the work that actually happens here.
    • Recommendation: —
    • Priority: Low
  • 29. Is there a skills, commands or reusable-prompt library in the repo? Look for .claude/skills, .agents/skills, .claude/commands, or a prompts directory. Committed and pinned beats committed; note whether anything ties the copies to a source.

    • Status: PASS
    • Proof: .codex/skills/ holds 13 skills across 24 tracked files: babysit-pr, code-review plus four code-review-* dimensions, codex-bug, codex-issue-digest, codex-pr-body, path-types, pushing-ci-changes, remote-tests, test-tui, update-v8-version. Three carry per-agent config (agents/openai.yaml), two ship executable scripts with their own unit tests (test_gh_pr_watch.py, test_collect_issue_digest.py), and babysit-pr has a references/ directory with github-api-notes.md and heuristics.md. All committed and first-party, so there is nothing vendored to pin — no lock or manifest ties them to an upstream source, which is the right call here but worth noting. .claude/ and CLAUDE.md are gitignored, so this library is reachable by Codex-family agents only.
    • Recommendation: —
    • Priority: Low
  • 30. Do the skills, servers and tools that exist cover the work this team plainly repeats? First identify the repeated work from the README and the commit history. If skills, servers or documented tooling exist but miss it, FAIL naming the gap. If none exist at all and the repo plainly repeats work, FAIL pointing at the outside-systems item and the skills-library item. If the project is too small to repeat anything, N/A.

    • Status: PASS
    • Proof: The repeated work is legible from git log and the workflow set, and the skills line up against it almost one-to-one. Shepherding a PR through a seven-job blocking suite → babysit-pr (with gh_pr_watch.py and heuristics.md). Reviewing a change against the four house rules → code-review plus its four dimension skills. Issue triage at volume → codex-issue-digest plus the issue-labeler and issue-deduplicator workflows. Bumping V8, a recurring chore visible in v8-canary.yml, rusty-v8-release.yml, .github/scripts/rusty_v8_bazel.py and v8_canary_changes.pyupdate-v8-version. Cross-OS app-server/exec-server integration testing → remote-tests plus scripts/test-remote-env.sh. TUI testing → test-tui. Writing PR bodies → codex-pr-body. Filing a repro → codex-bug. Getting a CI-config push approved → pushing-ci-changes. No obvious repeated job is left uncovered.
    • Recommendation: —
    • Priority: Low
  • 31. Are there project CLI scripts or task-runner targets for the common jobs? Check the manifest's scripts, the Makefile, the justfile, or this ecosystem's equivalent. The test: does routine work need a raw multi-flag command that someone has to remember?

    • Status: PASS
    • Proof: justfile carries 40+ documented targets covering run (codex, exec, file-search, code-mode-host, mcp-server-run, tui-with-exec-server), check (fmt, fmt-check, fix, clippy, test, bazel-test, bazel-clippy, argument-comment-lint), codegen (write-config-schema, write-app-server-schema, write-hooks-schema), release (build-for-release), benchmarks (bench, bench-smoke, bench-e2e) and debugging (log, which tails the state SQLite database). Alongside it: package.json scripts for formatting, scripts/ as a proper Python package with pyproject.toml and uv.lock, .github/scripts/ for CI-side jobs, and tools/argument-comment-lint/ for the custom lint. Routine work is a named target throughout — the multi-flag Bazel invocations that would otherwise have to be remembered are wrapped, e.g. bench-e2e-smoke hides four --@rules_rust//... flags behind one name.
    • Recommendation: —
    • Priority: High
  • 32. Are those scripts named somewhere the agent will actually read them? If no scripts exist, FAIL with a one-line proof pointing at the task-scripts item. Otherwise check the agent instruction file, the README, and whether the manifest itself is self-explanatory.

    • Status: PASS
    • Proof: Named in all three places an agent looks. AGENTS.md names nine of them inline with the conditions for running each. docs/install.md repeats the core loop (just fmt, just fix -p <crate>, just test -p codex-tui, just test) and docs/contributing.md says to "Use the root just helpers so you stay consistent with the rest of the workspace". The runner is self-describing: just help runs just -l, and every target in justfile has a # doc comment above it that just -l prints.
    • Recommendation: —
    • Priority: High
  • 33. Can an agent get this project running — is there a reproducible environment or a documented setup path? Look for a pinned runtime (.nvmrc, .tool-versions, rust-toolchain), a lockfile, a container or nix file, and written setup steps. Try the first step if it is cheap and safe. This one bites hardest the moment work happens in a fresh git worktree or a new clone — the normal way to run agents in parallel. A new worktree has no installed dependencies, no .env, no build cache, so anything that works today only because of untracked state sitting on someone's machine simply does not run there. The test: would a bare checkout plus the written steps get this project up? Name any prerequisite nothing creates — an env file someone hand-made, a seeded database, a manual login — because each one is a wall a worktree hits on its first command.

    • Status: PASS
    • Proof: The pinning is comprehensive: codex-rs/rust-toolchain.toml (1.95.0 plus components), .bazelversion (9.0.0), package.json engines (node >=22, pnpm >=10.33.0) and a packageManager field carrying a sha512, and eight tracked lockfiles — codex-rs/Cargo.lock, tools/argument-comment-lint/Cargo.lock, pnpm-lock.yaml, .devcontainer/codex-install/pnpm-lock.yaml, MODULE.bazel.lock, flake.lock, scripts/uv.lock, sdk/python/uv.lock, sdk/python-runtime/uv.lock. Environments: flake.nix dev shell for all four Linux/macOS arches, and .devcontainer/ with two Dockerfiles, init-firewall.sh and post-start.sh. Written setup: docs/install.md gives the full sequence from git clone through rustup, cargo install --locked just / dotslash / cargo-nextest, cargo build, cargo run. Worktree angle: nothing here needs untracked hand-made state — no .env is tracked and none is required (the CLI reads ~/.codex/config.toml at runtime, documented in docs/config.md), no seeded database, no manual login for a build. The repo has explicitly thought about worktrees: .worktreeinclude lists user.bazelrc as the one untracked file a new worktree should carry over. The prerequisite a bare checkout does hit is the four cargo install tool fetches, and AGENTS.md:7 covers it ("Install any commands the repo relies on (for example just, rg, or cargo-insta) if they aren't already available"). I could not run the first step to confirm — no Rust toolchain on this machine.
    • Recommendation: —
    • Priority: High
  • 34. Can an agent see the results of a failed run — do the tools here produce output it can read and act on? Judge from the runs you already did in Verification: does a failure print a path, a line, a name — something actionable — or a wall of noise? If nothing could be run, FAIL saying why.

    • Status: PASS
    • Proof: Judged from the runs I did. verify_cargo_workspace_manifests.py failed with the exact file and the exact remedy — "codex-rs/code-mode/Cargo.toml: - remove the stale [features] exception from MANIFEST_FEATURE_EXCEPTIONS" — preceded by a paragraph explaining why the rule exists at all. verify_bazel_clippy_lints.py printed a one-line confirmation naming both files it compared. The unittest runs printed counts and status ("Ran 17 tests in 20.671s / OK"). Lint messages are written to be actionable by design: every entry in codex-rs/clippy.toml carries a reason that names the replacement, e.g. "Create SQLite pools through codex-state's sqlite shim" and "Use ANSI colors, which work better in various terminal themes". codex-rs/.config/nextest.toml writes junit.xml, and bazel.yml uploads execution logs as artifacts.
    • Recommendation: —
    • Priority: Low

5. Safety

  • 35. Are credentials kept out of the repo — nothing secret committed, ignore rules in place, an example env file for the shape? Three checks: grep tracked files for key-shaped strings, read the ignore rules for env and key patterns, and look for an example env file. Report each of the three separately.

    • Status: PASS
    • Proof: Three checks, separately. (a) Key-shaped strings in tracked files: git grep -nIE '(sk-[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{30,}|-----BEGIN (RSA|OPENSSH|PRIVATE) )' returns five hits, all deliberate test material — codex-rs/secrets/src/sanitizer.rs:48,52 ("Bearer sk-abcdefghijklmnopqrst+secret_suffix", "Bearer AKIAABCDEFGHIJKLMNOP/~secret_suffix" as sanitizer inputs), codex-rs/memories/write/src/phase1.rs:763,773 (a fake token plus the assertion that it is not serialized), codex-rs/agent-identity/src/lib.rs:832 (a test PEM). No live credential found. (b) Ignore rules: .gitignore carries .env* with a !.env.example negation, plus .venv/ and personal/. (c) Example env file: none tracked — the !.env.example negation has nothing to match. That sub-check is satisfied in this repo's own idiom instead: runtime configuration is ~/.codex/config.toml, and its shape is documented at length in docs/config.md and docs/example-config.md.
    • Recommendation: —
    • Priority: High
  • 36. Does anything scan for secrets automatically? Look for gitleaks, trufflehog, detect-secrets or this ecosystem's equivalent, wherever it is wired in — a CI step, a pre-commit hook, or forge-level push protection visible from the repo. CI is where this normally lives, and that is a PASS; a local hook on top is better, because it catches the key before it is pushed rather than after, but its absence is a line in the proof, not a FAIL. Say where the scan runs. "Nothing secret exists today" does not make this N/A — the scan is for the day that changes.

    • Status: FAIL
    • Proof: Nothing scans. grep -rilE 'gitleaks|trufflehog|detect-secrets|secret-scan|ggshield' .github scripts returns no hits. The seven jobs in blocking-ci.yml are bazel, blob-size-policy, cargo-deny, codespell, repo-checks, rust-ci, sdkcargo-deny covers advisories and licences, blob-size-policy covers blob size, neither looks at content for keys. There is no local hook either: no .pre-commit-config.yaml, no husky, no .githooks in git ls-files. Org-level GitHub push protection may well be enabled, but nothing visible from the repo shows it. This matters more here than in most repos, because the codebase deliberately contains key-shaped test fixtures (see the credentials item), so a real key would not stand out to a human skimming a diff.
    • Recommendation: Add a secret-scan job to .github/workflows/blocking-ci.yml next to codespell — a gitleaks action over the PR diff — and add codex-rs/secrets/src/sanitizer.rs, codex-rs/memories/write/src/phase1.rs and codex-rs/agent-identity/src/lib.rs to its allowlist so the known test fixtures do not make it noisy.
    • Priority: High
  • 37. Are dependencies pinned, so a build is reproducible? Look for lockfiles in every package of the repo, exact versions for load-bearing dependencies, a pinned runtime, and an install command that respects the lock (npm ci, not npm install).

    • Status: PASS
    • Proof: Eight lockfiles, all tracked: codex-rs/Cargo.lock, tools/argument-comment-lint/Cargo.lock, pnpm-lock.yaml, .devcontainer/codex-install/pnpm-lock.yaml, MODULE.bazel.lock, flake.lock, scripts/uv.lock, sdk/python/uv.lock, sdk/python-runtime/uv.lock. Runtime pinned by codex-rs/rust-toolchain.toml (1.95.0), .bazelversion (9.0.0), and package.json packageManager: pnpm@10.33.0+sha512.... Install respects the lock: repo-checks.yml runs pnpm install --frozen-lockfile, and just install runs cargo fetch against the committed lock. Above the bar in two more ways — every GitHub Action is pinned to a commit SHA rather than a tag (actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2), and package.json resolutions pins 16 transitive dependencies to exact versions (esbuild: 0.28.1, hono: 4.12.25, flatted: 3.4.2, ...). Drift is actively policed: just bazel-lock-check fails with "MODULE.bazel.lock is out of date", bazel.yml runs scripts/check-module-bazel-lock.sh, AGENTS.md:37-39 tells agents to run just bazel-lock-update in the same change, and cargo-deny (codex-rs/deny.toml) runs in the blocking suite.
    • Recommendation: —
    • Priority: High
  • 38. Is anything watching those dependencies for known vulnerabilities? Look for dependabot or renovate config, an audit step in CI, or this ecosystem's equivalent. Check every lockfile in the repo is covered, not just the root one.

    • Status: FAIL
    • Proof: Something is watching, but not everything. .github/dependabot.yaml covers six ecosystems weekly with a 7-day cooldown: bun (.github/actions/codex), cargo (codex-rs and codex-rs/*), devcontainers (/), docker (codex-cli), github-actions (/), rust-toolchain (codex-rs); cargo-deny adds advisory checking for the Rust workspace in blocking CI. Checked every lockfile against that list, and six of the nine are uncovered: the root pnpm-lock.yaml and .devcontainer/codex-install/pnpm-lock.yaml (no npm or pnpm ecosystem entry at all), all three Python locks — scripts/uv.lock, sdk/python/uv.lock, sdk/python-runtime/uv.lock — and tools/argument-comment-lint/Cargo.lock, which the codex-rs/* glob does not reach. The sdk/ gap is the sharpest one, because .github/workflows/python-sdk-release.yml publishes that tree to PyPI.
    • Recommendation: Add three blocks to .github/dependabot.yaml: package-ecosystem: npm for / and /.devcontainer/codex-install, package-ecosystem: uv for /scripts, /sdk/python and /sdk/python-runtime, and extend the cargo directories list with tools/argument-comment-lint.
    • Priority: Low
  • 39. Are the review rules written down — who reads a change, and what they check? Look for CONTRIBUTING, a PR template, or a review checklist in the agent instruction set. On a solo repo the "who" is N/A-shaped but the "what gets checked before it lands" still matters — judge that half.

    • Status: PASS
    • Proof: Written down in four places, and they agree. Who: docs/contributing.md ("One maintainer will be assigned as a primary reviewer", squash-and-merge, invitation-only, CLA flow) plus .github/CODEOWNERS, which assigns @openai/codex-core-agent-team to the core crates (core/, codex-mcp/, exec-server/, arg0/, prompts/), to the macOS signing paths, and to CODEOWNERS itself. What gets checked: .github/pull_request_template.md asks for What/Why/How plus a linked issue; docs/contributing.md lists the pre-PR bar (run all checks locally, atomic commits, tests that fail before and pass after); and AGENTS.md:85-131 is a full review checklist — keep crate API surfaces small, the six model-visible-context rules with "Highlight new individual items that can cross >1k tokens as P0", the five breaking-change surfaces to search, integration tests mandatory for agent-logic changes, and the 800-line/500-line change-size ceilings with staging guidance. The four .codex/skills/code-review-* skills encode the same list for machines.
    • Recommendation: —
    • Priority: Low
  • 40. Are the operations that need a human named somewhere an agent will read them? Look for a "never without asking" list in the agent instruction file or the README. Docs that hand out production commands with no fence around them count against, and the proof should quote one.

    • Status: FAIL
    • Proof: Several ask-first operations are named where an agent reads them: AGENTS.md:68 ("project-specific or individual tests can be run without asking the user, but do ask the user before running the complete test suite"), AGENTS.md:64 marks just fmt as explicitly not needing approval, .codex/skills/code-review/SKILL.md ("Do not leave GitHub comments unless explicitly asked"), and .codex/skills/pushing-ci-changes/SKILL.md explains that pushes touching .github/**/*.yml are rejected until a human obtains an approval the agent cannot request itself. What is missing is any fence around the one operation in this repo that ships software to the public: .github/workflows/rust-release.yml opens with the recipe, unqualified — "To release, follow a workflow like: git tag -a rust-v0.1.0 -m \"Release 0.1.0\" / git push origin rust-v0.1.0" — and neither AGENTS.md nor README.md nor docs/contributing.md says that pushing a rust-v* tag is a human-only act. There is no consolidated "never without asking" list anywhere in the instruction set.
    • Recommendation: Add a short "requires a human" block to AGENTS.md naming the three operations an agent must not perform unattended: pushing a rust-v* or python-v* tag (both trigger public signed releases), pushing changes under .github/** (see .codex/skills/pushing-ci-changes/SKILL.md), and running the full just test sweep.
    • Priority: High
  • 41. Does every action that spends money, destroys data or changes production have a human in the way? Start from the damage, not from the tooling: list what in reach of this repo could charge a card, drop or overwrite data, or alter what users are running. Then trace the shortest route an agent could take to each one — a push that auto-deploys, a script carrying live credentials, a migration that runs on merge, an infrastructure apply with no plan-and-approve step. PASS when every route meets a human first, whether that is a review, a manual trigger or a protected environment. FAIL when even one route runs start to finish unattended, and quote that route in the proof so the fix is obvious.

    • Status: PASS
    • Proof: Traced every route from the damage backwards. Public release of signed binaries: .github/workflows/rust-release.yml fires only on push: tags: rust-v*.*.* — a deliberate human act — and its header records that "Tag releases sign macOS binaries and DMGs through the protected codesigning GitHub environment and Azure Key Vault", so a protected environment gates the signing keys. CDN publish: .github/workflows/r2-release.yml is workflow_call only, never triggered directly, and runs in the protected environment: codex-r2-publisher. PyPI publish: python-sdk-release.yml on push: tags: python-v*, additionally fenced by if: github.repository == 'openai/codex'. Reaching main at all: blocking-ci.yml on pull_request with a required aggregate check, CODEOWNERS review on the core crates, and an invitation-only contribution policy. CI configuration: a push restriction on .github/**/*.yml that needs an out-of-band approval. postmerge-ci.yml runs on push to main but only runs tests and the V8 canary — it deploys nothing. There is no migration-on-merge, no terraform apply, no infrastructure apply anywhere in the repo. Every route meets a human.
    • Recommendation: —
    • Priority: High
  • 42. If a prompt injection landed tonight, how far would it reach — are the credentials an agent can get to here scoped to the job, with nothing production-grade in reach? Inventory what an agent in this repo can reach: env files, cloud CLI profiles, tokens named in docs or config, deploy commands that work from a laptop. Scoped-or-absent passes; production-grade reach fails with the item named.

    • Status: PASS
    • Proof: Inventory of what an agent in a local checkout can actually reach: no .env is tracked and none is required; no cloud CLI profile or service-account file is committed; .github/codex/home/config.toml holds only model = "gpt-5.1"; CI secrets are secrets: inherit inside GitHub Actions, reachable only from a workflow run, not from a checkout on disk; and the release path needs a signed tag plus a protected environment, so a laptop cannot deploy. The one real reach is gh authentication for the PR and issue skills — repo-scoped, and its most dangerous target (.github/**) is separately fenced by the CI-push restriction. Nothing production-grade is in reach. Worth noting that the product this repo builds is itself a sandbox: codex-rs/linux-sandbox, codex-rs/bwrap, Seatbelt integration, codex-rs/network-proxy, codex-rs/execpolicy, and AGENTS.md:8 forbids touching the sandbox environment variables at all.
    • Recommendation: —
    • Priority: High
  • 43. Can a change reach production a slice at a time — a feature flag that defaults to off, a canary, a staged rollout — rather than everyone at once? Look for a flag system and check the default, or canary and staged-rollout config in the deploy pipeline. Flags that need a rebuild to flip are worth naming in the proof — they gate exposure but they are not a kill switch.

    • Status: PASS
    • Proof: codex-rs/features/ is a real flag registry, not a boolean scattered through the code: src/lib.rs defines a Stage enum with UnderDevelopment ("still under development, not ready for external use"), Experimental (carrying a menu name, description and announcement, surfaced through the /experimental menu), Stable, Deprecated and Removed, and src/feature_configs.rs holds the per-feature TOML config types (CodeModeConfigToml, MultiAgentV2ConfigToml, NetworkProxyConfigToml, ...). Flags resolve from config-like inputs, so they flip at runtime through ~/.codex/config.toml or -c key=value on the command line — no rebuild needed, which is the kill-switch property this question is after. On the distribution side, rust-release.yml computes prerelease=true / make_latest=false for any version carrying a suffix after x.y.z, so an alpha ships to people who ask for it without becoming the default download, and v8-canary.yml gives the riskiest dependency bump its own canary lane.
    • Recommendation: —
    • Priority: High
  • 44. Once a change is live, can anyone see what it is doing — logs, metrics, traces, alerts that fire on their own, and can an agent read them too? Look for logging setup, an error tracker, analytics, alerting config — and then ask the second half: could an agent reach any of it (a CLI, an MCP server, an API named in the docs), or does observability stop at a dashboard behind a login?

    • Status: PASS
    • Proof: Instrumentation: codex-rs/otel/ (OTLP exporter, provider, metrics, events, trace_context.rs, targets.rs), codex-rs/analytics/, codex-rs/diagnostics/, and codex-rs/cli/src/doctor/ for self-checks; AGENTS.md:45-48 mandates #[tracing::instrument(...)] on definitions rather than spans at call sites. The second half also holds — an agent can read it without a browser: just log runs cargo run -p codex-cli --bin logs_client to tail the state SQLite database from the terminal, docs/install.md documents RUST_LOG and codex -c log_dir=./.codex-log followed by tail -F ./.codex-log/codex-tui.log, and codex-rs/.config/nextest.toml emits junit.xml. Server-side telemetry for OpenAI's own fleet is not visible from the repo, which is expected for a client-side CLI.
    • Recommendation: —
    • Priority: Low
  • 45. Is there a way back — can a bad change be undone without a rebuild and a redeploy, including the ones that touched a database or a queue? Look for a documented rollback path, a revert-and-redeploy story, down-migrations, or a flag that can turn the change off at runtime. Deployment docs that only say how to go forward are worth quoting.

    • Status: PASS
    • Proof: Three ways back, none needing a rebuild. Runtime: any feature can be turned off through ~/.codex/config.toml or -c key=value because codex-rs/features/ resolves flags from config at startup rather than at compile time. Version: README.md documents downloading a specific platform binary from any GitHub Release, and docs/install.md documents the DotSlash idiom for pinning an executable version in source control — "makes it possible to make a lightweight commit to source control to ensure all contributors use the same version of an executable". Release-level: make_latest=false on prereleases keeps a bad build from becoming the default download. Two honest gaps: there is no written rollback runbook — grep -ril 'rollback|revert' docs .github/workflows finds only docs/contributing.md ("Keep commits atomic... This makes reviews and potential rollbacks easier") — and the local state migrations under codex-rs/state/migrations/, goals_migrations/, logs_migrations/ and memory_migrations/ are forward-only numbered SQL files with no down-migrations, so downgrading to an older binary after a schema bump is untested territory.
    • Recommendation: —
    • Priority: Low
  • 46. Does a past failure leave a check behind — is there a test or a rule in here that exists because something broke once? Look for tests, lint rules or comments that name the incident they guard against — "regression", "this broke when", a linked issue. On a very young repo with no failures yet, N/A with the age as the reason.

    • Status: PASS
    • Proof: Failures leave checks behind, in both forms. Named regression tests: codex-rs/file-search/src/lib.rs:1075 ("Regression test for #3493: a parent directory's .gitignore with *"), codex-rs/tui/tests/suite/status_indicator.rs:1 ("Regression test: ensure that StatusIndicatorWidget sanitises ANSI escape"), plus regression coverage in codex-rs/core/tests/suite/mcp_auth_refresh.rs, codex-rs/app-server/tests/suite/v2/remote_thread_store.rs and realtime_conversation.rs. Whole checks that exist because something broke: .github/scripts/verify_tui_core_boundary.py (codex-tui must not import codex-core), verify_bazel_clippy_lints.py ("Without that opt-in, cargo clippy can miss violations that Bazel clippy catches"), blob-size-policy.yml with its .github/blob-size-allowlist.txt, just bazel-lock-check, the check-clean-worktree action on every CI job, scripts/mcp_conformance/regression-baseline-v1.json with review_regressions.py, and every reason string in codex-rs/clippy.toml — the sqlx pool bans and "Audited against workspace sqlx 0.9.0. Revisit this SQLite escape-hatch list when bumping sqlx" read as scar tissue. codex-rs/.config/nextest.toml sets retries = 1 with the comment "Retry once so one transient failure does not fail full-CI outright."
    • Recommendation: —
    • Priority: Low

Learn more: https://ainativesoftware.engineering/

Get one for your own repo

Nothing here is special to openai/codex. Install the skill and ask your agent to run it — it answers the same 46 checks and writes the same file into your repo.

npx skills add alfonsograziano/skills --skill aine-readiness-analyzer

AI-Native Software Engineering