Skill v1.0.1
currentAutomated scan100/1005 files
version: "1.0.1" name: doc-review description: "Document review via Codex MCP. Use when: reviewing .md docs, tech spec audit, document quality check. Not for: code review (use codex-code-review), test review (use test-review). Output: 5-dimension rating table + gate." allowed-tools: mcp__codex__codex, mcp__codex__codex-reply, Bash(git:*), Read, Grep, Glob context: fork agent: Explore
Document Review Skill
Trigger
- Keywords: review doc, document review, tech spec review, review-spec, doc-refactor, streamline doc
When NOT to Use
- Code review (use
codex-code-review) - Test coverage review (use
test-review) - Just want to read a document (use Read directly)
Commands
| Command | Description | Use Case | |
|---|---|---|---|
/codex-review-doc | Codex reviews .md docs | Document changes | |
/review-spec | Review tech spec | Spec confirmation | |
/doc-refactor | Streamline documents | Doc too long | |
/update-docs | Research & update docs | After code change |
Workflow: /codex-review-doc
Target set → Deterministic checks → Resolve profiles + batches → Codex review per batch → Rating table + Gate → Loop
All changed `.md` in one change are one review plan. The plan is the unit; it holds one or more physical batches, and within the budget it is exactly one batch and therefore one dispatch. Reviewing file-by-file is what this workflow replaced — it multiplied a three-file change into three whole- document reviews.
Step 1: Determine the Target Set
| Condition | Action | |
|---|---|---|
| Paths specified | Use them — all of them, as one plan | |
| No path | git diff --name-only HEAD and untracked, filtered to .md | |
| Nothing changed | Report it and stop; there is no document to review |
Never narrow a multi-file change to one file, and never ask the user to pick one. A file the plan drops is a file nothing reviewed.
Step 2: Deterministic Checks First
node scripts/check-doc-links.js --root "$(git rev-parse --show-toplevel)" <changed .md paths>
Resolves the repo-local file links it can classify, prints the ones that do not resolve, and prints unresolved — how many link shapes it declined to classify. Heading fragments are out of scope: [x](#frag) is dropped uncounted the way an external URL is, and [x](./a.md#frag) is checked as a link to a.md alone. A dead #fragment is therefore not a finding this step establishes, and the reviewer is free to raise one.
Scan only the paths that exist in the working tree. A deleted .md (the resolver reports it deleted: true) is omitted from this scan — passing it produces an unreadable failure that hands the reviewer a defect when the deletion is the change. Its review copy is git show HEAD:<path>, and the prompt says so per file.
node is deliberately not in allowed-tools. allowed-tools is pre-approval, not a capability boundary — an omitted tool stays reachable through the normal permission flow, so Steps 2 and 3 ask for permission when they run. What the omission buys is that a node invocation this workflow never names cannot run silently under a review's grant. Advisory input, not a gate: it always exits 0, and its output is fed to the reviewer as findings already established so the LLM does not spend a pass rediscovering them. markdownlint does not resolve links, so nothing else answers this.
Pass both fields to the prompt, and never `failures` alone. It is a scanner, not a CommonMark parser — this repository ships zero dependencies — so failures: [] settles the link question only alongside unresolved: 0. With unresolved > 0 that many link shapes went unchecked, and saying "already settled" over them is the one way this advisory input can cost a review rather than save one.
Step 3: Resolve Profiles and Batches
node scripts/resolve-review-profile.js --tier <effective tier> --files <a.md,b.md> --root "$(git rev-parse --show-toplevel)"
Emits a per-file profile with the reasons it is not shallower, plus the batch plan. Richer inputs — the ## sections a shallow profile is confined to, and whether code landed with the change — go in via --plan <file|->, a JSON document of the same shape the resolver prints:
{ "tier": "standard", "code_changed": false,"files": [ { "path": "docs/features/x/2-tech-spec.md","profile": "living-sync", "sections": ["3. Design"] } ] }
Three things this step decides, and none of them is negotiable afterwards:
- The profile is resolved before the prompt is built. A shallow prompt for a change that did not
earn one is never assembled, so there is no mismatch to detect afterwards and nothing to poison.
- Escalation is one-way and per file. One file escalating raises that file's questions and the
batch's shared dimensions; it never withdraws another file's record-diff exemption.
- An over-budget plan splits loudly. Say which batches were produced and why, then dispatch each.
Never claim one dispatch you did not make, and never drop a file to fit.
Step 4: Codex Review, One Dispatch Per Batch
First review: mcp__codex__codex with the doc review prompt. See references/codex-prompt-doc.md.
Config: sandbox: 'read-only', approval-policy: 'never'
Save the returned `threadId` — one per batch.
Loop review: mcp__codex__codex-reply with the re-review template. See references/review-loop-doc.md.
Stop cat-ing whole existing files into the prompt. Codex has sandbox access; the prompt carries the file list, each file's profile, and what that profile says to read.
Step 5: Consolidate Output
Organize results into rating table + severity-grouped findings + gate. One gate for the plan: a batch that comes back ⛔ Needs revision blocks the plan.
The conjunction is behaviour-layer, and the state slot cannot hold it. The reminder state (hook-lightweighting § 3.2) stores one doc_review note and a later note overwrites it — last write wins, whatever an earlier batch said. Hold the conjunction yourself: fix and re-dispatch every blocked batch (references/review-loop-doc.md § Loop Rules), and call the plan Mergeable only when the latest dispatch of every batch passed — never because the final dispatch happened to. Then self-note the plan's verdict once, not per batch:
CHECKER=".claude/scripts/review-state.js"; [ -f "$CHECKER" ] || CHECKER="scripts/review-state.js"node "$CHECKER" note doc_review pass # every batch's latest dispatch passednode "$CHECKER" note doc_review fail # any batch still blocked — increments the rounds count
The note is the declared-provenance record the reminder hooks read; it is advisory, binds to the current tree digest (a later .md edit re-opens the plane by construction), and a failed note never fails the review — the cost is one redundant reminder line.
Review Profiles
Resolved by scripts/resolve-review-profile.js, never chosen by hand at dispatch time.
| Profile | Used when | Reviewer reads | Questions | |
|---|---|---|---|---|
full-design | Design landing pre-implementation; unknown classification; security / data-integrity; any escalation | Whole changed document + linked design context | All five dimensions | |
implementation-sync | Current-authority doc updated after code landed | Changed hunks + enclosing ## sections + preamble + link definitions | Does any reviewed section contradict the implementation? Is any affected cross-reference dead? | |
living-sync | Current-authority doc, doc-only edit | Changed sections | Accuracy and internal consistency | |
record-diff | Design / work / history record | Changed hunks | Is the edit internally coherent and correctly marked as a record? No code-alignment obligation | |
executable | Instruction surfaces (skills/**, rules/**) | Changed sections + the file's own contract | Does the instruction still execute? Any conflicting directive? |
New (untracked) files are read whole under any profile — every line is new.
The profile narrows what the reviewer reads, never whether review runs, and no resolver outcome auto-passes anything (Anchor Register #5, #6). Contract and escalation table: docs/features/doc-review-phasing/2-tech-spec.md § 3.3–3.4.
Review Dimensions
| Dimension | Checks | |
|---|---|---|
| Architecture Design | System boundaries, responsibilities, dependencies, extensibility | |
| Performance | Bottlenecks, concurrency, caching, resource usage | |
| Security | Data leakage, access control, input validation, error handling | |
| Documentation Quality | Structure, completeness, accuracy, examples, docs-writing standards | |
| Code Consistency | Pseudocode matches codebase, referenced files exist, technical accuracy |
Review Loop
⚠️ @CLAUDE.md auto-loop: fix → re-review → ... → ✅ PASS ⚠️
⛔ Needs revision → fix 🔴 items → /codex-review-doc --continue <threadId> → repeat until ✅ Mergeable.
The round budget is the tier's cap (fast — docs are the tier's primary case — caps at 6; an explicit ## Max Rounds in rules/auto-loop-project.md overrides it, per rules/auto-loop.md § Tiers). Still failing at the cap → report blocker.
🔴 only. 🟡 and ⚪ are non-blocking: log them and proceed.
[NIT_DEFERRED] file:line | issue | reason: sub-threshold-doc | <ISO8601>
That tag and field order are a reporting convention — nothing parses or persists the line (hook-lightweighting § 3.3: the nit-history store retired with the hook that owned it). The durable record is the review report and the conversation, where the line is greppable; keep the fixed field order for exactly that grep. references/codex-prompt-doc.md asks Codex for a ### Deferred Findings section so the report itself carries the deferrals.
Do not batch-fix 🟡/⚪ and re-review to confirm — that spends a round on findings the gate already declared non-blocking. The two exceptions are the same as for code (@rules/auto-loop.md § Sub-Threshold Findings): a one-line fix in a file already open, and a mis-marked security / data-integrity issue that should have been 🔴.
What counts as 🔴 is pinned in references/codex-prompt-doc.md § Severity Calibration — it is the reviewer prompt, not this file, that keeps the loop short.
Verification
- [ ] Each issue tagged with severity (🔴/🟡/⚪)
- [ ] Gate is clear (✅ Mergeable / ⛔ Needs revision)
- [ ] Codex verified code-documentation consistency independently
Required Actions
| Change Type | Must Execute | |
|---|---|---|
.md docs | /codex-review-doc or /review-spec | |
| Tech spec | /review-spec | |
| README | /codex-review-doc |
References
- Doc review prompt:
references/codex-prompt-doc.md - Review loop:
references/review-loop-doc.md - Profile resolver:
scripts/resolve-review-profile.js— profiles, escalation, batch plan - Link checker:
scripts/check-doc-links.js— advisory deterministic input. Reportsfailures
and unresolved: it is a scanner, not a CommonMark parser, and failures: [] settles the link question only when unresolved is 0
- Standards: @rules/docs-writing.md
Examples
Input: /codex-review-doc docs/features/xxx/2-tech-spec.mdAction: Link check → resolve profile → Codex doc prompt scoped to the changed sections → Rating table + Findings + GateInput: /codex-review-docAction: Collect every changed .md → link check → one plan, one batch, one dispatch → Rating table + GateInput: /codex-review-doc (a 25-file feature folder)Action: Resolver splits the plan loudly into batches, each within budget → one dispatch per batch → one consolidated gateInput: Review this tech spec for meAction: /review-spec → Check completeness/feasibility/risks → Output GateInput: This document is too long, streamline itAction: /doc-refactor → Tabularize + Mermaid → Output comparison