Skip to content

fix: honor stop_hook_active in the stop-review-gate hook - #565

Open
mittalpk wants to merge 1 commit into
openai:mainfrom
mittalpk:fix/stop-review-gate-honor-stop-hook-active
Open

fix: honor stop_hook_active in the stop-review-gate hook#565
mittalpk wants to merge 1 commit into
openai:mainfrom
mittalpk:fix/stop-review-gate-honor-stop-hook-active

Conversation

@mittalpk

Copy link
Copy Markdown

Fixes #548.

Summary

When the review gate is enabled, main() in stop-review-gate-hook.mjs runs the stop-time Codex review and emits {"decision":"block"} on any non-ok outcome (no output, timeout, failure, invalid JSON) — without checking whether this invocation is already a forced retry.

Claude Code re-invokes Stop hooks with stop_hook_active: true after a block decision. Since this hook never checked that flag, a review that times out or errors would re-run and re-block on every retry, until the harness's forced-retry cap force-ends the turn — worst in exactly the cases where the gate is least useful (a broken/slow review blocking the session 9× before being overridden).

Fix

Skip the review and return cleanly when input.stop_hook_active is set, matching the fix the issue itself suggested:

if (input.stop_hook_active) {
  logNote(runningTaskNote);
  return;
}

The issue also flagged codex-companion.mjs's config.stopReviewGate branches as "worth auditing... for the same issue." I checked — those are only config get/set/report code (buildSetupReport, handleSetup), not the review-blocking logic itself, which lives solely in stop-review-gate-hook.mjs. Confirmed via grep -rl "decision.*block\|runStopReview\|stop_hook_active" across plugins/ that only this one file implements it, so no second file needs the fix.

Tests

Added a regression test that:

  1. Enables the review gate and snapshots the fake Codex binary's appServerStarts counter.
  2. Sends the Stop hook input with stop_hook_active: true.
  3. Asserts the hook exits with empty stdout (no block decision) and that appServerStarts is unchanged — proving the review subprocess was never spawned a second time, not just that the output happened to look right.

Confirmed the test fails against the pre-fix code via git stash isolation, reproducing the exact stale block decision the issue describes (Codex stop-time review found issues that still need fixes...).

Verification

  • npm test: 92/92 passed, no regressions.
  • No lint config found in this repo (no .eslintrc*/eslint.config.*).
  • npm run build (tsc) requires a live codex app-server generate-ts run to produce .generated/app-server-types, which needs an actual Codex CLI installation not available in this environment — this change touches no TypeScript files, so it's out of scope for this fix, but flagging honestly rather than silently skipping it.

When the review gate is enabled, main() ran the stop-time Codex review
and emitted {"decision":"block"} on any non-ok outcome (no output,
timeout, failure, invalid JSON) without checking whether this
invocation was already a forced retry. Claude Code re-invokes Stop
hooks with stop_hook_active: true after a block decision, so a review
that times out or fails would re-run and re-block on every retry,
until the harness's forced-retry cap force-ends the turn -- worst in
exactly the cases where the gate is least useful.

Fix: skip the review and return cleanly when stop_hook_active is set,
matching the fix the issue reporter suggested. The issue also flagged
codex-companion.mjs's `config.stopReviewGate` branches as worth
auditing for the same problem; traced them and confirmed they're only
config get/set/report code, not the review-blocking logic itself
(that lives solely in stop-review-gate-hook.mjs), so no second file
needs the fix.

Added a regression test that snapshots the fake Codex binary's
appServerStarts counter before and after a stop_hook_active retry,
proving the review subprocess is not spawned a second time and no
"decision":"block" is emitted. Confirmed the test fails against the
pre-fix code (git stash isolation) with the exact stale block
decision the issue describes. Full suite: 92/92 passed, no
regressions.
@mittalpk
mittalpk requested a review from a team July 27, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop-review gate hook loops until CLAUDE_CODE_STOP_HOOK_BLOCK_CAP (missing stop_hook_active guard)

1 participant