openqodex 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/agents.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agents
2
2
 
3
- OpenQodex runs inside Claude Code, Cursor, Codex CLI and Cline. `openqodex init` installs it into each one it finds. The review then runs on the agent's own model, with no key.
3
+ OpenQodex runs from Claude Code, Cursor, Codex CLI and Cline. `openqodex init` installs it into each one it finds. Whichever agent asks for the review, the review itself runs in a reviewer process OpenQodex starts: Claude Code, on your Claude Code login, with no other key. Codex and Cursor are not used as reviewers yet (`security` says why); in Cursor, Codex and Cline the review works when Claude Code is installed too. Without it, `review` names the command with which the agent you are in reviews the change itself (see "Who reviews, by what is installed").
4
4
 
5
5
  ## Run init
6
6
 
@@ -12,7 +12,7 @@ npx openqodex init
12
12
 
13
13
  `init` prints every file it will write and asks once. `--agent <name>` picks agents by hand: `claude-code`, `cursor`, `codex`, `cline` or `all`. `--dry-run` prints the plan and writes nothing.
14
14
 
15
- Inside a repository, `init` also asks: "Add the git pre-push hook, so every push from this repo gets a scan, from an agent or by hand?" The default is yes. `--hook pre-push` or `--hook none` answers without asking, and `--yes` answers yes. The answer is recorded for that repository, so a second `init` does not ask again. The hook is described under "A git hook for every tool" below.
15
+ Inside a repository, `init` also asks: "Add the git pre-push hook, so every push from this repo is checked for a review, from an agent or by hand?" The default is yes. `--hook pre-push` or `--hook none` answers without asking, and `--yes` answers yes. The answer is recorded for that repository, so a second `init` does not ask again. The hook is described under "A git hook for every tool" below.
16
16
 
17
17
  ## The instruction section
18
18
 
@@ -22,7 +22,7 @@ Inside a repository, `init` also asks: "Add the git pre-push hook, so every push
22
22
  <!-- openqodex:start -->
23
23
  ## Review with OpenQodex
24
24
  - When a feature or fix is done, and before any push, review it with the openqodex skill: "review my change with openqodex".
25
- - Run that review in a separate subagent: the agent that wrote the code does not judge its own work.
25
+ - OpenQodex starts its own reviewer process for the review: the agent that wrote the code does not judge its own work.
26
26
  - Do not push on a blocked verdict unless the developer says so after seeing the findings.
27
27
  - The report is in `.openqodex/reviews/`.
28
28
  <!-- openqodex:end -->
@@ -39,8 +39,8 @@ The section goes into `CLAUDE.md` and `AGENTS.md` at the root of the repository,
39
39
  ```
40
40
  <!-- openqodex:start -->
41
41
  ## Review with OpenQodex before you push
42
- - Before any `git push`, run `npx -y openqodex@<version> review --agent` from the repository root and follow the brief it prints to the end, including the finalize command it names.
43
- - Run that review in a separate subagent when your agent has one: the agent that wrote the code does not judge its own work.
42
+ - Before any `git push`, run `npx -y openqodex@<version> review` from the repository root. It takes one to three minutes: allow it up to ten minutes, or run it in the background and wait for it to exit.
43
+ - Show the developer the report it prints, exactly as printed. OpenQodex starts its own reviewer process: the agent that wrote the code does not judge its own work.
44
44
  - Do not push on a blocked verdict unless the developer says so after seeing the findings.
45
45
  - The report is in `.openqodex/reviews/`.
46
46
  <!-- openqodex:end -->
@@ -48,18 +48,30 @@ The section goes into `CLAUDE.md` and `AGENTS.md` at the root of the repository,
48
48
 
49
49
  The two files show in `git status`, and `init` says to commit them. `init` writes neither file through a symbolic link. A section you edited is yours: a later `init` and `--uninstall` leave it as it is. `--uninstall` removes our untouched section, and deletes a file only when `init` created it and nothing else is in it. In project scope the same two files carry the instruction section instead, never both.
50
50
 
51
- ## The review runs in a separate subagent
51
+ ## The review runs in its own reviewer process
52
52
 
53
- The skill hands the review to a subagent whose only task is the review, so the agent that wrote the code does not judge its own work. In Claude Code, that is a subagent started with the Agent tool. In Codex, Cursor and other hosts, the skill uses their sub-task or background agent feature when there is one. Where the host has none, the agent tells you the review is not independent, and the report's summary says so on its first line.
53
+ The skill tells the agent to run one command, `review`, wait for it, and show you the report exactly as printed. The agent does not review the change itself and starts no subagent. `review` starts a fresh Claude Code process with no memory of the agent's session, none of your settings or instruction files, and read, search and list tools only, inside a frozen copy of the change. The report says which reviewer ran; the tool writes that line, never the model.
54
54
 
55
- The reviewer may run the project's own tests. It never runs the project's other scripts or starts its services, and it removes anything a test run created.
55
+ A review takes one to three minutes. Agents often stop a command after two minutes, so the skill tells the agent to allow up to ten minutes or run it in the background; `review` prints a line every 15 seconds while the reviewer works.
56
+
57
+ The reviewer runs nothing: no tests, no scripts, no shell.
58
+
59
+ ## Who reviews, by what is installed
60
+
61
+ | Installed | What `review` gives you |
62
+ |---|---|
63
+ | Claude Code, logged in | A fresh Claude Code process that OpenQodex starts reviews the change. |
64
+ | Only Codex or only Cursor (or Claude Code logged out) | "Full review unavailable", exit 2, and a fallback: run `review --agent` and the agent you are in follows the brief it prints, then `review --finalize`. That report says on its first line after the verdict "Reviewed by the coding agent you are using." |
65
+ | None of them | "Full review unavailable", exit 2, and the scanner findings saved to a file as unchecked candidates, never as a review. The fallback line prints too, but no agent is there to follow it. |
66
+
67
+ The skill tells the agent to follow the fallback when `review` prints it. The push hooks count a fallback review as reviewed, with one line naming who reviewed.
56
68
 
57
69
  ## The repo folder
58
70
 
59
71
  Inside a repository, `init` creates two files in `.openqodex/`, and so does the first `review` or `scan` there:
60
72
 
61
73
  - `.openqodex/config.yaml`: the config, every key at its default with a comment. `config` lists every key. It is not created while a `.openqodex.yaml` sits at the root of the repository; that file is still read, and `init` says how to move it.
62
- - `.openqodex/custom-instructions.md`: what a reviewer of this repository must know: conventions, what never to flag, what always to check. The review brief carries its text word for word. A file over 32 KB stops the review with a message; nothing in it is cut. The brief shows it to the agent as quoted text from the repository, because anyone who can commit can change it. It can widen or narrow what the agent flags, and a candidate dropped because of it says so in the report; it cannot make the agent run a command, skip a step or change the finding shape or the finalize step.
74
+ - `.openqodex/custom-instructions.md`: what a reviewer of this repository must know: conventions, what never to flag, what always to check. The review brief carries its text word for word. A file over 32 KB stops the review with a message; nothing in it is cut. The brief shows it to the reviewer as quoted text from the repository, because anyone who can commit can change it. It can widen or narrow what the reviewer flags, and a candidate dropped because of it says so in the report; it cannot give the reviewer a tool, skip a check or change the finding shape.
63
75
 
64
76
  Both are meant to be committed, so the whole team shares them. A file that exists is never touched. `.openqodex/.gitignore` keeps the review reports out of git, so after the first run `git status` shows only these files and the `.gitignore`.
65
77
 
@@ -73,7 +85,7 @@ The default is user scope. `init` writes into your home folder, so one install w
73
85
 
74
86
  In user scope, the push gate hooks, the skill and the Cursor and Cline rules call a launcher, not npx. Every user-scope install gets it, with or without a hook. `init` copies the package to `~/.openqodex/runtime/<version>/` and checks the copy runs. It writes the version to the first line of `~/.openqodex/runtime/current`, then writes `~/.openqodex/bin/openqodex`, a small script that runs the copy that line names with your Node. When the line is missing, is not a version, or names a copy that is gone, the script runs the version `init` installed. The hooks and the skill's commands call that script by its full path, so they do not depend on npx or your `PATH`. A copy is never changed once written: when a folder of the same version with other contents is in the way, `init` stops and names it.
75
87
 
76
- The user-scope skill is a short stub: when to run, who reviews (a separate subagent where the host has one), and one command, `<launcher> guide skill`, which prints the full procedure of the version the launcher runs, with every command written for the launcher. No file `init` writes in user scope names a version or holds the procedure, so an update changes none of them. In user scope the Cursor and Cline rules call the launcher too, and say to run `<launcher> guide skill` when the skill is not loaded.
88
+ The user-scope skill is a short stub: when to run, who reviews (the reviewer process OpenQodex starts), and one command, `<launcher> guide skill`, which prints the full procedure of the version the launcher runs, with every command written for the launcher. No file `init` writes in user scope names a version or holds the procedure, so an update changes none of them. In user scope the Cursor and Cline rules call the launcher too, and say to run `<launcher> guide skill` when the skill is not loaded.
77
89
 
78
90
  In project scope, the hooks, the skill and the rules call `npx -y openqodex@<version>` and the skill holds the full procedure, because the launcher path would not exist on a teammate's machine. These files, and the review section `init` adds to a repository's `CLAUDE.md` and `AGENTS.md`, stay on the version they name: an update never changes them. Run `init` again to move them.
79
91
 
@@ -88,7 +100,7 @@ In project scope, the hooks, the skill and the rules call `npx -y openqodex@<ver
88
100
 
