Code Review Checklist
shareAI-lab/learn-claude-code
Reviews code against a five-part checklist covering security, correctness, performance, maintainability and testing, and reports findings in a fixed format.
Implement features via Codex exec. An agent skill from sd0xdev/sd0x-harness.
$ npx skills add sd0xdev/sd0x-harness --skill codex-implement -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install sd0xdev/sd0x-harness codex-implement --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/sd0xdev/sd0x-harness.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/codex-implement .claude/skills/codex-implement && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "codex-implement" agent skill from https://github.com/sd0xdev/sd0x-harness/tree/main/skills/codex-implement into .claude/skills/codex-implement/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "codex-implement", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/sd0xdev/sd0x-harness/tree/main/skills/codex-implementType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add sd0xdev/sd0x-harness --skill codex-implement -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install sd0xdev/sd0x-harness codex-implement --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/sd0xdev/sd0x-harness.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/codex-implement .agents/skills/codex-implement && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "codex-implement" agent skill from https://github.com/sd0xdev/sd0x-harness/tree/main/skills/codex-implement into .agents/skills/codex-implement/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "codex-implement", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add sd0xdev/sd0x-harness --skill codex-implement -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install sd0xdev/sd0x-harness codex-implement --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/sd0xdev/sd0x-harness.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/codex-implement .cursor/skills/codex-implement && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "codex-implement" agent skill from https://github.com/sd0xdev/sd0x-harness/tree/main/skills/codex-implement into .cursor/skills/codex-implement/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "codex-implement", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/sd0xdev/sd0x-harness.git --path skills/codex-implement--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add sd0xdev/sd0x-harness --skill codex-implement -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install sd0xdev/sd0x-harness codex-implement --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/sd0xdev/sd0x-harness.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/codex-implement .gemini/skills/codex-implement && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "codex-implement" agent skill from https://github.com/sd0xdev/sd0x-harness/tree/main/skills/codex-implement into .gemini/skills/codex-implement/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "codex-implement", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install sd0xdev/sd0x-harness codex-implementInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add sd0xdev/sd0x-harness --skill codex-implement -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/sd0xdev/sd0x-harness.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/codex-implement .github/skills/codex-implement && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "codex-implement" agent skill from https://github.com/sd0xdev/sd0x-harness/tree/main/skills/codex-implement into .github/skills/codex-implement/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "codex-implement", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add sd0xdev/sd0x-harness --skill codex-implement -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install sd0xdev/sd0x-harness codex-implement --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/sd0xdev/sd0x-harness.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/codex-implement .opencode/skills/codex-implement && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "codex-implement" agent skill from https://github.com/sd0xdev/sd0x-harness/tree/main/skills/codex-implement into .opencode/skills/codex-implement/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "codex-implement", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
codex-implementImplement features via Codex exec. An agent skill from sd0xdev/sd0x-harness.
Codex Implement is an agent skill from sd0xdev/sd0x-harness. Implement features via Codex exec. Use when: writing new code from specs, implementing features, Codex-driven development. Not for: code review (use codex-code-review), architecture advice (use codex-architect). Output: implemented code + review loop.
Its SKILL.md is about 8.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files, including reference files (for example `references/codex-prompts.md`).
It sits in Development, covering Code review. It works with Git. The repository describes itself as: The harness layer for Claude Code — a reference implementation of harness engineering with hook-enforced dual review, state-machine gates that survive context compaction, and… The licence is MIT.
6 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit c9a2036. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
Bash(git:*)ReadGrepGlobEditWriteAskUserQuestionSkillAgentBash(node:*)From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitnodecodexFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
API_TOKENFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Codex Implement loads about 8.7k tokens when it runs, and up to ~9.9k if it reads all its reference files. Until then it costs about 67 tokens; SKILL.md has 2,972 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
e files are not source, and it is where `.env`, credentials, key material and logs live —this repository ignores `.env` itself.// these are not source, and it is where .env, credentials, keys and logs live.Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from sd0xdev/sd0x-harness at commit c9a2036, republished under its MIT licence (© sd0xdev). 2,972 words, ~8,687 tokens.
.claude/skills/codex-implement/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub./codex-architect)/codex-review-fast)/bug-fix)Parse args → Decompose → Collect context → Iterate items → Review loop → Done
↕
codex → diff → confirm
↕
reject/modify → § ResumeThe index must carry no assume-unchanged / skip-worktree path before anything is dispatched
— under the two grants this skill holds, Bash(git:*) and Bash(node:*), and no others:
node -e '
const { execSync } = require("child_process");
// The REPOSITORY, not the invocation directory. `git ls-files` is path-limited to the cwd
// subtree, so running this from `skills/` would inspect a fraction of the index and report a
// clean tree while a flagged file sat elsewhere — and the dispatch runs Codex at the top level
// regardless of where this skill was invoked.
const root = execSync("git rev-parse --show-toplevel").toString().trim();
// `git ls-files -v` tags every cached path. Measured on git 2.55.0: `H` is the ordinary cached
// entry, `S` is skip-worktree, and `-v` LOWERCASES the tag of an assume-unchanged file — so `h`
// is assume-unchanged and `s` is both bits. Hidden state is therefore "tag is `S`, or the tag is
// lowercase"; testing only for lowercase misses plain skip-worktree, and testing for "not `H`"
// over-triggers on `M`, an unmerged entry, whose remedy is finishing the merge and which neither
// `--no-assume-unchanged` nor `--no-skip-worktree` would touch.
const lines = execSync("git ls-files -v", { cwd: root, maxBuffer: 1 << 28 }).toString()
.split("\n").filter(Boolean);
const hidden = lines.filter((l) => /^[a-z] /.test(l) || l.startsWith("S "));
const unmerged = lines.filter((l) => /^[Mm] /.test(l));
console.log(hidden.length ? hidden.join("\n") : "(none)");
if (unmerged.length) console.log("[UNMERGED — finish the merge first]\n" + unmerged.join("\n"));
'A hidden-state tag is an assume-unchanged or skip-worktree bit, and such a path is invisible three
times over: the precondition below cannot see a local edit to it, Step 3b cannot display one, and
Step 3b's Reject would treat it as baseline-absent and run git checkout -- <path> — which restores
the index version and destroys the edit for good. That is data loss with no recovery path, so it
is a hard stop rather than an opt-out: list the flagged paths, ask the user to clear the bit
(git update-index --no-assume-unchanged <path> / --no-skip-worktree <path>) or to commit or stash
the work, and re-run this step. Never auto-revert a tracked path whose prior contents were not
captured — the Reject row's "no state to lose" holds only for paths the baseline could actually
see.
Then resolve the adapter through @skills/codex-code-review/references/codex-transport.md
§ Locator, and let any auto-install that section prescribes happen before the git status below —
in a consuming repository its second step writes the adapter into the tree, and a write after the
snapshot puts an untracked file there that the changeset this step captured does not contain. Same reason the rest of this step is ordered the way it is: a snapshot that goes stale
while the work runs describes a tree nobody is looking at.
Then git status --porcelain --untracked-files=all --ignored, and satisfy the precondition in
§ Step 3a before Step 1 reads anything. Step 1 parses the spec, loads the feature intent and derives the items
from files the precondition may ask the user to stash or remove; running it first leaves the plan —
and later the dispatched prompt — describing a tree that no longer exists. Three review rounds moved
this earlier twice: it began inside Step 3, then before Step 2, and neither was early enough because
each time a reader still hit a repository read first. It is a numbered step so the order is
executable rather than asserted from inside a later one.
If cleanup happens after this step for any other reason, repeat the Step 1 reads and plan confirmation, and rebind the Step 2 context, before dispatching.
Then pin the redactor Step 3b will run, by digest, before Codex is dispatched. Step 3b executes
that module (require), and by then the tree has been through a write-capable child — so no check
made afterwards can establish what the module was: a child that sets assume-unchanged on a
tracked file and then overwrites it leaves git status empty, which is exactly the invisibility this
step's own probe exists to catch, and a path swapped for a symlink resolves somewhere else entirely.
Authenticating the bytes now and requiring the same bytes later is the only order that works.
node -e '
const fs = require("fs"), path = require("path"), crypto = require("crypto");
const { execSync, execFileSync } = require("child_process");
const root = fs.realpathSync(execSync("git rev-parse --show-toplevel").toString().trim());
const real = (p) => { try { return fs.realpathSync(p); } catch { return null; } };
const inside = (p) => p === root || p.startsWith(root + path.sep);
const dirs = (d) => { try { return fs.readdirSync(d, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path.join(d, e.name)); } catch { return []; } };
// Candidates: the named active installation, the two in-repo locations, and a bounded walk of the
// plugin tree (layouts vary: cache/<marketplace>/<plugin>/<version>/, marketplaces/.../plugins/,
// data/<plugin>/), deduplicated by real path so one physical file is never counted twice.
// Keep BOTH paths: the one we were given and where it really points. Classifying by the real path
// alone is an escape - a repository-local `scripts/security-redact.js` symlinked to a file outside
// the tree would resolve "outside" and skip git validation entirely, while still being a path the
// child can replace. So a candidate is repository-local if EITHER form is inside, and a
// repository-local candidate must be a regular file at the path as given, not a link to one.
const cands = [];
const add = (p) => { const ap = path.resolve(p); if (!cands.some((c) => c.given === ap)) cands.push({ given: ap, real: real(ap) }); };
if (process.env.CLAUDE_PLUGIN_ROOT) add(path.join(process.env.CLAUDE_PLUGIN_ROOT, "scripts", "security-redact.js"));
for (const rel of [".claude/scripts/security-redact.js", "scripts/security-redact.js"]) add(path.join(root, rel));
const plugins = [];
let frontier = [path.join(require("os").homedir(), ".claude", "plugins")], seen = 0;
for (let d = 0; d < 6 && frontier.length && seen < 4000; d++) {
const next = [];
for (const dir of frontier) {
if (++seen > 4000) break;
for (const c of [path.join(dir, "sd0x-dev-flow", "scripts", "security-redact.js"),
path.join(dir, "scripts", "security-redact.js")]) {
const rp = fs.existsSync(c) && c.split(path.sep).includes("sd0x-dev-flow") ? real(c) : null;
if (rp && !plugins.includes(rp)) plugins.push(rp);
}
next.push(...dirs(dir));
}
frontier = next;
}
if (plugins.length === 1) add(plugins[0]);
// Pick the first candidate that is a regular file and, if it lives inside the repository, is
// tracked and unmodified. THIS check is sound here and nowhere later: the tree is clean by the
// precondition above and no write-capable child has run yet.
const ok = cands.find((c) => {
let st; try { st = fs.lstatSync(c.given); } catch { return false; }
if (!st.isFile()) return false; // lstat: a symlink is never a candidate
if (!inside(c.given) && (!c.real || !inside(c.real))) return true; // genuinely outside the tree
const rel = path.relative(root, c.real || c.given);
try { execFileSync("git", ["ls-files", "--error-unmatch", "--", rel], { cwd: root, stdio: "ignore" }); }
catch { return false; }
return execFileSync("git", ["status", "--porcelain", "-z", "--", rel], { cwd: root }).toString() === "";
});
if (!ok) { console.log("[STOP] no trusted security-redact.js to pin" + (plugins.length > 1 ? " (several plugin installations found - set CLAUDE_PLUGIN_ROOT)" : "")); process.exit(1); }
console.log(ok.given);
console.log(crypto.createHash("sha256").update(fs.readFileSync(ok.given)).digest("hex"));
'Carry both lines — the absolute path and its digest — in the conversation, as you carry the
threadId. Step 3b takes them as its two arguments and refuses to run if the file no longer hashes
to that value.
--spec provided: Read spec/request doc, extract individual items.
Arguments without --spec: Use directly as single item.
No arguments: Ask user for requirement, target file, reference files.
Intent check: identify the feature this work belongs to (from the spec, the task, or the
paths) and read docs/features/<key>/intent-<key>.md if it exists — its INV-* invariants and
Non-goals constrain every item; a planned item that contradicts one stops and asks the user
(cite the line). No identifiable feature → nothing to load; proceed.
Break into implementation items — each one logical unit (interface, method, endpoint), implementable in dependency order, small enough for one Codex call.
Present plan before starting:
| # | Item | Target File | Depends On |
|---|-------------------|----------------------|------------|
| 1 | Define interfaces | src/interface/x.ts | - |
| 2 | Core logic | src/service/x.ts | 1 |
| 3 | Controller/Route | src/controller/x.ts | 2 |
Proceed?Claude researches the codebase before calling Codex:
.claude/CLAUDE.md (fallback CLAUDE.md) — tech stack, conventions, test commandsSummarize as PROJECT_CONTEXT for Codex.
Implement one item at a time, in dependency order.
See references/codex-prompts.md for the full prompt template.
Two reads, in this order, before EVERY item — 3a, every 3c, and every Step 5 dispatch alike. Saying "take the baseline after the precondition" was circular, since the precondition is evaluated against a baseline; a reviewer caught it. They are two different reads:
git status --porcelain --untracked-files=all --ignored — which is what the
precondition below is evaluated against;git rev-parse HEAD and git stash list. Taking this one after the cleanup is what stops
the user's own commit or stash being read as a Codex violation.The detector baseline is what the table below compares against — 3a and each 3c alike, never once per session:
git status --porcelain --untracked-files=all --ignored. This skill owns the implementation lifecycle, so it owns
the baseline. Both flags matter, measured once in this checkout on 2026-08-30 as a dated example
and not a property of any tree: the bare command returned 92 entries where this returned 166. The
ratio is the point, not the numbers — --untracked-files=all because an untracked directory otherwise collapses to a
single line and hides its files, --ignored because ignored files are omitted entirely. Either
omission makes an existing path look newly created. Capturing once per session is the other half of
the same bug: rejecting item 2 against item 1's baseline can revert item 1's accepted work.
Rejection (Step 3b) reverts only paths absent from the current item's baseline.
One precondition, checked BEFORE dispatching each item — 3a and every 3c alike. Step 3b runs after the Codex call, so a warning there reaches the user only once the overwrite has happened.
The item's write set must be clean at baseline. The prompt tells Codex to touch whatever the
item needs — tests, imports, call sites — so that set is not knowable in advance, which makes the
only checkable form of this condition the strong one: every path in the baseline must be resolved
before dispatch (commit, stash, or remove), or the user explicitly accepts both consequences,
named separately because they are different losses: this run has no working rollback at all, and
a modified baseline-present untracked or ignored file keeps its ??/!! status, so Step 3b cannot
show it either — the "complete changeset" it promises is complete only over a tree that was clean at
baseline. Accepting the first is not accepting the second; ask for both.
Five review rounds were spent patching this branch one category at a time — staged changes, ignored
files, collapsed untracked directories, the ordering, and finally the discovery that a modified
baseline-present untracked or ignored file keeps its ??/!! status and so cannot be shown to the
user at all, let alone restored. The diagnosis behind the current shape: git cannot give this
workflow reversibility on a dirty tree, and per-category reasoning kept finding one more category.
A single upfront condition is the thing that is actually true, so it replaced the sequence of
partial promises.
What the condition does not cover, stated rather than implied. This class runs Codex with workspace write access, so nothing written here prevents what it does to the tree. What the workflow can still do is notice, and three reviewers corrected an earlier version of this paragraph that got the boundary wrong — it claimed staging bypasses Step 3b, which is false. Measured:
| Operation | Visible to Step 3b? | Detected how |
|---|---|---|
Staging (git add) | Yes — git diff HEAD includes staged changes, and the status command shows the index column (M rather than M) | Already displayed; the Reject row stops on it |
| Commit | No — HEAD moves, so the committed changes vanish from git diff HEAD. Measured: after a full commit that display is empty, but a partial commit (git commit -- <path>) leaves everything else in it, so a non-empty diff is no evidence that nothing was committed | Record git rev-parse HEAD with the baseline and compare after the item. A moved HEAD means the decision was bypassed: say so and stop |
| Stash | No, for the stashed portion only — git stash push takes --keep-index, --staged, --patch and a pathspec, so anything it did not take stays visible. Same shape as the partial-commit row: what is still in the diff proves nothing about what was stashed | git stash list before and after — git log does not see a stash at all. A changed list stops the item, exactly as a moved HEAD does: show the new entries (git stash list and git stash show -p <entry>) and say that reviewed changes may have left the tree |
assume-unchanged / skip-worktree on a tracked path | No — the path is absent from git status entirely, so neither the baseline nor Step 3b's display can see a local edit to it | The Step 0 probe above reads git ls-files -v and flags a tag that is S or lowercase. Measured: h = assume-unchanged, S = skip-worktree, s = both; -v lowercases only the assume-unchanged mark, so a lowercase-only test misses plain skip-worktree and a not-H test misdiagnoses M (an unmerged entry, reported separately with its own remedy). Step 0's precondition requires them cleared before any dispatch — this row used to read "nothing here detects it", which was true of git status and false of the index |
Run the comparisons after every write-capable dispatch — each item at 3a/3c and each Step 5
review-loop dispatch — not once at the end: an item that committed is only attributable to that item
if the check ran around it. The prompt forbids all three operations (references/codex-prompts.md), which is an instruction,
not an enforcement — the HEAD and stash comparisons above observe persistent drift in those two values — not the operation: a commit later undone inside the same item, or a stash created and popped, leaves both readings unchanged.
Bind every placeholder before writing prompt.md. The template is body-only, so nothing
evaluates an expression inside it — a ${X || 'default'} would reach Codex as literal text. Two
have no natural empty form and this skill supplies it: ${CONTEXT_CONTENT} becomes None when
there is no extra context, and ${TARGET_CONTENT} becomes (new file) when the target does not
exist yet.
Dispatch per @skills/codex-code-review/references/codex-transport.md § Start with
--class implement — this skill is its only caller, and that class is what gives Codex
workspace-write. Guard 3 in test/rules/codex-transport-guards.test.js pins that ownership.
The MCP era's ask-on-failure approval had no headless equivalent, so the transport pins its own
policy instead — the value is in @skills/codex-code-review/references/codex-transport.md § Start, and naming it here would make this file a second
authority for it. The reason is that the exec transport is non-interactive: nobody could answer an
approval prompt, so the old policy could only hang or fail. The human control that replaced it is
Step 3b below, which shows the complete changeset and requires the user to accept, reject or
modify every item before the next one.
Save the returned threadId.
After each Codex call, show the complete changeset, then ask the user. The changeset has two halves and both go through the scanner — the single command below produces the whole display:
git diff HEAD — HEAD, not a bare git diff, which shows only
unstaged changes and would hide anything the item staged. Printing that diff directly was a leak
path of its own: an item that writes a token into an existing tracked file and stages it puts the
token in the diff, and the created-file scan never sees that file at all;git status --porcelain --untracked-files=all --ignored, including
ignored ones that --exclude-standard would omit. Listing filenames is not showing the changeset:
this workflow explicitly supports new files (${TARGET_CONTENT} renders (new file)), so without
the contents a user can accept source or test files they never saw.Nothing is printed until it has passed a content scan, and status alone is not the
boundary. An earlier revision withheld ignored (!!) files and printed the rest, which is
half a rule: a credentials.json or a source file with an embedded token is routinely untracked
and not ignored, so a status test would have printed it. Two gates, and every printed path — a
tracked diff included — must clear both:
!!) is never printed. .gitignore is the project's own statement
that these files are not source, and it is where .env, credentials, key material and logs live —
this repository ignores .env itself.Both gates run under the existing Bash(node:*) grant, and deliberately so — this workflow adds no
command grant to display a changeset:
The whole thing is one node command that takes no arguments — locator and file list included.
Three constraints force that shape, each measured:
Bash(node:*), so a line beginning REDACT=... is not a command this skill may run;require("scripts/security-redact.js") resolves as a package name and fails with
Cannot find module, so the locator resolves to an absolute path first;$(printf SHELL_INJECTED >&2) is expanded by the shell before node starts — measured, it
printed SHELL_INJECTED — so interpolating paths into the command line hands a write-capable
child arbitrary command execution in the parent. Node's -- end-of-options marker does not help
here: it protects node's own parser, one layer too late. The only fix is to remove the boundary:
the program asks git for the created paths itself, NUL-delimited, and nothing about a filename is
ever parsed by a shell.The two arguments it does take are the redactor path and digest Step 0 produced before the
dispatch, and they are the exception that proves the rule: they are values this workflow computed
while the tree was still clean, not names the child chose. Both are single-quoted in the
invocation above, and that is not decoration — an ordinary installation path such as
/Users/me/My Project/scripts/security-redact.js would otherwise split into two arguments and the
run would stop with the second half read as a digest. Write an embedded apostrophe as '\''. The
digest is validated as 64 hex characters before anything is read, and the run stops if the file no
longer hashes to it.
node -e '
const fs = require("fs"), path = require("path");
const { execSync, execFileSync } = require("child_process");
const MAX_BYTES = 1 << 20, MAX_LINES = 500;
// The repository root, for the same reason the Step 0 probe resolves it: paths are root-relative
// and the redactor lives under the root, so a nested invocation would mislocate both.
const root = execSync("git rev-parse --show-toplevel").toString().trim();
// THE REDACTOR IS CODE THIS COMMAND EXECUTES, and it runs AFTER a workspace-write child. So the
// candidate list is trust-ordered, not convenience-ordered: a plugin copy outside the workspace
// first (Codex is confined to the repository by `-C <toplevel>`, so it cannot write there), and an
// in-repo copy only when git says this run did not create or modify it. Without that, Codex could
// drop a `.claude/scripts/security-redact.js` of its own and the confirmation step would execute
// it before the user was asked to accept anything.
// The redactor is pinned, not searched. Step 0 resolved it and recorded its digest BEFORE the
// write-capable dispatch; this process only checks that the same bytes are still there. Searching
// here cannot be made safe: after a write-capable child has run, a tracked file marked
// `assume-unchanged` and then overwritten still reports clean, and a path replaced by a symlink
// resolves somewhere else - so every predicate evaluated at this point can be arranged to pass.
const crypto = require("crypto");
const [pinnedPath, pinnedDigest] = process.argv.slice(1);
if (!pinnedPath || !/^[0-9a-f]{64}$/.test(pinnedDigest || "")) {
console.log("[STOP] this command takes the redactor path and its 64-hex digest from Step 0; re-run Step 0 to obtain them");
process.exit(1);
}
let pst; try { pst = fs.lstatSync(pinnedPath); } catch { pst = null; }
if (!pst || !pst.isFile()) {
console.log(`[STOP] the pinned redactor is missing or is no longer a regular file: ${pinnedPath}`);
process.exit(1);
}
// ONE read. The bytes that are hashed are the bytes that are executed - `require(pinnedPath)`
// would open the pathname a second time, and between the two opens a background process left by
// the child can swap the file. Compiling the verified buffer closes that window by construction;
// no ordering of checks around a second open can.
const src = fs.readFileSync(pinnedPath);
const nowDigest = crypto.createHash("sha256").update(src).digest("hex");
if (nowDigest !== pinnedDigest) {
console.log(`[STOP] the redactor changed since Step 0 pinned it (${pinnedPath}); this run may have written it - restore it and re-run`);
process.exit(1);
}
const Module = require("module");
const mod = new Module(pinnedPath, null);
mod.filename = pinnedPath;
mod.paths = Module._nodeModulePaths(path.dirname(pinnedPath));
mod._compile(src.toString("utf8"), pinnedPath);
const { redact } = mod.exports;
if (typeof redact !== "function") {
console.log(`[STOP] the pinned redactor exports no redact() function: ${pinnedPath}`);
process.exit(1);
}
// CAPABILITY CHECK, defence in depth behind the digest. The pin proves the bytes did not change
// since Step 0; it does not prove Step 0 pinned a redactor that actually works - an older
// installation is unchanged and still gets the positional cases wrong (the value equal to, or
// inside, its own key). A redactor that fails this is not used at all: with no trustworthy scan,
// nothing may be printed.
for (const probe of ["{\"password\":\"pass\"}", "API_TOKEN=TOKEN"]) {
let masked; try { masked = redact(probe); } catch { masked = ""; }
if (!masked.includes("[REDACTED]") || /:"pass"|=TOKEN/.test(masked)) {
console.log(`[STOP] the pinned redactor (${pinnedPath}) fails its capability probe - it leaves ${probe} unmasked, so it is an older copy; point CLAUDE_PLUGIN_ROOT at the active installation or update it, then re-run Step 0`);
process.exit(1);
}
}
// Half one: the tracked diff, per file, each hunk set scanned before it is shown. A diff is text
// like any other and a token written into an existing tracked file arrives here, not in the
// created-path loop below.
// A repository with no commit yet has no HEAD to diff against, and that is not an error: every
// path in it is a created path and the loop below covers them all. Failing here would take the
// whole display down on the first item of a brand-new project.
let diffNames = [];
try {
diffNames = execSync("git diff --name-only -z HEAD", { cwd: root, maxBuffer: 1 << 28 })
.toString().split("\0").filter(Boolean);
} catch { diffNames = []; }
for (const rel of diffNames) {
let d;
try { d = execFileSync("git", ["diff", "HEAD", "--", rel], { cwd: root, maxBuffer: 1 << 28 }).toString(); }
catch (e) { console.log(`[WITHHELD unreadable-diff] ${rel} — ${e.code}`); continue; }
if (d.length > MAX_BYTES) { console.log(`[WITHHELD oversized-diff] ${rel} (${d.length} bytes)`); continue; }
let safeDiff; try { safeDiff = redact(d); }
catch (e) { console.log(`[WITHHELD secret-in-diff] ${rel} — ${e.name}; inspect it locally`); continue; }
console.log(`--- diff ${rel}`); console.log(safeDiff);
}
// Half two: the created paths, asked of git INSIDE this process. `-z` is NUL-delimited, so a
// filename may contain spaces, quotes, newlines or shell metacharacters and still arrive as one
// field.
const fields = execSync("git status --porcelain=v1 -z --untracked-files=all --ignored",
{ cwd: root, maxBuffer: 1 << 28 }).toString().split("\0");
for (let i = 0; i < fields.length; i++) {
const e = fields[i];
if (!e || e.length < 4) continue;
const x = e[0], y = e[1], rel = e.slice(3);
if (x === "R" || x === "C") { i++; continue; } // rename/copy carries an origin field too
const ignored = x === "!" && y === "!";
const created = ignored || (x === "?" && y === "?") || x === "A";
if (!created) continue;
// Gate 1, status: an ignored path is named and never read. .gitignore is the project saying
// these are not source, and it is where .env, credentials, keys and logs live.
if (ignored) { console.log(`[WITHHELD ignored] ${rel} — inspect it locally`); continue; }
const p = path.resolve(root, rel);
// Gate 2, the filesystem node, BEFORE any read. lstat does not follow links, so a symlink is
// classified as one; readFileSync would have followed it out of the repository, and a FIFO or
// device would block. Size is checked here too — a multi-gigabyte or sparse file must never be
// read into memory to discover it was too big.
if (p !== root && !p.startsWith(root + path.sep)) { console.log(`[WITHHELD outside-repo] ${rel}`); continue; }
let st; try { st = fs.lstatSync(p); } catch (e) { console.log(`[WITHHELD unreadable] ${rel} — ${e.code}`); continue; }
if (!st.isFile()) { console.log(`[WITHHELD non-regular] ${rel} (${st.isSymbolicLink() ? "symlink" : "not a regular file"})`); continue; }
if (st.size > MAX_BYTES) { console.log(`[WITHHELD oversized] ${rel} (${st.size} bytes)`); continue; }
let buf; try { buf = fs.readFileSync(p); } catch (e) { console.log(`[WITHHELD unreadable] ${rel} — ${e.code}`); continue; }
// Metadata without `file` or `wc`: a NUL byte is the binary test, newlines are the line count.
if (buf.includes(0)) { console.log(`[WITHHELD binary] ${rel} (${buf.length} bytes)`); continue; }
const text = buf.toString("utf8"), lines = text.split("\n").length;
if (lines > MAX_LINES) { console.log(`[WITHHELD oversized] ${rel} (${lines} lines)`); continue; }
// Gate 3, content. Redact FIRST, then print: a header before the scan announces a file the scan
// is about to refuse, and the reader has to notice the WITHHELD line to know it was void.
let safe; try { safe = redact(text); }
catch (e) { console.log(`[WITHHELD secret] ${rel} — ${e.name}; inspect it locally`); continue; }
console.log(`--- ${rel} (${lines} lines)`); console.log(safe);
}
' -- '<the path Step 0 printed>' '<the digest Step 0 printed>'For every withheld path — ignored, binary, oversized, secret-bearing, non-regular or outside the
repository — give the path, the reason
and its size, say plainly that the contents were withheld, and ask the user to inspect it locally
before deciding. Name the reason, never the matched value (@rules/security.md; a fingerprint is
what security-redact.js returns for that purpose). Accepting a file unseen is the user's explicit
call, not a silent default.
What is printed, then, is the ordinary case this workflow exists for — new source and test files, untracked, not ignored, and carrying no secret the scan can see.
The same commands and the same two gates apply at Step 4's final confirmation, not only here.
| Choice | Action |
|---|---|
| Accept | Proceed to next item (3c) |
| Reject | Revert only paths absent from this item's baseline (git checkout -- <path>…) — those had no state to lose, which is true only because Step 0 refused to dispatch while any assume-unchanged / skip-worktree path existed: such a path is baseline-absent and holds state, and reverting it destroys the edit. Any path that appeared in the baseline in any column (staged, unstaged or untracked) is never reverted automatically: name it, show git diff -- <path>, and let the user decide, because its pre-item contents were overwritten and nothing here can recover them. Files the item created are baseline-absent, but git checkout -- cannot restore a file with no committed version — removal is a separate, confirmed action: show each and ask. If the item staged anything, stop and say so — git checkout -- restores from the index. Never a bare git checkout .. Re-attempt (max 2 retries, then ⛔) |
| Modify | § Resume with feedback on the same thread → loop back to 3b |
Dispatch per that reference's § Resume with the saved threadId — still --class implement, since the class does not change across a thread and a continuation must still be able to make edits. See references/codex-prompts.md.
Repeat 3b → 3c until all items done.
the same complete changeset as Step 3b — git diff HEAD, plus the untracked-and-ignored listing
with the contents of each printable new file and the named-but-withheld entry for each ignored,
binary, oversized or secret-bearing one → user confirms.
Every dispatch in this loop is a dispatch: re-record the baseline and re-check the precondition before each one, exactly as at 3a and 3c. A reviewer found the loop reaching § Resume without either, so a run that started clean could accumulate unacknowledged overwrites here.
⚠️ @CLAUDE.md auto-loop: fix → re-review → ... → ✅ PASS ⚠️
| Step | Command | On fail |
|---|---|---|
| 1 | /codex-review-fast | § Resume on the same thread to fix → re-review |
| 2 | /precommit | § Resume on the same thread to fix → re-run |
Issues found → use same Codex thread to fix (not manual). See references/codex-prompts.md for fix prompt.
Max 3 rounds per step. Still failing → report blocker.
| Change Type | Required Tests |
|---|---|
| New service/provider | Unit (happy + error + edge) |
| New API endpoint | Unit + integration |
| Modified logic | Existing pass + new logic tests |
| Bug fix scenario | Regression test |
If Codex omitted tests → § Resume on the same thread to request them.
## Codex Implementation Report
### Implementation Items
| # | Item | Target File | Status |
|---|------|-------------|--------|
| 1 | ... | ... | ✅/❌ |
### Change Summary
| File | Operation | Description |
|------|-----------|-------------|
| ... | Create/Modify | ... |
### Review Result
<codex-review-fast output>
### Gate
✅ Complete / ⛔ Needs modification/codex-review-fast passed/precommit passed/codex-implement "Add a method to calculate fees"
/codex-implement "Implement wallet service" --spec docs/features/wallet/2-tech-spec.md
/codex-implement "Add getUserBalance method" --target src/service/wallet.service.ts
/codex-implement "Implement cache logic" --target src/service/cache.ts --context src/service/redis.ts© sd0xdev, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file (references) in skills/codex-implement of sd0xdev/sd0x-harness.
Open the folder on GitHubat commit c9a2036
Codex Implement next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Codex Implement this skillsd0xdev/sd0x-harness | 192 | — | ~8.7k | Automated safety check: Notes | MIT | |
| Code Review ChecklistshareAI-lab/learn-claude-code | 78k | 5 repos | ~1.1k | Automated safety check: Pass | MIT | |
| Understand Diff AnalysisEgonex-AI/Understand-Anything | 85k | 1 repos | ~1.4k | Automated safety check: Pass | MIT | |
| Open Code Review CLIalibaba/open-code-review | 44k | — | ~3.1k | Automated safety check: Pass | Apache-2.0 | |
| Open Code Review Delegatealibaba/open-code-review | 44k | — | ~2k | Automated safety check: Pass | Apache-2.0 | |
| PR Review State Fetchprisma/orm | 48k | — | ~767 | Automated safety check: Pass | Apache-2.0 |
shareAI-lab/learn-claude-code
Reviews code against a five-part checklist covering security, correctness, performance, maintainability and testing, and reports findings in a fixed format.
Egonex-AI/Understand-Anything
Reads your git changes or a pull request against a prebuilt knowledge graph of the project to explain what changed, which components are affected and what is risky.
alibaba/open-code-review
Runs the ocr command-line tool to review Git changes, a commit or a branch comparison with an AI model, returning line-level comments and optionally applying fixes.
alibaba/open-code-review
Has the host agent do the code review itself while the ocr CLI handles file selection and rule lookup, covering workspace changes, branch ranges or single commits.
prisma/orm
Fetches a pull request's canonical review state as JSON, validates it, and renders markdown, a text summary and triage target files from it using bundled scripts.
tirth8205/code-review-graph
Reviews a pull request or branch diff with a code knowledge graph and produces a structured review that includes blast-radius analysis.
sd0xdev/sd0x-harness
Write an Architecture Decision Record (ADR) for a feature — Context / Decision / Status / Consequences / Alternatives, filed as docs/features/<feature/adr-<NNN-<title.md with a 3-digit zero-padded…
sd0xdev/sd0x-harness
Load GitHub PR review comments into AI session — analyze, triage, plan.
sd0xdev/sd0x-harness
Change-aware next step advisor. An agent skill from sd0xdev/sd0x-harness.
sd0xdev/sd0x-harness
Obsidian vault integration via official CLI. An agent skill from sd0xdev/sd0x-harness.
sd0xdev/sd0x-harness
Agent-driven workflow orchestration (v1 report-only). An agent skill from sd0xdev/sd0x-harness.
sd0xdev/sd0x-harness
Post friendly review comments to a GitHub PR — prepare locally, preview, then submit as atomic review.
Works with
Categories
Implement features via Codex exec. An agent skill from sd0xdev/sd0x-harness. Codex Implement is an agent skill from sd0xdev/sd0x-harness. Implement features via Codex exec.
Codex Implement fits situations like: : writing new code from specs; implementing features; Codex-driven development.
Run `npx skills add sd0xdev/sd0x-harness --skill codex-implement -a claude-code`. Or copy the skill folder (skills/codex-implement in sd0xdev/sd0x-harness) into .claude/skills/codex-implement in your project. Claude Code loads it when a task matches its description.
Run `npx skills add sd0xdev/sd0x-harness --skill codex-implement -a codex`. Or copy the skill folder (skills/codex-implement in sd0xdev/sd0x-harness) into .agents/skills/codex-implement in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add sd0xdev/sd0x-harness --skill codex-implement -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/codex-implement, .gemini/skills/codex-implement, .github/skills/codex-implement and .opencode/skills/codex-implement in your project.
Going by SKILL.md and its folder, Codex Implement needs the command-line tools its instructions call (git, node and codex) and credentials named API_TOKEN. Its frontmatter pre-approves these tools: Bash(git:*), Read, Grep, Glob, Edit, Write, AskUserQuestion, Skill, Agent, Bash(node:*).
SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Codex Implement is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.7k tokens (SKILL.md is roughly 35k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 1.2k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Codex Implement: Code Review Checklist (shareAI-lab/learn-claude-code, 78k stars), Understand Diff Analysis (Egonex-AI/Understand-Anything, 85k stars), Open Code Review CLI (alibaba/open-code-review, 44k stars) and Open Code Review Delegate (alibaba/open-code-review, 44k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
sd0xdev (a GitHub user) maintains it in sd0xdev/sd0x-harness, which has 192 GitHub stars. The repository holds 91 skills in this directory. The repository was last updated on October 6, 2026.
Source: sd0xdev/sd0x-harness on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.