A Git pre-push hook sounds like a natural place to enforce AI code review. Before code reaches the remote, let a tool check it; if nobody has reviewed the change or a serious finding remains, stop the push.

I tested OpenQodex 0.11.0 using several disposable Git fixtures. The result was less straightforward than installing a hook and calling it done. ShellCheck did catch an intentionally introduced shell bug. The Git hook also blocked an unreviewed push under the right configuration. But an independent model review that never finished does not necessarily prevent a push.

This is not a verdict on the quality of OpenQodex’s AI reviews. That part of the experiment did not pass acceptance. It is a look at three different parts of the system: static scanning, the independent AI reviewer, and the Git gate that looks up existing review receipts. They should not be treated as a single automatic operation.

ShellCheck found the bug—but only when it actually ran

I used a disposable Git repository with two revisions of the same shell script. The baseline quoted a variable; the modified version deliberately removed the quotes to trigger ShellCheck rule SC2086.

The scanner experiment used OpenQodex 0.11.0 without invoking a model. The essential CLI flags were:

openqodex scan --uncommitted --only shellcheck --block-on-severity nitpick

That line is a simplified illustration, not a verbatim copy of the experimental command. The test harness also specified --cwd, --report-dir, and a fixed path to the verified package. To reproduce the behavior, prepare a real Git diff in an isolated repository and use the pinned CLI version. The fixed-version CLI documentation describes the commands.

The same synthetic change produced very different results depending on whether ShellCheck was available:

EnvironmentShellCheck statusFinal findingsScan exit code
Offline, installation forbiddennot_installed00
Scanner installation and execution allowedran1 (SC2086)1

These are sanitized observations from the fixed-version synthetic experiment on October 10, 2026. The online run reported rawCount=1 and keptCount=1, and its report contained SC2086. The offline run did not execute ShellCheck at all. Its zero findings therefore cannot mean that the script is clean.

There is another easy-to-miss detail: in this mapping, SC2086 has nitpick severity. OpenQodex’s default review.severity_threshold is minor. If you inspect only the default visible findings, you may assume the scanner found nothing. We had already encountered this filtering issue; the test explicitly set --block-on-severity nitpick so the seeded finding would remain visible.

After that, I stopped trusting a single green exit code. I want to know whether the scanner ran, and what happened to its findings after severity and changed-line filtering. A successful command with not_installed is not a successful code check.

A real pre-push hook does not automatically start an AI reviewer

For the next experiment, Git pushed to a local bare repository. No production project or real hosted remote was involved.

I installed OpenQodex’s actual pre-push hook and committed an unreviewed change. Under the default configuration, the hook warned that no review existed, yet Git push succeeded and the local bare remote advanced.

Then I added:

version: 1
review:
  block_on_severity: major

With a new unreviewed commit, the same kind of Git push was rejected. The test also checked that the bare remote’s main stayed at its previous commit. These were real invocations of an installed hook—not a dummy script that merely returned exit code 1.

Synthetic push caseObserved behavior
Default settings, no review receiptWarned, but push succeeded
block_on_severity: major, still unreviewedPush rejected; remote HEAD unchanged

This verifies unreviewed-change handling, not a complete AI review. The hook experiment did not start Codex and did not produce a receipt from a successful independent reviewer.

The pinned hook implementation makes the division of labor clear. At push time, it examines the Git range, looks for a corresponding receipt in the user’s OpenQodex home, and makes a gate decision. It does not rerun every scanner or launch another model automatically whenever you type git push.

In short, scan completed, review completed, and hook allowed the push are three claims requiring three separate checks. None implies the others.

When the reviewer fails, why can the hook still allow a push?

The third experiment exposed the awkward case. In a separate Mac attempt, the independent Codex reviewer process started a request but received HTTP 401. OpenQodex classified the result as incomplete; there was no valid completed-review receipt. That attempt cannot tell us how accurately the independent model detects bugs, or whether valid and stale receipts are correctly handled end to end.

The Mac test summary also reported a successful push after the incomplete review, following an earlier rejected unreviewed push. However, those two pushes did not target precisely the same remote ref, and the original Mac report files have not been ingested as independently verifiable evidence. I would not call that sequence a fully controlled reproduction of a failure-to-push causal chain.

What we can inspect directly is OpenQodex 0.11.0’s source.

In the pinned push-gate.ts, checkPush() returns abstain when it finds a matching receipt with kind: "incomplete". That means the gate chooses not to block. Crucially, setting review.block_on_severity: major does not turn this particular branch into a denial.

A separate rule appears in hook.ts. The generated Git hook exits with code 1 only when its child command exits with code 1; other tool errors or failure to start the child are translated into a successful shell exit. This avoids locking the developer out of Git whenever the review tool is broken. It also means a severity threshold is not the same thing as requiring a fully completed review.

This is a policy difference, not proof of some arbitrary production security exploit. A tool can deliberately favor uninterrupted developer workflows when a reviewer is unavailable. A team may deliberately prefer the opposite. The danger is believing the former enforces the latter.

What I would use when review really must be mandatory

For now, I would treat OpenQodex as developer feedback, not as the sole unbypassable merge gate. The scanner provides precise rule names and file locations. The pre-push hook reminds you when a change lacks review. But the decision to merge should be made by a separate, observable CI check that verifies at least:

  • Whether each required scanner actually ran, rather than returning not_installed, skipped, or error.
  • Whether the independent AI reviewer completed, rather than reporting unavailable or incomplete.
  • Whether the review belongs to the current commit/diff and was evaluated under the intended policy.
  • Whether branch protection requires the CI check to succeed when the CI system or reviewer fails.

This is a deployment recommendation, not a GitHub branch-protection integration proven by this experiment. If your team requires every merge to have a completed independent review, you need to verify the CI gate’s own fail-closed behavior. The local pre-push configuration alone does not demonstrate that guarantee.

For a personal repository, I am not convinced that every push needs to invoke another model. Running review deliberately, using scans as fast feedback, and reserving a testable CI gate for critical branches may be a better balance of cost and developer flow. Just do not confuse “the hook is installed” with “an independent AI has reviewed this code.”

The evidence here is deliberately narrow. OpenQodex 0.11.0 detected SC2086 in a synthetic Shell change. A disposable Git remote demonstrated default warning-only behavior and a configured rejection of an unreviewed push. The pinned implementation also contains explicit paths where incomplete receipts and certain tool failures do not block. The independent reviewer never completed successfully in this test series; we have no reliable conclusions about model accuracy, false positives, latency, or a valid receipt round trip.

That is enough to change my decision. OpenQodex remains useful as a scanner and review entry point, but I would not make its local hook the only mandatory AI review gate for a production repository without first proving what actually happens when the reviewer cannot finish.