openai / openai/codex-plugin-cc
Stop review gate has no way to pin the model or reasoning effort it reviews with
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 33.3k
- Forks
- 2.3k
- PR merge metrics
- No merged PRs in 30d
Description
What happens
scripts/stop-review-gate-hook.mjs runs its review by spawning the companion as a task:
// scripts/stop-review-gate-hook.mjs:105 (1.0.6)
const result = spawnSync(process.execPath, [scriptPath, "task", "--json", prompt], {
cwd,
env: childEnv,
encoding: "utf8",
timeout: STOP_REVIEW_TIMEOUT_MS
});
The invocation passes neither --model nor --effort, so model selection falls through to
normal codex resolution: $CODEX_HOME/config.toml, or ~/.codex/config.toml by default. There
is no gate-specific setting anywhere.
Why that is awkward
The gate fires at the end of every turn, in every workspace where it is enabled, on whatever the
effective codex default happens to be at that moment. On one machine over four days that meant
stop-gate reviews ran on four different models without anyone choosing a reviewer. The only way
to change it is to change the default for every codex invocation on the machine, codex exec and
every other plugin command included.
That blocks ordinary cases: a cheap model in a repository where the gate is a tripwire, a
stronger one where the review is meant to be read, or simply pinning the reviewer so that
changing a global default does not silently redefine what "reviewed" means.
Why it looks like a cheap fix
Everything needed is already present:
codex-companion.mjsalready accepts--model <model|spark>and
--effort <none|minimal|low|medium|high|xhigh>ontask(usage string at
scripts/codex-companion.mjs:82), normalizes them at:103(normalizeRequestedModel, aliases
at:72) and:115(effort validation, valid set at:71), and threads both into
buildTaskRequest({ cwd, model, effort, ... })at:604, called at:793.- The plugin already keeps per-workspace configuration in
state.jsonunder aconfigobject.
stopReviewGatelives there (default atscripts/lib/state.mjs:23), andsetConfigat
scripts/lib/state.mjs:153writes arbitrary keys into it.loadStateandsaveStatemerge
rather than whitelist, so an added key survives round trips.
Nothing is wired between the two.
Proposed change
A stopReviewModel key (and, if wanted, stopReviewEffort) in the existing per-workspace
config object, unset by default.
Setter, in codex-companion.mjs, beside the existing gate toggle in the setup handler
(:218 declares the boolean options, :229–:233 writes stopReviewGate):
if (options["stop-review-model"]) {
const raw = String(options["stop-review-model"]).trim();
if (raw === "default") {
setConfig(workspaceRoot, "stopReviewModel", null); // see note below
actionsTaken.push(`Stop-gate reviews for ${workspaceRoot} will use the codex default.`);
} else {
const model = normalizeRequestedModel(raw);
setConfig(workspaceRoot, "stopReviewModel", model);
actionsTaken.push(`Stop-gate reviews for ${workspaceRoot} will run on ${model}.`);
}
}
normalizeRequestedModel applies MODEL_ALIASES and passes any other string through, so this
does not constrain which models are nameable. That is deliberate: the plugin does not maintain a
model allowlist elsewhere either.
Reader, in stop-review-gate-hook.mjs. runStopReview(cwd, input) at :98 does not have the
config in scope, but the caller does: main() already calls getConfig(workspaceRoot) at :146
for the stopReviewGate check at :152. Either pass the model in or read it inside:
function runStopReview(cwd, input = {}, model = null) {
...
const args = model
? [scriptPath, "task", "--json", "--model", model, prompt]
: [scriptPath, "task", "--json", prompt];
const result = spawnSync(process.execPath, args, { cwd, env: childEnv, ... });
with the caller passing the config.stopReviewModel it already holds.
Documentation, one line in commands/setup.md beside --disable-review-gate.
Notes for whoever implements it
- Clearing. Writing
nullworks with the reader above becausenullis falsy, but then
"absent key" and "key set to null" mean the same thing by accident. Ifstate.mjsgrows an
unset operation, use it; otherwise the null-means-inherit-default convention should be written
down rather than left to the reader. - Scope. Small and localised, but not one line: configuration, command surface,
documentation, plus tests worth having. Unset keeps current behaviour; an explicit model reaches
the task request;defaultclears it; two workspaces hold independent values; a state file
written before the change still loads. - Shape of the key. Flat
stopReviewModelalongsidestopReviewGateis the smaller patch. A
nestedstopReviewGate: { enabled, model }would read better but needs a migration, and is only
worth it if structured config is already the direction. --effortis the adjacent knob.taskalready validates
none|minimal|low|medium|high|xhigh. If the motive for pinning a model is cost, effort is
usually the larger lever, and the same key shape works for it.
Separate issue, mentioned so the two do not get merged
When the review cannot run at all (expired credentials, for instance), the gate receives
status: 1 with empty rawOutput and reports a failed review. An unavailable reviewer and an
adverse verdict are presented identically, so a credentials problem reads as a finding and blocks
the turn until the operator works out what it was. Distinguishing "reviewer ran, adverse verdict"
from "reviewer could not produce a verdict", and stating a policy for the second, is a
correctness change that does not belong in the model patch above.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the setup handler in scripts/codex-companion.mjs, the configuration helpers in scripts/lib/state.mjs, and runStopReview/main in scripts/stop-review-gate-hook.mjs. Read commands/setup.md for the documented setup options and inspect the existing configuration and task-request tests, if present. Done means workspace-specific model selection is persisted, reaches stop-gate task execution, can be cleared to restore the default, and is documented without changing existing behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100