Skip to content

Codex generates API references, architecture documents, inline TSDoc, and changelog drafts by reading source files and git history, working in an isolated Git worktree so nothing reaches the main checkout unreviewed. Generation is the easy half. Documentation stays true when deterministic gates check it and a scheduled codex exec drift report returns schema-checked JSON that CI can fail on.

This page is for developers who own a service’s documentation and tech leads who want docs to stop being a review burden. The situation is familiar: docs/API.md still lists an endpoint removed two sprints ago, the README’s setup command fails on a clean machine, and every new joiner asks the same questions because ARCHITECTURE.md describes the system as it was before the last refactor. Nobody updates docs by hand, so the fix is a workflow that regenerates them and a gate that fails when they drift.

  • A decision table: which documents Codex should generate, which it should only draft, and the check that proves each one.
  • An AGENTS.md section and a repository skill that make every Codex session write docs the same way.
  • Copy-paste prompts for an API reference with a contract test, a cited ARCHITECTURE.md, and a changelog draft.
  • A read-only drift report from codex exec --output-schema that turns “the docs look fine” into an exit code.
  • A failure-mode list with the recovery for each one.

Before you start, have Codex installed and an AGENTS.md at the repository root; setting up AGENTS.md for Codex covers the basics. Commands on this page were checked against Codex CLI 0.157.1.

Codex is reliable wherever the answer is in the code and unreliable wherever the answer is in someone’s head. Decide per document type before you prompt:

DocumentSource of truthCodex’s roleWhat proves it is right
TSDoc / JSDoc on exported symbolsSignatures, types, thrown errorsGenerateTypeDoc validation fails on any undocumented export
API reference (openapi.yaml or API.md)Route handlers, validation schemas, middlewareGenerateRedocly lint plus a contract test that compares documented routes with registered routes
ARCHITECTURE.mdDirectory layout, entry points, data flowGenerate with citationsA script checks that every cited path exists
README setup sectionScripts that actually runGenerate, then executeCI runs the setup commands from a clean checkout
Changeloggit log between tagsDraftThe release owner edits the wording and signs off
Decision records (“why we chose X”)People’s reasoningInterview and draft onlyThe decision owner writes or approves every rationale

The last row matters most. Asked why a module uses a queue, Codex writes a plausible reason whether or not one exists. Keep rationale with people, using the interview workflow in architecture decisions agents can follow.

Add the checks before the first generated document, so the first diff is already verified. For a TypeScript service, install TypeDoc and the Redocly CLI (0.28.20 and 2.54.3 on npm on 2026-09-26):

Terminal window
npm install -D typedoc @redocly/cli

Then add one docs:check script to package.json. --emit none validates without writing HTML, and --treatValidationWarningsAsErrors makes TypeDoc exit non-zero when an exported symbol under src/ has no documentation:

{
"scripts": {
"docs:check": "typedoc --emit none --entryPointStrategy expand --entryPoints src --validation.notDocumented --treatValidationWarningsAsErrors && redocly lint docs/openapi.yaml && bash scripts/check-doc-citations.sh"
}
}

The citation check catches the commonest form of invented prose: a claim that cites a file that does not exist. Save it as scripts/check-doc-citations.sh:

#!/usr/bin/env bash
# Fail when a doc cites a repository path that does not exist.
# A line suffix such as `src/routes/billing.ts:42` is checked as the bare path.
set -uo pipefail
missing=0
for doc in docs/ARCHITECTURE.md docs/API.md README.md; do
[ -f "$doc" ] || continue
for path in $(grep -oE '`(src|scripts|tests|docs|config)/[A-Za-z0-9_./-]+(:[0-9]+)?`' "$doc" \
| tr -d '`' | sed -E 's/:[0-9]+$//' | sort -u); do
if [ ! -e "$path" ]; then
echo "$doc cites a missing path: $path"
missing=1
fi
done
done
exit "$missing"

The three gates are chained with &&, so npm run docs:check stops at the first one that fails, and redocly lint fails anyway until docs/openapi.yaml exists. To build the backlog, run them separately once and keep every failure:

Terminal window
npx typedoc --emit none --entryPointStrategy expand --entryPoints src --validation.notDocumented --treatValidationWarningsAsErrors; \
npx redocly lint docs/openapi.yaml; \
bash scripts/check-doc-citations.sh