89
101
  The hook is one `PreToolUse` entry. It matches the `Bash` tool and runs only for `git push` commands. It calls `openqodex hook check`.
90
102
 
91
- In user scope, `init` adds rules so Claude Code runs these review commands without asking, and the agent can review unattended: `<launcher> review --agent`, `review --finalize`, `review --agent --all` and `review --finalize --all`, each also with ` --offline` at the end, plus `guide`, `guide skill` and `guide <topic>`. Each rule matches one exact line, so the same command with any other flag, such as `--output` or `--config`, or chained with `&&`, still asks you. `scan`, `doctor`, `trust`, `update`, `init` and `report` still ask you. The brief's finalize command is one of these lines too, unless the review was run with `--config`. Project scope writes no permission rule: a committed settings file would decide for every teammate. A rule you already had is left alone, and `init --uninstall` removes only the rules `init` added. When a later version grants a different set, the next `init` removes the rules an earlier version added and adds the new ones. When your home path holds a space or another character the shell would read, the launcher is written in single quotes in the skill and in the rules alike. When the launcher's path holds `*`, which Claude Code reads as a wildcard, `init` writes no rule and says so in one line; Claude Code then asks before each review command.
103
+ In user scope, `init` adds rules so Claude Code runs these review commands without asking, and the agent can review unattended: `<launcher> review` and `review --all`, each also with ` --offline` at the end, plus `guide`, `guide skill` and `guide <topic>`. Each rule matches one exact line, so the same command with any other flag, such as `--output` or `--config`, a branch or a pull request, or chained with `&&`, still asks you. `scan`, `doctor`, `trust`, `update`, `init` and `report` still ask you. The rules of earlier versions for `review --agent` and `review --finalize` are removed by the next `init`. Project scope writes no permission rule: a committed settings file would decide for every teammate. A rule you already had is left alone, and `init --uninstall` removes only the rules `init` added. When a later version grants a different set, the next `init` removes the rules an earlier version added and adds the new ones. When your home path holds a space or another character the shell would read, the launcher is written in single quotes in the skill and in the rules alike. When the launcher's path holds `*`, which Claude Code reads as a wildcard, `init` writes no rule and says so in one line; Claude Code then asks before each review command.
92
104
 
93
105
  A skill, rule or permission rule an earlier `init` wrote, such as the full-text skill of 0.2.1, is replaced by the next `init` only while it is still exactly as written. One you edited is left as it is, and `init` says so.
94
106
 
@@ -113,7 +125,7 @@ Codex runs a new hook only after you trust it. Open Codex, run `/hooks`, and tru
113
125
  | Skill | `~/.cursor/skills/openqodex/SKILL.md` | `.agents/skills/openqodex/SKILL.md` |
114
126
  | Rule | `.cursor/rules/openqodex.mdc` in the repository, excluded from git | `.cursor/rules/openqodex.mdc` |
115
127
 
116
- Cursor has no rule file in the home folder, so the rule always goes in the repository. In user scope, run `init` inside each repository where you want the rule. The rule applies to every chat, carries the instruction section, and tells Cursor to review before any `git push`.
128
+ Cursor has no rule file in the home folder, so the rule always goes in the repository. In user scope, run `init` inside each repository where you want the rule. The rule applies to every chat, carries the instruction section, and tells Cursor to run `review` before any `git push`. The review itself needs Claude Code installed: `cursor-agent` cannot be held to reading only, so it is not a reviewer.
117
129
 
118
130
  OpenQodex writes no Cursor hook. The rule asks Cursor to review, but nothing stops a push from Cursor.
119
131
 
@@ -128,14 +140,13 @@ OpenQodex writes no Cline hook. The rule carries the instruction section and ask
128
140
 
129
141
  ## What the push gate does
130
142
 
131
- The gate runs in Claude Code and Codex, through the hooks above. It never approves a push for you: your agent's own permission prompt for `git push` still applies.
132
-
133
- Without `review.block_on_severity` in the config, the gate never stops a push:
134
-
135
- - When a finished review of the current change has findings, it adds the finding counts and the report path. A clean review adds nothing.
136
- - When none exists, it adds a note that the change was not reviewed and how to review it.
143
+ The gate runs in Claude Code and Codex, through the hooks above, and in the git pre-push hook below. It looks for the record `review` wrote for exactly the change being pushed, in your own `~/.openqodex/receipts/`. Report files a branch carries under `.openqodex/` never count. It never scans, never starts a review, and never approves a push for you: your agent's own permission prompt for `git push` still applies.
137
144
 