That list is the backlog you hand to Codex, one directory per task. The TypeDoc and README-execution gates are tool-independent; the Claude Code documentation guide shows a CI step that runs a README’s quick-start block in a clean container.

Codex reads AGENTS.md before it does any work, so conventions written there apply to every session, including worktree threads and scheduled runs. Add a short section to the root AGENTS.md:

## Documentation
- API reference: docs/openapi.yaml. Architecture: docs/ARCHITECTURE.md. Changelog: CHANGELOG.md.
- Cite every claim about the code as a repo-relative path in backticks, for example `src/routes/billing.ts`.
- Never write rationale ("we chose X because") that is not in a code comment, docs/adr/ or the prompt.
Write "(rationale: ask the owner)" instead.
- In hand-written docs, edit only between <!-- generated:start --> and <!-- generated:end -->.
- Before finishing any change to docs/ or to an exported API, run `npm run docs:check` and fix failures.

Keep it to rules that apply to every task. The multi-step procedure for a full documentation pass belongs in a skill, which Codex loads only when a task needs it. Save this as .agents/skills/docs-sync/SKILL.md:

---
name: docs-sync
description: Bring docs/ back in line with the code after a change. Use when a task changes routes, exported APIs, scripts or directory layout.
---
1. Run `git diff --name-only origin/main...HEAD` and list the changed routes, exports and scripts.
2. For each change, find every place in docs/, README.md and docs/openapi.yaml that describes it.
3. Update only those passages. Cite the file each claim comes from.
4. Run `npm run docs:check`. Fix documentation, never code, until it passes.
5. Report: each doc changed, the code change that caused it, and the final docs:check output.

Invoke it with $docs-sync in a prompt, or browse installed skills with /skills. If the skill does not appear, optimizing AGENTS.md and skills for Codex shows how to see exactly what Codex loaded.

Generate reference docs in a worktree task

Section titled “Generate reference docs in a worktree task”

A worktree keeps generated files away from your checkout until you have checked them, and lets a docs task run beside your feature work.

  1. Start the task in its own worktree. Commit or stash first: a worktree starts from committed state, so uncommitted edits are not in it.

    Terminal window
    codex --worktree

    --worktree runs the session in a new managed Git worktree (worktree support is on by default since Codex CLI 0.156.0).

  2. Install dependencies inside the worktree (npm ci), or the gates fail for a reason that has nothing to do with the docs.

  3. Paste one prompt below, one document or one directory per task. Small tasks give diffs you can spot-check in a minute.

  4. Let the gates decide. The prompt ends by running npm run docs:check. Green means every export is documented, the spec parses and every citation resolves. It does not mean the prose is true, which is why the contract test exists.

  5. Run a review pass on the diff. Type /review in the session, or run codex exec review --uncommitted for the built-in review. To apply the documentation-review instructions below, run them as a read-only codex exec (see the review section).

  6. Bring the work home through Git. Commit in the worktree and open a pull request. Isolated Codex tasks with Git worktrees covers merging back and cleaning up.

From then on the contract test is the oracle: a route added without a spec entry turns CI red, whoever added it. The rest of the contract-first API workflow is in building APIs with Codex.

The “Drift found” list is often worth more than the document: it is the audit of the docs you already have. Check every (inferred) marker; each one is a claim no file backs.

For TSDoc, point Codex at the TypeDoc warnings for one directory and add two constraints: “follow the style already used in src/lib/database.ts” and “keep each comment shorter than the function body it documents”. The second curbs the most common failure of generated comments, which is restating the code.

A changelog is a communication to users, so Codex drafts it and a person owns the wording. Run this in a normal local thread; it only reads history and writes one file.

The “Not user-facing” list makes the agent’s classification checkable. The release owner reads that list and the breaking changes, not every entry.

Generated docs are true on the day they are generated. The drift report is what keeps them true: a read-only codex exec run that compares docs with code and returns JSON in a fixed shape.

Save the schema as scripts/docs-drift/schema.json. --output-schema runs in strict mode, so every object lists all its properties in required and sets additionalProperties to false:

{
"type": "object",
"properties": {
"verdict": { "type": "string", "enum": ["current", "drift"] },
"findings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"doc": { "type": "string" },
"claim": { "type": "string" },
"code_evidence": { "type": "string" },
"kind": { "type": "string", "enum": ["missing", "stale", "wrong", "dead-reference"] }
},
"required": ["doc", "claim", "code_evidence", "kind"],
"additionalProperties": false
}
}
},
"required": ["verdict", "findings"],
"additionalProperties": false
}

Save the prompt as scripts/docs-drift/prompt.md:

Run it from the repository root and let jq -e turn the answer into an exit code:

Terminal window
codex exec -c default_permissions=":read-only" \
--output-schema scripts/docs-drift/schema.json -o drift.json \
"$(cat scripts/docs-drift/prompt.md)"
jq -e '.verdict == "current"' drift.json > /dev/null || {
jq -r '.findings[] | "\(.doc): \(.claim) <- \(.code_evidence)"' drift.json
exit 1
}

:read-only is a built-in permission profile (beta since Codex CLI 0.138.0); the legacy equivalent is --sandbox read-only. Use one or the other, not both, because OpenAI states that the two systems do not compose. The schema guarantees the shape of the answer, not its truth: the code_evidence field is there so you check a finding by opening one file, not by rereading the docs.

Create an automation in the Codex desktop app with the prompt above, pick the project, and schedule it weekly. It runs only while your machine is awake and reports to the app’s inbox. Test the prompt twice by hand before you schedule it; the scheduled Codex automations guide covers the procedure and sandbox choice.

When the report shows drift, start a worktree task with $docs-sync and paste the findings into the prompt. The fix then goes through the same gates as any other docs change.

Review a docs diff without reading every line

Section titled “Review a docs diff without reading every line”

Documentation diffs are long and look plausible, which makes them the worst kind of diff to read line by line. Put the reading on the gates and a focused review pass, and keep human attention for the claims that no gate can check.

Save it as scripts/docs-drift/review.md and run it as a read-only codex exec, which reads the diff the prompt names:

Terminal window
codex exec -c default_permissions=":read-only" "$(cat scripts/docs-drift/review.md)"

For the built-in review without custom instructions, run codex exec review --base main. In Codex CLI 0.157.1, --base, --uncommitted and --commit cannot be combined with custom instructions; the CLI exits with “cannot be used with ‘[PROMPT]’”.

Who signs off stays explicit:

ChangeChecked bySigns off
TSDoc, OpenAPI, citationsnpm run docs:check, contract testCode owner of the touched paths, from the gate results
ARCHITECTURE.mdCitation check, (inferred) markers, review passTech lead reads the “Drift found” list and every (inferred) line
ChangelogHumanRelease owner
Rationale and decision recordsHumanDecision owner

For the evidence a pull request should carry so the reviewer can approve from it, see the evidence bundle.

When Codex-generated documentation goes wrong

Section titled “When Codex-generated documentation goes wrong”

The docs explain why, and the reasons are invented. Codex fills gaps with plausible rationale. Recovery: the AGENTS.md rule above makes it write “(rationale: ask the owner)” instead; search the diff for “because” and “in order to” and confirm each one has a source.

Codex rewrites hand-written sections. A full-file regeneration flattens the paragraphs a person wrote. Recovery: wrap generated parts in <!-- generated:start --> and <!-- generated:end -->, state the rule in AGENTS.md, and add a check that fails when a diff touches lines outside the markers.

The gates fail in the worktree for reasons unrelated to the docs. A fresh worktree has no node_modules and none of your gitignored local files. Recovery: run npm ci first, or add the install to the worktree setup described in the worktrees guide.

Codex ignores your documentation rules. Since Codex CLI 0.150.0, untrusted projects no longer supply project-level AGENTS.md instructions. Recovery: trust the project when Codex asks, then confirm the rules loaded with codex debug prompt-input.

The drift report changes every week without any code change. A vague prompt produces a different list on each run. Recovery: run it twice by hand; if the findings differ, narrow the scope (one doc per check) and require code_evidence for every finding, which the schema already enforces.

The drift job “fixes” the docs itself. A scheduled job that writes is a job whose changes nobody asked for. Recovery: keep the report on :read-only, and make fixing a separate worktree task that goes through review.

File citations break after a move. Renaming src/services/ to src/domain/ silently invalidates every citation. Recovery: the citation check fails the build; point Codex at its output with “update the citations in docs/ to the new paths, do not change code”. The citation check does not follow relative Markdown links such as [setup](../setup.md); those need a link checker in CI, for example markdown-link-check (3.15.0 on npm): find docs -name '*.md' -print0 | xargs -0 -n1 npx markdown-link-check, with redocly lint covering references inside the OpenAPI spec.

Where to go next with documentation in Codex

Section titled “Where to go next with documentation in Codex”