138
- With `review.block_on_severity` set, the gate denies the push unless a finished review of the current change passed. The reason names the next step.
145
+ - A complete review of this change that passed: the gate says nothing.
146
+ - A complete review of this change that is blocked: the gate denies the push when `review.block_on_severity` is set, with the counts and the report path.
147
+ - No review of this change: one line asking you to run `openqodex review`. With `review.block_on_severity` set, the gate denies the push, so an agent runs the review and tries again.
148
+ - An incomplete review of this change: one line saying so. It never blocks.
149
+ - A review from the older two-step protocol (`review --agent`, then `--finalize`): it counts as reviewed, with one line naming who reviewed.
139
150
 
140
151
  `OPENQODEX_SKIP=1` in the environment lets the push through and says so. It is your switch, not your agent's.
141
152
 
@@ -149,7 +160,7 @@ The git pre-push hook covers pushes from any tool, by an agent or by hand. `init
149
160
  npx openqodex hook install
150
161
  ```
151
162
 
152
- Before each push it runs `openqodex hook pre-push` through the launcher, which scans each commit the push sends. The scan compares that commit with exactly the remote's tip of its branch, so a force push shows the code it removes. A new branch is compared with the commit it grows from that the remote already has, otherwise with the usual base. A commit that is not checked out, or a checkout with uncommitted work, is scanned in a temporary copy of that commit, which is removed afterwards. The repository's config and custom instructions as they are in your checkout apply to every scan. It stops the push only when the config sets `review.block_on_severity` and the scan meets it. A scan that fails for its own reasons never stops the push. `cli` has the details.
163
+ Before each push it runs `openqodex hook pre-push` through the launcher. For each branch the push sends, it measures the change from the commit the remote already holds for that branch, or, for a new branch, from the merge base with the default branch (`review.default_base`, else the remote's default branch), to the pushed commit. It then looks for the review recorded in your home for exactly that change. When there is none, it also accepts the newest complete review whose range contains the push: its base is the push's base or an ancestor of it, the push's base is an ancestor of the pushed commit (so not a force push over work the review never saw), and the change from its base to the pushed commit is exactly the change it reviewed. So a branch reviewed with no upstream set, which a review measures from the default branch, still counts when it is pushed over its remote tip, while a push of another branch, or of work changed after the review, counts as not reviewed. When a branch the remote has is not reviewed and has no upstream here, the line says to set the upstream (`git branch --set-upstream-to <remote>/<branch>`), run `openqodex review`, then push. It prints the gate's line on stderr and nothing from the scanners. It stops the push only when the config sets `review.block_on_severity` and the review is missing or blocked. A lookup that fails for its own reasons never stops the push.
153
164
 
154
165
  ## Other ways to install
155
166
 
@@ -158,13 +169,13 @@ Before each push it runs `openqodex hook pre-push` through the launcher, which s
158
169
 
159
170
  ## Inside a sandbox
160
171
 
161
- Some agents run commands in a sandbox that cannot reach the network or write outside the project. There, the first review cannot download scanners. Each scanner reports why it was left out, and the review runs with what is available. Run this once in your own terminal to fix it:
172
+ Some agents run commands in a sandbox that cannot reach the network or write outside the project. There, the first review cannot download scanners, and the reviewer may not reach its model or write in `~/.openqodex/`. Each scanner reports why it was left out. Run this once in your own terminal for the scanners, and run `review` there when the reviewer cannot start inside the sandbox:
162
173
 
163
174
  ```
164
175
  npx openqodex doctor --install
165
176
  ```
166
177
 
167
- The review writes its files inside the repository, in `.openqodex/`. So it works in a sandbox that can write only the project.
178
+ The review writes its reports inside the repository, in `.openqodex/`, and its temporary copy of the change in `~/.openqodex/checkouts/`.
168
179
 
169
180
  ## Uninstall
170
181
 
package/docs/cli.md CHANGED
@@ -22,7 +22,7 @@ By default the change is the commits not yet pushed plus everything uncommitted,
22
22
  4. The point where the branch left the remote's default branch (`origin/HEAD`).
23
23
  5. The last commit, `HEAD`.
24
24
 
25
- A repository with no commits checks every file. OpenQodex never fetches from a remote.
25
+ A repository with no commits checks every file with `scan`; `review` needs a first commit to make its snapshot. A review of your own change never fetches from a remote; a review of a branch or a pull request does (see "Reviewing a branch or a pull request").
26
26
 
27
27
  ## Shared flags
28
28
 
@@ -47,12 +47,14 @@ Progress goes to stderr. The report goes to stdout.
47
47
  ## review
48
48
 
49
49
  ```
50
- openqodex review [--agent | --finalize [path]] [--all | --base <ref> | --uncommitted] [--no-graph] [--only <list>] [--skip <list>]
50
+ openqodex review [--all | --base <ref> | --uncommitted] [--reviewer auto|claude|codex|cursor] [--timeout <seconds>] [--no-graph] [--only <list>] [--skip <list>]
51
+ openqodex review <branch | #number | pull request link> [--base <ref>] [--reviewer auto|claude|codex|cursor] [--timeout <seconds>] [--no-graph] [--only <list>] [--skip <list>]
51
52
  ```
52
53
 
53
- - `--agent`: run the scanners, write the brief and print it. Your agent runs this.
54
- - `--finalize [path]`: check the agent's findings and write the report. Without a path it reads `agent-findings.json` in the newest report folder. With a path it finds the run by the `change_id` in that file.
55
- - Neither flag: run the scanners on the change and print their report, with the formats, flags and exit codes above. Then one line on stderr says how to get the full review from your agent, so `--format json` stays one JSON document. `scan` (see `plumbing`) does the same without that line.
54
+ - The whole review in one run. OpenQodex copies the change into a temporary snapshot in `~/.openqodex/checkouts/`, runs the scanners and the code graph on it, and starts a reviewer: Claude Code (`claude`), as a separate process with no window that can only read, search and list files in the snapshot. A script checks the reviewer's answer and sends problems back at most twice. The report goes to stdout; progress goes to stderr: one line per stage (for the scanners, for example "Scanners: 6 ran, 5 had nothing to check, 14 candidates to check"), and a line every 15 seconds while the reviewer works. The snapshot is deleted at the end. A review is complete only when every scanner candidate was raised or dropped and every changed range was given to the reviewer; otherwise it says what is missing and exits 2. With no reviewer installed and logged in, it prints "Full review unavailable", what is missing, the path of a file with the unchecked scanner candidates, and a fallback: the `review --agent` command for the agent you are in to review the change itself, and exits 2. `docs/internal-reviewer-drivers.md` records how the reviewer is started and isolated.
55
+ - `--reviewer auto|claude|codex|cursor`: the reviewer to start. Without it, `reviewer:` in `~/.openqodex/config.yaml` decides, else `auto`. `auto` picks the agent running the command when it can tell and its reviewer is enabled, else the first enabled one. Only `claude` is enabled today: `codex` and `cursor` say why they are not and exit 2 ("Full review unavailable"). `reviewer_web: on` in the same file gives the reviewer Claude Code's web tools; it is off by default (`security` says why).
56
+ - `--timeout <seconds>`: stop the reviewer after this long. The default is 600.
57
+ - `<branch>`, `#<number>` or a pull request link: review that branch or pull request instead of your own change. See "Reviewing a branch or a pull request".
56
58
  - `--base`, `--uncommitted`: see "Which change is checked".
57
59
  - `--all`: review the whole repository instead of the change. See "Reviewing the whole repository".
58
60
  - `--no-graph`: do not build the code graph for this run.
@@ -61,43 +63,62 @@ openqodex review [--agent | --finalize [path]] [--all | --base <ref> | --uncommi
61
63
 
62
64
  A scanner name is a built-in name such as `semgrep`, or `custom:<name>` for a custom scanner.
63
65
 
64
- `--finalize` exits 2 when:
66
+ `review --agent` and `review --finalize`, the two-step protocol of earlier versions, still work for skills installed before this one and are the fallback `review` names when no reviewer can start: `plumbing` describes them. A review finished that way is recorded as a legacy review, which the push hooks accept, with a line naming who reviewed.
65
67
 
66
- - the findings file breaks the shape, naming the first wrong field;
67
- - the change moved since the brief;
68
- - the config changed since the brief;
69
- - a finding cites a scanner rule or candidate that is not in this scan;
70
- - the brief was written by another openqodex version that is not installed in `~/.openqodex/runtime/`.
68
+ ### Deleted lines
71
69
 
72
- When the launcher started the review, the brief's finalize command is the plain line `<launcher> review --finalize`, with `--all` and `--offline` as the review had them, run from the repository root; it finds the run through `.openqodex/latest.json` (`latest-all.json` for `--all`). With `--config`, or when npx started the review, the command names the repository, the config and the findings file, so it works from any folder. When the version that runs `--finalize` is not the one that wrote the brief, and that one is installed by `init` or an update, it hands the run to that version by its findings file and exits with its code. A version reached that way never hands off again.
70
+ A finding counts toward the verdict only on a line the change added or modified. A change that only deletes lines, such as a removed check, has no such line, so the lines next to each deletion count too: the line just above and the line just below it in the new file. The brief lists each deletion point ("2 lines deleted after line 14 of app/auth.py") and tells the reviewer to cite one of those lines and say what was removed. Any other line the change did not touch stays under "Outside the changed lines".
73
71
 
74
- It never repairs a finding. Fix what it names, or run `review --agent` again.
72
+ ### Reviewing a branch or a pull request
73
+
74
+ `review <branch>` reviews a branch that is not your current work, and `review '#42'` or `review https://github.com/<owner>/<repo>/pull/42` a pull request. Quote `#42` in a shell, where `#` starts a comment. A bare number is a branch name. The branch may be local, `origin/<name>`, or a branch on the remote that is fetched on demand.
75
+
76
+ ```
77
+ openqodex review feature/login
78
+ openqodex review '#42'
79
+ ```
80
+
81
+ The change is what the target added since it left its base: from the merge base of the two to the target's head, read from the commits, never from a work tree. Commits that landed on the base after the split are not part of it. The base is, in this order:
82
+
83
+ 1. `--base <ref>`.
84
+ 2. The pull request's base, which `gh` names when it is installed and signed in. For a branch, only when it has exactly one open pull request.
85
+ 3. `review.default_base`.
86
+ 4. The remote's default branch (`origin/HEAD`), read without the network.
87
+
88
+ Without `gh`, a branch review uses the next source, and a review of `#<number>` says in one line that the pull request's base is not known. The first line of the output and the brief say which base was used and where it came from.
89
+
90
+ The head is fetched first: a branch from its remote, so a stale `origin/<name>` is brought up to date, and a pull request from `pull/<number>/head`, the ref GitHub keeps for every pull request. That ref is the one host convention OpenQodex uses. A base named as `<remote>/<branch>` is fetched too, even when this clone has never seen it. Fetches use git and its own credentials; OpenQodex reads no token. A fetch writes only `refs/remotes/<remote>/<branch>` for a branch, or a ref of its own under `refs/openqodex/tmp/` for a pull request, removed when the review ends: no configured fetch mapping, no tags, no pruning, so your branches and tags never change. A pull request link must name a remote of this repository whose host is exactly `github.com`. In a partial clone, a file that is not downloaded is never fetched for the checkout and the review stops with one line; this needs git 2.44 or newer. An older git fetches such a file itself, so with `--offline` a target review in a partial clone refuses to start on it. For `#<number>`, when `gh` names the repository the pull request was opened against and one of your remotes points at it, the head and the base are fetched from that remote, and a line says which. A local branch is read as it is. `--offline` fetches nothing and calls no `gh`, and says in one line when the target is not available locally.
91
+
92
+ The files are read in a temporary checkout of the head in `~/.openqodex/checkouts/`, a folder only you can open. Making it, and every later git call in it (the code graph included), runs nothing from the repository: no git hook, no file system monitor, no clean, smudge or process filter (including one an include adds only for linked work trees), no submodule. Files stored in Git LFS hold their pointers, and one line says so. Your settings apply, never the target's: the config and `custom-instructions.md` are read from your repository. Checking the target out runs nothing from it, and a link in it becomes a small plain file holding the link's target. The scanners you approved for this repository do run on the target's files, with this repository's settings; one named only in the target's config never runs. If you review pull requests from people you do not trust, approve only custom scanners that do not execute the code they scan. With `--agent`, when the target is your current commit and your work tree is clean, the files are read in place; with uncommitted work, the committed head is reviewed in a checkout, and one line says your uncommitted work is not part of it.
93
+
94
+ The review runs in a fresh checkout, and the checkout is removed at the end. With the older `--agent` protocol, the brief names the checkout, tells the agent to read the code there and never to run its tests or scripts, and prints the finalize line with `--run <id>`, run from your repository. The run folder stays in your repository. Finalize checks that the checkout is still at the reviewed commit. It removes the checkout when it succeeds or when the review must be run again, and keeps it after an error the agent can fix in its findings file. A target review writes no receipt, so it never replaces the review of the change you are about to push. A later `review` removes a checkout left for more than 24 hours.
95
+
96
+ `review --all` and `--uncommitted` cannot be combined with a target.
75
97
 
76
98
  ### Reviewing the whole repository
77
99
 
78
100
  `review --all` treats every file in the repository as the change: every tracked file and every untracked file git does not ignore, as they are on disk, minus `exclude` and `.openqodex/`. Every line of every text file is in scope, so the scanners report on the whole repository with no changed-line filter. Submodules, symbolic links, unreadable files and files over 5 MB are listed in the brief as left out.
79
101
 
80
- There is no scan-only report of the whole repository. With or without `--agent`, the command runs the scanners and prints a brief for your agent: the most-called functions from the code graph and the files with the most scanner hits, as places to start; the 50 most severe scanner candidates, with all of them in `candidates.json`; the matching patterns; and the file inventory in `inventory.json`. Without `--agent` it adds one line saying the review is done when your agent finalizes it. Ask your agent: review my whole repo with openqodex.
81
-
82
- `review --finalize` then works as for a change; with `--all` and no path it finalizes the newest whole-repo run. A finding must name a file in the inventory and a line that exists in it, or finalize exits 2. Any edit to any file after the brief moves the review id, and finalize says the change moved. A whole-repo run keeps its own receipt in `.openqodex/latest-all.json`, so it never replaces the review of the change you are about to push.
102
+ The command runs the scanners and gives the reviewer a brief: the most-called functions from the code graph and the files with the most scanner hits, as places to start; the 50 most severe scanner candidates, with all of them in `candidates.json`; the matching patterns; and the file inventory in `inventory.json`. A finding must name a file in the inventory and a line that exists in it. Every scanner candidate must be raised or dropped; coverage of the files is reported, not required, since no reviewer reads a whole repository line by line. A whole-repo run keeps its own receipt in `.openqodex/latest-all.json`, so it never replaces the review of the change you are about to push.
83
103
 
84
- The brief includes `.openqodex/custom-instructions.md` when the repo has one; a file over 32 KB is refused, never cut. The brief shows it to the agent as quoted text from the repository, because anyone who can commit can change it. It can widen or narrow what the agent flags, and a candidate dropped because of it says so in the report; it cannot make the agent run a command, skip a step or change the finding shape or the finalize step. A scanner given more files than one process can take runs once per batch of files, within its usual time limit.
104
+ The brief includes `.openqodex/custom-instructions.md` when the repo has one; a file over 32 KB is refused, never cut. The brief shows it to the reviewer as quoted text from the repository, because anyone who can commit can change it. It can widen or narrow what the reviewer flags, and a candidate dropped because of it says so in the report; it cannot give the reviewer a tool, skip a check or change the finding shape. A scanner given more files than one process can take runs once per batch of files, within its usual time limit.
85
105
 
86
106
  `--all` cannot be combined with `--base` or `--uncommitted`. The git hook and the GitHub Action never run it.
87
107
 
88
108
  ## init
89
109
 
90
110
  ```
91
- openqodex init [--agent <name>]... [--project] [--hook <pre-push|none>] [--no-repo] [--yes] [--uninstall] [--dry-run]
111
+ openqodex init [--agent <name>]... [--project] [--hook <pre-push|none>] [--no-repo] [--no-review] [--yes] [--uninstall] [--dry-run]
92
112
  ```
93
113
 
94
- Installs OpenQodex into your coding agents.
114
+ Installs OpenQodex into your coding agents, then reviews. After the install, inside a repository: when there is a change, it runs `review` and prints the report; when there is none, it asks what to review (the whole repository, a pull request, a branch, or not now). With `--yes` or without a terminal it prints the three commands instead of asking. This review uses the scanners already installed, starts no download, and never changes the exit code of `init`, which is about the install.
95
115
 
96
116
  - `--agent <name>`: `claude-code`, `cursor`, `codex`, `cline` or `all`. Repeat it for several. Without it, `init` uses every agent it finds.
97
117
  - `--project`: write the files into the repository for a team to commit. The default writes them in your home folder.
98
118
  - `--hook <pre-push|none>`: answer the pre-push hook question without asking. Without it, `init` asks once per repository and records the answer.
99
119
  - `--no-repo`: do not add the team review section to the repository's `CLAUDE.md` and `AGENTS.md`. Without it, `init` without `--project` asks once per repository (default yes) and records the answer; `--yes` or `--no-repo` on a later run replaces the recorded answer. A file the repository's git ignore rules hide is left alone, with one line saying why, since it could not be committed.
100
120
  - `--yes`, `-y`: do not ask. It adds the team review section, even where this repository answered no before (only `--no-repo` keeps it out), and adds the pre-push hook unless this repository answered no to it before or `--hook none` says so. Without a terminal, `init` needs this flag.
121
+ - `--no-review`: end after the install, with no review and no question.
101
122
  - `--uninstall`: remove what `init` wrote. A file you edited after `init` is left in place.
102
123
  - `--dry-run`: print the plan and write nothing.
103
124
 
package/docs/config.md CHANGED
@@ -219,9 +219,11 @@ The code graph lists the callers and importers of the code a change touches, for
219
219
 
220
220
  ## The user config, ~/.openqodex/config.yaml
221
221
 
222
- One file in your home folder holds what is yours, not the team's. It has one key today, and `OPENQODEX_HOME` moves it with the rest of `~/.openqodex/`.
222
+ One file in your home folder holds what is yours, not the team's. `OPENQODEX_HOME` moves it with the rest of `~/.openqodex/`.
223
223
 
224
224
  - `update`: `on` or `off`. The default is `on`. `off` stops the daily version check. `openqodex update --off` and `--on` write it.
225
+ - `reviewer`: `auto`, `claude`, `codex` or `cursor`. The default is `auto`. The agent `review` starts as its reviewer; `--reviewer` on the command line wins. Only `claude` is enabled today; `codex` and `cursor` make `review` say why and exit 2.
226
+ - `reviewer_web`: `on` or `off`. The default is `off`. `on` gives the reviewer Claude Code's WebSearch and WebFetch. A reviewer that reads private code and untrusted text and can open web addresses can be talked into sending the code out, so leave it off unless you accept that (`security`).
225
227
 
226
- A file that does not parse, or an `update` value that is neither `on` nor `off`, turns updates off until it is fixed. `openqodex doctor` says why updates are off.
228
+ A file that does not parse, or an `update` value that is neither `on` nor `off`, turns updates off until it is fixed. `openqodex doctor` says why updates are off. A file that does not parse, or a `reviewer` or `reviewer_web` value not listed above, stops `review` with one line naming the file.
227
229
 
@@ -1,6 +1,6 @@
1
1
  # GitHub Action
2
2
 
3
- The OpenQodex Action runs `openqodex scan` on a pull request's change. It uploads the findings to GitHub code scanning as SARIF. No model is involved: the Action runs the scanners only.
3
+ The OpenQodex Action runs `openqodex scan` on a pull request's change. It uploads the findings to GitHub code scanning as SARIF. No model is involved: the Action runs the scanners only, and its first output line says it is not a review. The full review runs on your machine with `openqodex review`.
4
4
 
5
5
  ## Example workflow
6
6
 
@@ -36,21 +36,47 @@ jobs:
36
36
 
37
37
  - `version`: the `openqodex` version to run. The default is the version the Action was released with.
38
38
  - `upload-sarif`: `true` or `false`. The default is `true`. Set it to `false` to skip the code scanning upload.
39
+ - `block-on-severity`: `info`, `nitpick`, `minor`, `major` or `critical`; any other value fails the step. The job fails on a finding on a changed line at or above it. When set, it wins over `review.block_on_severity` in the repository's config. Empty (the default) uses the config.
40
+ - `fail-on-tool-error`: `true` or `false`. The default is `false`. With `true`, the job fails when OpenQodex itself could not run the scan or install the scanners.
41
+ - `config-from`: `base` or `head`. The default is `base`. Any other value fails the step. In a pull request, `base` reads the OpenQodex config from the pull request's base commit, and `head` reads the one the pull request carries. Other events always read the checked-out config.
42
+
43
+ ## Outputs
44
+
45
+ - `status`: `passed`, `blocked` (a finding met the block severity) or `tool-failed` (OpenQodex could not run the scan).
46
+
47
+ ## The config a pull request can change
48
+
49
+ In a `pull_request` workflow the checkout is the pull request's own code, so its `.openqodex/config.yaml` or `.openqodex.yaml` is the pull request author's. That file can hide findings: `review.disabled_rules`, `review.severity_threshold`, `review.paths.exclude`, `scanners.disable` and `review.block_on_severity` all live there. So in a pull request the Action reads the config from the base branch instead. It fetches the base branch (`github.base_ref`), writes its `.openqodex/config.yaml`, or else its `.openqodex.yaml`, to a file under the runner's temporary folder with `git show`, and passes that file to OpenQodex with `--config`. It never falls back to the pull request's own file: a base branch with neither file, a fetch that fails, or a branch name that is not plain letters, digits, `.`, `_`, `/` and `-` gives the built-in defaults, and the last two also show a warning annotation.
50
+
51
+ The scan reads nothing else from the repository that could weaken it: it does not read `.openqodex/custom-instructions.md`, and a custom scanner never runs on the runner, since no approval is stored there, whichever config is used. The Action passes no `--only` or `--skip`.
52
+
53
+ A team that wants each pull request's own config sets `config-from: head`.
54
+
55
+ What the Action cannot control: on `pull_request` events GitHub runs the workflow file from the pull request itself, so an author who may change workflows can change or remove this step. Branch protection with required status checks, required workflows, or `pull_request_target` used with care are GitHub's own answers to that; the Action cannot defend against an edited workflow.
56
+
57
+ `block-on-severity` in the workflow also wins over any config, since the workflow file lives on your base branch:
58
+
59
+ ```yaml
60
+ - uses: openqodex/openqodex@v0
61
+ with:
62
+ block-on-severity: major
63
+ ```
39
64
 
40
65
  ## What it does
41
66
 
42
67
  1. Sets up Node 22.
43
68
  2. Restores `~/.openqodex/tools` from the Actions cache, keyed on the runner and the `openqodex` version.
44
- 3. Runs `npx -y openqodex@<version> doctor --install`, which installs every scanner and waits.
45
- 4. Runs `npx -y openqodex@<version> scan --base <pull request base commit> --format sarif`. The SARIF goes to a new folder under the runner's temporary folder, never into the checkout.
46
- 5. Uploads that SARIF to code scanning, when `upload-sarif` is `true` and the scan wrote a report.
47
- 6. Fails the job when the scan exited 1 or 2.
69
+ 3. In a pull request with `config-from: base`, fetches the base branch and writes its config to a file under the runner's temporary folder. Steps 3 to 5 run as `scripts/action-scan.sh`.
70
+ 4. Runs `npx -y openqodex@<version> doctor --install`, which installs every scanner and waits. A failure here is handled like a scan that could not run (below), and the scan is skipped.
71
+ 5. Runs `npx -y openqodex@<version> scan --base <base> --format sarif`, with `--config` from step 3 when there is one. The SARIF goes to a new folder under the runner's temporary folder, never into the checkout. The base is the pull request's base commit; on a `push` event it is the commit the push replaced, or, for a push that creates a branch, the merge base with the repository's default branch. Any other event has no base: the first output line says so and the scan uses its default scope.
72
+ 6. Uploads that SARIF to code scanning, when `upload-sarif` is `true` and the scan wrote a report.
73
+ 7. Fails the job when the scan exited 1, or exited 2 with `fail-on-tool-error: true`.
48
74
 
49
- The scan exits 1 only when `.openqodex.yaml` sets `review.block_on_severity` and a finding on a changed line meets it. Without that key, the job never fails on findings. A scan that fails for its own reasons exits 2, and the job fails too.
75
+ The scan exits 1 only when `block-on-severity` or the config's `review.block_on_severity` is set and a finding on a changed line meets it. Without either, the job never fails on findings. A scan that fails for its own reasons, for example on a config file it cannot read, exits 2; a scan killed by a signal or ending with any other code counts as exit 2 too. The same holds when the scanner install fails. The job then shows a warning annotation titled "OpenQodex did not run" with the last line OpenQodex printed (control characters and colon runs removed), writes the same line to the job summary, and sets `status` to `tool-failed`. It does not fail unless `fail-on-tool-error` is `true`.
50
76
 
51
77
  ## Config
52
78
 
53
- The Action reads `.openqodex.yaml` from the repository, like every other command. Custom scanners need an approval stored on the machine that runs them. The runner has none, so the Action lists custom scanners as `untrusted` and skips them.
79
+ Outside a pull request, and with `config-from: head`, the Action reads the repository's config like every other command. Custom scanners need an approval stored on the machine that runs them. The runner has none, so the Action lists custom scanners as `untrusted` and skips them.
54
80
 
55
81
  ## Pre-commit
56
82
 
@@ -59,7 +85,7 @@ The repository also ships a pre-commit hook for the pre-push stage. Add this to
59
85
  ```yaml
60
86
  repos:
61
87
  - repo: https://github.com/openqodex/openqodex
62
- rev: v0.3.0
88
+ rev: v0.5.0
63
89
  hooks:
64
90
  - id: openqodex-scan
65
91
  ```
package/docs/index.md CHANGED
@@ -11,7 +11,7 @@ These pages ship inside the npm package. `npx openqodex guide <topic>` prints on
11
11
  - Scanner: a program that checks code without a model, such as gitleaks or semgrep.
12
12
  - Finding: one problem at one place in the code, with a severity.
13
13
  - Candidate: a scanner finding on a changed line, waiting for the agent to verify it.
14
- - Brief: the text `openqodex review --agent` prints for the agent to review from.
14
+ - Brief: the text `openqodex review` gives its reviewer to review from.
15
15
  - Report: the result of a scan or a review, written as `report.md`, `report.json` and `report.sarif`.
16
16
  - Verdict: `passed` or `blocked`.
17
17
 
@@ -0,0 +1,143 @@
1
+ # Reviewer drivers
2
+
3
+ This page is for contributors. It records how `openqodex review` starts its own reviewer process, and what was observed with the real binary before a driver was enabled. A driver is enabled only when every property below was shown by a real run.
4
+
5
+ The reviewer is a separate coding-agent process that openqodex starts, without a window, for one review. It reads a frozen copy of the change (the snapshot) and answers with one JSON object. The trace is the agent's own event stream: every tool call, its input and whether it succeeded.
6
+
7
+ ## Claude Code
8
+
9
+ Tested with Claude Code 2.1.289 (`claude --version`) on macOS, 2026-10-03, on a throwaway folder and on the demo repo.
10
+
11
+ ### The command line
12
+
13
+ The driver starts this command without a shell, with the snapshot as the working directory, and writes the brief to standard input:
14
+
15
+ ```
16
+ claude -p --output-format stream-json --verbose --input-format stream-json
17
+ --tools Read,Grep,Glob
18
+ --permission-mode dontAsk
19
+ --setting-sources ""
20
+ --settings {"autoMemoryEnabled":false,"hooks":{},"disableAllHooks":true}
21
+ --strict-mcp-config --mcp-config {"mcpServers":{}}
22
+ --disable-slash-commands
23
+ --no-session-persistence
24
+ ```
25
+
26
+ The child gets an environment built from an allowlist (`reviewerEnv` in `packages/cli/src/reviewers/claude.ts`): `PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`, `TMPDIR`, locale, `TERM`, `TZ`, `CLAUDE_CONFIG_DIR`, proxy and CA settings, the `ANTHROPIC_*` key, URL and model variables, and the Bedrock, Vertex or Foundry variables only when the matching `CLAUDE_CODE_USE_*` flag is set; plus `OPENQODEX_REVIEW_DEPTH=1`. No other variable is copied, so no developer token and nothing that ties the child to a running Claude Code session (`CLAUDECODE`, `CLAUDE_CODE_SESSION_ID`, messaging sockets) reaches it. A run from inside a Claude Code session with this environment worked as the runs below did.
27
+
28
+ ### What each flag was observed to do
29
+
30
+ | Flag | Observed |
31
+ |---|---|
32
+ | `-p` with `--input-format stream-json` | A fresh session that reads user messages as JSON lines on standard input. After each answer it prints a `result` event and waits for the next message, so a correction round goes to the same session. Closing standard input ends the process with exit 0. |
33
+ | `--output-format stream-json --verbose` | One JSON event per line: an `init` event (tools, MCP servers, plugins, permission mode, memory paths, version), every `tool_use` with its input, every `tool_result` with `is_error`, a `permission_denied` event for each refusal, and a `result` event with the final text, turns, usage and cost. For a Read, `tool_use_result.file` gives the path, `startLine` and `numLines` delivered. |
34
+ | `--tools Read,Grep,Glob` | The `init` event lists exactly `Glob`, `Grep`, `Read`. Asked to run `ls /` and to write a file, the model answered that it has no Bash or Write tool; no such tool call appears in the trace. The `Agent` tool is absent, so no subagent can start. |
35
+ | `--permission-mode dontAsk` | Reads inside the working directory succeed. A Read of an absolute path outside it (`/tmp/.../outside/secret.txt`, `/etc/hosts`, a decoy ssh config), a relative path that leaves it (`../outside/secret.txt`), a Read through a link inside the folder that points outside, and a Grep or Glob rooted outside (`/tmp/...`, `/`) were each refused with a `permission_denied` event and an error result. A recursive Grep and a `**/*` Glob in the folder did not follow the link out. |
36
+ | `--tools Read,Grep,Glob,WebSearch,WebFetch --allowedTools WebSearch,WebFetch` (only with `reviewer_web: on`) | The `init` event lists the five tools. Without `--allowedTools`, `dontAsk` refused both web tools ("Permission to use WebFetch has been denied because Claude Code is running in don't ask mode"); with it, a WebFetch of example.com and a WebSearch both returned results (2026-10-03). |
37
+ | `--setting-sources ""` | No user, project or local settings file is read. In the same folder, a run without this flag loaded the project `CLAUDE.md` canary (the answer ended with the canary word) and the user's global instructions (the answer quoted them, about 155,000 input tokens); with it, the input was about 4,500 tokens and neither canary nor any sentence of the global file appeared anywhere in the event stream. With `--include-hook-events`, a run reading user settings showed 11 hook events; this run showed none. |
38
+ | `--settings {"autoMemoryEnabled":false,"hooks":{},"disableAllHooks":true}` | The `init` event has no `memory_paths`: auto memory is off. With `disableAllHooks`, a SessionStart hook that a terminal wrapper (cmux) added to every `claude` it starts no longer ran: 2 hook events without it, 0 with it (2026-10-04, Claude Code 2.1.289). The driver also stops the run if any hook event appears in the stream. A repository `AGENTS.md` with a canary instruction was not followed. |
39
+ | `--strict-mcp-config --mcp-config {"mcpServers":{}}` | The `init` event lists no MCP server. |
40
+ | `--disable-slash-commands` | The `init` event lists no skill and no slash command. |
41
+ | `--no-session-persistence` | Nothing is saved for a later `--resume`; the correction rounds use the open process instead. |
42
+
43
+ `AGENTS.md`: a canary there was not loaded in any run, with or without settings. The built-in plugins (`cc-plugin-agents-md`, `cc-plugin-telemetry`, `cc-plugin-plugin-authoring`) stay listed; they are part of Claude Code and read no repository instruction file in this configuration.
44
+
45
+ ### Where it works
46
+
47
+ - Started from a Bash tool inside a running Claude Code session: works, with the same tools and the same refusals.
48
+ - Started from a plain environment (`PATH`, `HOME`, `USER`, `LOGNAME`, `TMPDIR` and the developer's own `CLAUDE_CONFIG_DIR`): works. Without `USER` the login in the macOS keychain is not found and the run ends with "Not logged in".
49
+ - A temporary `CLAUDE_CONFIG_DIR` loses the login, so the driver keeps the developer's own configuration folder and excludes its contents with the flags above.
50
+
51
+ ### Detecting it
52
+
53
+ - `claude --version` prints the version.
54
+ - `claude auth status` prints JSON with `loggedIn`. Exit 1 and `"loggedIn": false` mean the reviewer cannot start.
55
+
56
+ ### Usage
57
+
58
+ Each `result` event carries `num_turns` and `usage` for that turn (input, output and cache tokens), and `total_cost_usd` and `modelUsage` for the session so far. The driver adds up the turns and takes tokens and cost from the last `result` event.
59
+
60
+ ### The boundary and the alarm
61
+
62
+ The boundary is Claude Code's own permission rules: `--tools Read,Grep,Glob` and `--permission-mode dontAsk` with no settings source, which refused every read outside the working folder in the runs above. The alarm is the tool's own check of the event stream (`packages/cli/src/reviewers/trace.ts`), which does not trust the boundary and fails closed:
63
+
64
+ - the run fails when the `init` event lists any tool beyond Read, Grep and Glob, any MCP server or a memory path; the `Agent` tool is never listed, so no subagent or nested turn can make a call the stream does not show, and every `tool_use` in the stream is checked whichever turn it came from;
65
+ - every tool call counts from the moment the agent asks for it, with or without a result; a tool name other than the three makes the review incomplete;
66
+ - every path-bearing input (`file_path`, `path`, `notebook_path`, `cwd`, `directory`, and a `pattern` or `glob` that starts at `/`, `~`, a drive or `..`) is resolved against the snapshot, then through the real path of its deepest existing folder, and compared case-insensitively on macOS and Windows; a path with `$`, `%` or a NUL is refused, `~` is the home folder;
67
+ - an input that is not an object, or a path field that is not text, makes the review incomplete;
68
+ - any attempt outside the snapshot makes the review incomplete, even one the agent refused.
69
+
70
+ The snapshot holds no links (they are written as plain files) and secrets the scanners found are redacted in every file of it before the reviewer starts; a file too large to check is removed from it.
71
+
72
+ ### What the agent stores
73
+
74
+ With `--no-session-persistence` and auto memory off, real runs with Claude Code 2.1.289 left no transcript, no `history.jsonl` line and no project entry for a snapshot folder in the configuration folder (searched for the brief's text and the snapshot paths after the runs). An earlier run without `autoMemoryEnabled: false` left one empty `projects/<folder>/memory` folder; with the flag, none. The driver keeps the developer's configuration folder because a temporary one loses the login.
75
+
76
+ ### Not covered
77
+
78
+ - Managed (policy) settings set by an organisation still apply; they can add hooks or permission rules. The trace check above still fails a run that reads outside the snapshot.
79
+ - Each new Claude Code version can change these flags. Re-run these checks before raising the tested version.
80
+
81
+ ## Codex
82
+
83
+ Not enabled. Tested with codex-cli 0.160.0 (`/opt/homebrew/bin/codex --version`) on macOS, 2026-10-03, logged in with a ChatGPT account, on a throwaway folder under `~/.openqodex/` with canaries. `--reviewer codex` names it, `auto` never picks it, and its `detect()` says why it is off. Two properties failed; the rest held.
84
+
85
+ ### The command line that was tested
86
+
87
+ Started without a shell, from an environment built with `env -i` (`PATH`, `HOME`, `USER`, `LOGNAME`, `TMPDIR`, `LANG`), the prompt on standard input:
88
+
89
+ ```
90
+ codex exec --json --color never --ephemeral --skip-git-repo-check
91
+ --ignore-user-config --ignore-rules
92
+ -C <snapshot>
93
+ -c approval_policy="never"
94
+ -c default_permissions="openqodex_review"
95
+ -c permissions.openqodex_review.filesystem={":minimal"="read",":project_roots"="read","/tmp"="deny"}
96
+ -c web_search="disabled"
97
+ -c project_doc_max_bytes=0
98
+ -c allow_login_shell=false
99
+ -c shell_environment_policy.inherit="core"
100
+ -c skills.include_instructions=false -c skills.bundled.enabled=false
101
+ --disable plugins --disable apps --disable hooks --disable multi_agent --disable memories
102
+ --disable browser_use --disable computer_use --disable image_generation --disable skill_search
103
+ --disable tool_suggest --disable goals --disable in_app_browser --disable view_image
104
+ -
105
+ ```
106
+
107
+ `codex sandbox -c ... -- <command>` runs one command under the same sandbox without a model, and `codex debug prompt-input -c ...` prints what the model would be given; both cost nothing and were used for most checks below.
108
+
109
+ ### What each part was observed to do
110
+
111
+ | Part | Observed |
112
+ |---|---|
113
+ | `exec --json` | One JSON event per line: `thread.started`, `turn.started`, `item.started` and `item.completed` for messages and commands, `turn.completed` with `usage` (`input_tokens`, `cached_input_tokens`, `output_tokens`, `reasoning_output_tokens`). The run ends after one answer. |
114
+ | `--ephemeral` | No rollout file was written for the test folder. A follow-up in the same session is not possible, so a correction round would need a new run that carries the previous answer. A subagent cannot start: `spawn_agent` failed with "no rollout found for thread id". |
115
+ | `-s read-only` alone | Writes and network were refused, but every read was allowed: `cat` of a file in another `/tmp` folder, of `/etc/hosts` and `ls ~/.ssh` all succeeded. |
116
+ | the `openqodex_review` permission profile | Reads outside the folder were refused (`Operation not permitted`): a file in a home folder outside it, `~/.ssh`, `~/.codex`, `~/Projects`, `$TMPDIR`, and `/tmp` with the `/tmp` deny entry. Reads inside it worked. `/etc` and the system folders `:minimal` names stay readable; without `:minimal` no command could start. `touch` was refused. `curl` could not resolve a host. The patch tool was refused ("writing is blocked by read-only sandbox"). |
117
+ | `web_search="disabled"` | The model reported no web search tool. |
118
+ | `project_doc_max_bytes=0` | A canary `AGENTS.md` in the folder did not appear in the prompt input or the answer. |
119
+ | `skills.include_instructions=false` | Without it, a canary skill in the folder's `.agents/skills/` was listed to the model, which then followed it. With it, the skills block is gone from the prompt input. |
120
+ | `--ignore-user-config` | The developer's `config.toml` (MCP servers, plugins, model, trusted projects) is not read. A canary `developer_instructions` in the folder's `.codex/config.toml` did not appear either way: the folder is not a trusted project. |
121
+
122
+ ### Why it is not enabled
123
+
124
+ 1. The developer's global instructions are loaded. `~/.codex/AGENTS.md` (or `$CODEX_HOME/AGENTS.md`) appeared in the prompt input with every flag above, and the model quoted its first sentence. No configuration key removed it (`instructions`, `user_instructions`, `agents_md.enabled`, `include_agents_md`, `features.agents_md` were tried). Only a different `CODEX_HOME` leaves it out, and that moves the login: a copy of `auth.json` would refresh its token on its own and can leave the developer's real login with a used refresh token.
125
+ 2. The event stream does not show every command. Every current model in the catalog (`codex debug models`) has `tool_mode: code_mode_only` except gpt-5.5: the shell is a nested tool inside a code tool. In one run, two shell commands ran (their output came back in the answer) and no `command_execution` event appeared in the stream. So neither the alarm (every read checked from the trace) nor coverage from the trace can be computed. `--disable code_mode_host` removes the shell altogether ("code-mode host is disabled").
126
+
127
+ Read confinement, by contrast, held: the permission profile is a real boundary, stronger than `-s read-only`. The code tool's own JavaScript runtime has no file or network access (`require`, `import("node:fs")` and `fetch` were all undefined or refused).
128
+
129
+ ### Where it was run
130
+
131
+ From a Bash tool inside a running Claude Code session, through `env -i`: it worked. A plain terminal was not tried separately, because the driver stays off.
132
+
133
+ ### Usage
134
+
135
+ Each `turn.completed` event carries `usage`. The test runs used 51,000 to 92,000 input tokens (most of them cached) and 600 to 950 output tokens per run, in 45 seconds or less.
136
+
137
+ ### What would enable it
138
+
139
+ A switch that leaves out `$CODEX_HOME/AGENTS.md` without moving the login, and an event for every command the code tool runs. Re-run the checks above on each new Codex version.
140
+
141
+ ## Cursor
142
+
143
+ Not enabled. `cursor-agent` 2025.09.18-7ae6800 was on this Mac and not logged in. Its help shows `-p` ("Has access to all tools, including write and bash"), `--output-format stream-json`, `--model`, `--force` and `--resume`: no option to limit its tools, no read-only sandbox, no switch to skip the repository's rules or the developer's settings. None of the checks above can pass with those options, so no invocation was built. Cursor users get the full review through Claude Code when it is installed.
package/docs/llms.txt CHANGED
@@ -7,6 +7,7 @@
7
7
  - [FAQ](faq.md)
8
8
  - [GitHub Action](github-action.md)
9
9
  - [OpenQodex docs](index.md)
10
+ - [Reviewer drivers](internal-reviewer-drivers.md)
10
11
  - [Plumbing commands](plumbing.md)
11
12
  - [Quickstart](quickstart.md)
12
13
  - [Scanners](scanners.md)