The Codex gate carries a human approval over to a later head when Codex rates the commits since then as not critical, or when a rebase kept the same blocks.
See docs/prs/ for the details.
The Codex gate carries a human approval over to a later head when Codex rates the commits since then as not critical, or when a rebase kept the same blocks.
See `docs/prs/` for the details.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-code
marked the pull request as ready for review 2026-10-24 23:21:27 +02:00
Gate: the approval of mi came before this result; review requested again, please approve again after reading it; merge scheduled for when the checks succeed.
Change kind: mixed.
Reasons
ORANGE 1 finding(s) that Jev rates as probably real and severe: werkrevisor/codexrunner.py:433-435
YELLOW model reports a security concern: Merge-gate bypass: a critical change since the approved head can pass without a current-head human approval when the overall PR rating is false, because the delta rating is not reflected in the gate's critical marker.
hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 9 changed file(s) agree with the PR document according to Jev
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
[high] Delta ignored at werkrevisor/codexrunner.py:433-435, ORANGE, as a line comment
Classifier
10 of 10 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46152, calls 1, tokens in 44341 / out 27742 (reasoning included in out), duration 244.9 s, cost 0.0466 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8341, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 1807, cost 0.0001 USD, PR document check `jev-1.14.0` calls 9, tokens in 28725, cost 0.0012 USD, build file judge `jev-1.14.0` calls 6, tokens in 8912, cost 0.0004 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 242.7 s, finding_judge 0.1 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).
Reviewed head `cca713b961f14a726802a701eaca289ed28a4e56` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
<!-- werkrevisor:cca713b961f14a726802a701eaca289ed28a4e56 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:97dc5fb019fd80ae66bac84b2c4d8e69,66fae4e55827ebaf8647c4323385c81f,99eadc37e9691a21cee6657d11ce8125,3bfebf84bf45e9ea63cf1b7a45e5d4b8,5b27f011b98f4ef161abf6a6094bdf86,7d48f8e9b414cbc68b347d0224c4a24a,85331beb05536072650bdc096fbcd2e3,1e37b6681295269d96d0ce5e901ebc76,9b64b41da8a6843fe5feea88c6fe3a2d,20a53a1b136b74d33e7b216e827fbb67,41e34a9fb2cb2de1041016371b102b14,07951cbf2f5edc1dd0c217cbf13b3707,6dff1467faf39b146ff3d312d2153798,3e4eb25edfbbd85ad2d8a749fd2df5d5,502bd7af6a897d12bf35de0ff4b3d4a3,42f79b41d42aef7c02af0ade81600364,1db65270885598af8d19464390d21ca9,77c1fb070f0c54781121dd8091de65d2,e609a970c441fffb294be7bcd5ea2c95,d04f177c2bacdad342f5f0265d693f94,0ae7e16d0f9a025c50888d0a870c1995,3d15e6b72f14924ee30ac447a1e2d066,89cb539afba8714d252b1e2846f547cf,9049ef0e9d38320bd3d9d4ec2493766a,fdff8291871acbc8a844ae8bd1bd1785,875282dc370ffa8124eff8ebe3cc5260,7c2afe3655ce5489713e1519efec1603,cfd7f7f971bd89c45c99e967ada9bf78,ff12758487d861f0d87ff76aaafa22e9,d76f51eef2f46ed71114bb29aac0dd9f,d0c4044ae93461a32181b87933d7addc,f0fe9acc179ab84565920fad0bf57876,033b3b84b64b926c81318a8e174f6e72,4c35a0954003146eff2ab718a90c9a96,2b084e9d2ddea62fe15b3a715c8b3140,b3326ad12c6826903dd3b79baa5ef648,259e49114aea051c57401c9fe64bc9a8,5eb69675e125c7544260ff50235f88b7,c5c37337407c8c3314d4b605a79045d6,5da1e0572e4d4d07aaea616cc8bd11bc,34dd37fcaf7d724e88bfadea920a7ee6,87b28cbccd82aa10210f7c8f9158188f -->
<!-- werkrevisor:diagrams -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>cca713b</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/144#issuecomment-8020">the result for <code>e1070e9</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Gate: the approval of mi came before this result; review requested again, please approve again after reading it; merge scheduled for when the checks succeed.
Change kind: `mixed`.
### Reasons
- **ORANGE** 1 finding(s) that Jev rates as probably real and severe: werkrevisor/codexrunner.py:433-435
- **YELLOW** model reports a security concern: Merge-gate bypass: a critical change since the approved head can pass without a current-head human approval when the overall PR rating is false, because the delta rating is not reflected in the gate's critical marker.
- **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 9 changed file(s) agree with the PR document according to Jev
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **[high] Delta ignored** at `werkrevisor/codexrunner.py:433-435`, ORANGE, as a line comment
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46152, calls 1, tokens in 44341 / out 27742 (reasoning included in out), duration 244.9 s, cost 0.0466 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8341, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 1807, cost 0.0001 USD, PR document check `jev-1.14.0` calls 9, tokens in 28725, cost 0.0012 USD, build file judge `jev-1.14.0` calls 6, tokens in 8912, cost 0.0004 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 242.7 s, finding_judge 0.1 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).<br>
Reviewed head `cca713b961f14a726802a701eaca289ed28a4e56` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
</sub>
### Diagrams
Drawn by a model from the diff with [PR Lens](https://github.com/coldteadotai/pr-lens): a view of what the PR touches, not a statement of what it does.


</details>
werkrevisor: ORANGE / FINDINGS for cca713b961f14a726802a701eaca289ed28a4e56.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `cca713b961f14a726802a701eaca289ed28a4e56`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:cca713b961f14a726802a701eaca289ed28a4e56 -->
The new delta rating is used only to decide whether to create a carried marker; it does not determine whether the gate requires a human. result_comment still derives the critical marker solely from the overall answer. For example, if an approved head adds a critical protection and the new head removes it, the net PR can be rated critical: false while the delta is correctly rated since_approval_critical: true. No carried marker is created in that case, and gate_state sees a non-critical marker and passes without a current-head approval. Use the delta rating to require human review, or reject such answers, and add this net-revert scenario to the runner tests.
**[high] Delta ignored** (werkrevisor)
ORANGE; Jev: real 0.81, severity 3.0 of 3
The new delta rating is used only to decide whether to create a carried marker; it does not determine whether the gate requires a human. `result_comment` still derives the critical marker solely from the overall answer. For example, if an approved head adds a critical protection and the new head removes it, the net PR can be rated `critical: false` while the delta is correctly rated `since_approval_critical: true`. No carried marker is created in that case, and `gate_state` sees a non-critical marker and passes without a current-head approval. Use the delta rating to require human review, or reject such answers, and add this net-revert scenario to the runner tests.
Fixed in e1070e9: codex.needs_human is true when Codex rates the change since the approval as critical, also in a PR that is not critical as a whole; the result comment names the rating. New test test_a_critical_change_since_the_approval_needs_a_human_also_in_an_uncritical_pr, red without the fix.
Fixed in `e1070e9`: `codex.needs_human` is true when Codex rates the change since the approval as critical, also in a PR that is not critical as a whole; the result comment names the rating. New test `test_a_critical_change_since_the_approval_needs_a_human_also_in_an_uncritical_pr`, red without the fix.
claude-code marked this conversation as resolved
ai-reviewer
scheduled this pull request to auto merge when all checks succeed 2026-10-24 23:36:09 +02:00
ai-reviewer
requested review from mi 2026-10-24 23:36:09 +02:00
git log --cc omits resolutions that select one parent's content. If main adds a security check and a merge retains the PR parent's version without that check, this command shows no patch for the removal; main's commits are also excluded. The incremental assessment can consequently report no critical change and carry the approval even when the full review correctly marks the removal as critical.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Combined diffs hide security-relevant merge resolutions** (Codex review)
git log --cc omits resolutions that select one parent's content. If main adds a security check and a merge retains the PR parent's version without that check, this command shows no patch for the removal; main's commits are also excluded. The incremental assessment can consequently report no critical change and carry the approval even when the full review correctly marks the removal as critical.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in e1070e9: Codex now compares the change of the PR then and now, git diff <merge base of approved and base>..<approved> against git diff <base>..HEAD, also in binary files, modes and renames. A merge that keeps the PR side and drops a check of main shows as a removal in the second diff.
Fixed in `e1070e9`: Codex now compares the change of the PR then and now, `git diff <merge base of approved and base>..<approved>` against `git diff <base>..HEAD`, also in binary files, modes and renames. A merge that keeps the PR side and drops a check of `main` shows as a removal in the second diff.
[high] Block equality overlooks new binary changes (Codex review)
The markers contain only text-hunk fingerprints; binary changes, pure renames and mode-only changes contribute no blocks. After a rebase, retaining the approved text edits while adding an executable binary therefore satisfies this comparison. I reproduced the resulting Codex gate success despite critical=true and no current-head approval.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Block equality overlooks new binary changes** (Codex review)
The markers contain only text-hunk fingerprints; binary changes, pure renames and mode-only changes contribute no blocks. After a rebase, retaining the approved text edits while adding an executable binary therefore satisfies this comparison. I reproduced the resulting Codex gate success despite critical=true and no current-head approval.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in e1070e9: after a rebase the runner no longer compares the block fingerprints itself. It needs werkrevisor's own carry-over of the same human approval, "the changed blocks are those of the approved head"; werkrevisor counts a file without text hunks, e.g. a binary file, a mode change or a rename, as a new file and carries nothing over then. The test with equal fingerprints but no such carry-over is red with the old comparison.
Fixed in `e1070e9`: after a rebase the runner no longer compares the block fingerprints itself. It needs werkrevisor's own carry-over of the same human approval, "the changed blocks are those of the approved head"; werkrevisor counts a file without text hunks, e.g. a binary file, a mode change or a rename, as a new file and carries nothing over then. The test with equal fingerprints but no such carry-over is red with the old comparison.
This changes the human-approval requirement of a merge gate. Mistakes can authorize critical changes that no human reviewed.
Security effect: weakens; fails closed: no; a test of the closed case: no.
Findings
[high] Block equality overlooks new binary changes at werkrevisor/codexrunner.py:84, as a line comment
[high] Combined diffs hide security-relevant merge resolutions at werkrevisor/codex.py:96, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 375904 (cached 318848) / out 3555, 102.3 s, 8 commands; reviewed head `cca713b961f14a726802a701eaca289ed28a4e56`.
<!-- werkrevisor-codex:cca713b961f14a726802a701eaca289ed28a4e56 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the human-approval requirement of a merge gate. Mistakes can authorize critical changes that no human reviewed.
Security effect: weakens; fails closed: no; a test of the closed case: no.
### Findings
1. **[high] Block equality overlooks new binary changes** at `werkrevisor/codexrunner.py:84`, as a line comment
2. **[high] Combined diffs hide security-relevant merge resolutions** at `werkrevisor/codex.py:96`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 375904 (cached 318848) / out 3555, 102.3 s, 8 commands; reviewed head `cca713b961f14a726802a701eaca289ed28a4e56`.
</sub>
ORANGE 2 finding(s) that Jev rates as probably real and severe: werkrevisor/codexrunner.py:98-100, werkrevisor/codexrunner.py:80-87
YELLOW model reports a security concern: Gate bypass through injected carry-over metadata: PR-controlled content can influence model-produced text that the runner later interprets as proof of a human carry-over. With an existing earlier approval, this can suppress the required human review for a critical change. The issue is conditional on the model emitting the requested marker or sentence, but the current parsers provide no boundary ag…
hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 9 changed file(s) agree with the PR document according to Jev
hint 1 finding(s) of low severity or rated minor by Jev: docs/werkrevisor.werkbaum:167-168
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
[high] Marker Injection at werkrevisor/codexrunner.py:98-100, ORANGE, as a line comment
[high] Prose Authorization at werkrevisor/codexrunner.py:80-87, ORANGE, as a line comment
[medium] Missing Diagram at docs/werkrevisor.werkbaum:167-168
hint; Jev: real 0.78, severity 2.1 of 3
This adds a new Codex carry-over decision, but the PR does not update the review diagrams under docs/. AGENTS.md explicitly requires diagram updates whenever review steps or decisions change; leaving the diagrams unchanged documents the old rule that an earlier approval never unlocks ai-review/codex. Update the relevant diagrams in this PR.
Classifier
10 of 10 units passed all questions (1 PR document(s)).
2 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 47899, calls 1, tokens in 46032 / out 37758 (reasoning included in out), duration 325.7 s, cost 0.0591 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8545, cost 0.0004 USD, finding judge `jev-1.14.0` calls 3, tokens in 5254, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 31183, cost 0.0013 USD, build file judge `jev-1.14.0` calls 6, tokens in 9540, cost 0.0004 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 323.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).
Reviewed head `e1070e9f47e3307bcadef297c0c5b417f4d04676` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
<!-- werkrevisor:e1070e9f47e3307bcadef297c0c5b417f4d04676 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:b2180b261baac96380797752a4d48b6f,4c273a4f46ce36c0c75865c2776244e7,98dbf304cb39374c01def149aeb4812d,192c5cd9a1752e521d573e7873ef736d,5b27f011b98f4ef161abf6a6094bdf86,3a294666c0ef69d175ab06a2faf91b1c,85331beb05536072650bdc096fbcd2e3,9cb9caf8f1e801976beb76374f183c72,9b64b41da8a6843fe5feea88c6fe3a2d,20a53a1b136b74d33e7b216e827fbb67,41e34a9fb2cb2de1041016371b102b14,7c795b70a963944949e6990acb9250d5,6dff1467faf39b146ff3d312d2153798,3e4eb25edfbbd85ad2d8a749fd2df5d5,502bd7af6a897d12bf35de0ff4b3d4a3,053e0af124f937b2c77702fcf0d8d21a,538f8c2c6a06d42daacf1212446709af,1053490c8a87f87e3fbaeddd060f118e,c50024159824bb6db5d23b7347f1f9ad,d04f177c2bacdad342f5f0265d693f94,74f9b0b268d3b40040446f24216d1253,3d15e6b72f14924ee30ac447a1e2d066,0ad7f002864c5bf096376c43f1c6dd6c,d35bf98effa731f15175197be963fc9c,fdff8291871acbc8a844ae8bd1bd1785,875282dc370ffa8124eff8ebe3cc5260,1a44d1094c2979f1c1654acfc44bf6f6,1a7b28e746892ef23db0499bcbe44931,cfd7f7f971bd89c45c99e967ada9bf78,ff12758487d861f0d87ff76aaafa22e9,d76f51eef2f46ed71114bb29aac0dd9f,d0c4044ae93461a32181b87933d7addc,f0fe9acc179ab84565920fad0bf57876,35882e0d626a5d5be7d73fb9ea181bdd,4c35a0954003146eff2ab718a90c9a96,f5062072bc93a769c13632054e6a5e80,df83d50b9124754b74f6d41df62ef644,259e49114aea051c57401c9fe64bc9a8,5eb69675e125c7544260ff50235f88b7,c5c37337407c8c3314d4b605a79045d6,5da1e0572e4d4d07aaea616cc8bd11bc,877c6649fce93e6aadcbd3cf9b53b8d0,69f7878cc37efeabc5081aadc77af03e -->
<!-- werkrevisor:diagrams -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>e1070e9</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/144#issuecomment-8040">the result for <code>55ec69c</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 2 finding(s) that Jev rates as probably real and severe: werkrevisor/codexrunner.py:98-100, werkrevisor/codexrunner.py:80-87
- **YELLOW** model reports a security concern: Gate bypass through injected carry-over metadata: PR-controlled content can influence model-produced text that the runner later interprets as proof of a human carry-over. With an existing earlier approval, this can suppress the required human review for a critical change. The issue is conditional on the model emitting the requested marker or sentence, but the current parsers provide no boundary ag…
- **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 9 changed file(s) agree with the PR document according to Jev
- **hint** 1 finding(s) of low severity or rated minor by Jev: docs/werkrevisor.werkbaum:167-168
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **[high] Marker Injection** at `werkrevisor/codexrunner.py:98-100`, ORANGE, as a line comment
2. **[high] Prose Authorization** at `werkrevisor/codexrunner.py:80-87`, ORANGE, as a line comment
3. **[medium] Missing Diagram** at `docs/werkrevisor.werkbaum:167-168`
hint; Jev: real 0.78, severity 2.1 of 3
This adds a new Codex carry-over decision, but the PR does not update the review diagrams under `docs/`. `AGENTS.md` explicitly requires diagram updates whenever review steps or decisions change; leaving the diagrams unchanged documents the old rule that an earlier approval never unlocks `ai-review/codex`. Update the relevant diagrams in this PR.
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
2 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 47899, calls 1, tokens in 46032 / out 37758 (reasoning included in out), duration 325.7 s, cost 0.0591 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8545, cost 0.0004 USD, finding judge `jev-1.14.0` calls 3, tokens in 5254, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 31183, cost 0.0013 USD, build file judge `jev-1.14.0` calls 6, tokens in 9540, cost 0.0004 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 323.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).<br>
Reviewed head `e1070e9f47e3307bcadef297c0c5b417f4d04676` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
</sub>
### Diagrams
Drawn by a model from the diff with [PR Lens](https://github.com/coldteadotai/pr-lens): a view of what the PR touches, not a statement of what it does.


</details>
werkrevisor: ORANGE / FINDINGS for e1070e9f47e3307bcadef297c0c5b417f4d04676.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `e1070e9f47e3307bcadef297c0c5b417f4d04676`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:e1070e9f47e3307bcadef297c0c5b417f4d04676 -->
_same_blocks() treats an English sentence anywhere in the werkrevisor result as proof that a rebase preserved approved blocks. That result also contains model-generated reasons, so if the model follows a crafted instruction, an author can make it emit the exact sentence and SHA; the runner then skips the Codex comparison and carries the old approval even for a critical rebased change. Represent this fact with a dedicated machine-readable marker in a fixed section.
**[high] Prose Authorization** (werkrevisor)
ORANGE; Jev: real 0.89, severity 3.0 of 3
`_same_blocks()` treats an English sentence anywhere in the werkrevisor result as proof that a rebase preserved approved blocks. That result also contains model-generated reasons, so if the model follows a crafted instruction, an author can make it emit the exact sentence and SHA; the runner then skips the Codex comparison and carries the old approval even for a critical rebased change. Represent this fact with a dedicated machine-readable marker in a fixed section.
Fixed in 55ec69c: the runner no longer reads the werkrevisor result for the rebase case. It compares the change itself in git, codex.change_signature of <base then>..<approved> and <base>..HEAD.
Fixed in `55ec69c`: the runner no longer reads the werkrevisor result for the rebase case. It compares the change itself in git, `codex.change_signature` of `<base then>..<approved>` and `<base>..HEAD`.
carried_head() searches the entire result comment for a carry marker, but result_comment() places model-produced fields such as critical_reason and since_approval_reason in that same comment. A crafted PR description can ask Codex to echo a marker containing the known approved SHA; if the model complies, update_gate() treats it as a genuine carried approval and gate_state() suppresses the critical rating. Parse only a fixed bot-generated marker line, never model-produced text.
**[high] Marker Injection** (werkrevisor)
ORANGE; Jev: real 0.79, severity 3.0 of 3
`carried_head()` searches the entire result comment for a carry marker, but `result_comment()` places model-produced fields such as `critical_reason` and `since_approval_reason` in that same comment. A crafted PR description can ask Codex to echo a marker containing the known approved SHA; if the model complies, `update_gate()` treats it as a genuine carried approval and `gate_state()` suppresses the critical rating. Parse only a fixed bot-generated marker line, never model-produced text.
Fixed in 55ec69c: the runner reads its markers only from header, the marker lines at the start of its own comment; Codex text comes after the heading. This also covers the older markers werkrevisor-codex-unattached and the head marker. Test test_text_of_codex_never_passes_for_a_marker forges all three in the reasons; it is red with the old substring search.
Fixed in `55ec69c`: the runner reads its markers only from `header`, the marker lines at the start of its own comment; Codex text comes after the heading. This also covers the older markers `werkrevisor-codex-unattached` and the head marker. Test `test_text_of_codex_never_passes_for_a_marker` forges all three in the reasons; it is red with the old substring search.
[high] Rebase carry-over overlooks mode changes alongside approved text hunks (Codex review)
The werkrevisor carry-over message does not prove that file metadata stayed unchanged: its block comparison ignores modes on files containing approved text hunks. A rebased head can therefore add an executable-bit change to an already edited file and still satisfy _same_blocks. This bypasses the since-approval assessment and carries the approval even when Codex marks the current change critical. An in-memory reproduction with an additional 100644→100755 change produced a successful Codex gate. Validate metadata changes before accepting this carry-over.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Rebase carry-over overlooks mode changes alongside approved text hunks** (Codex review)
The werkrevisor carry-over message does not prove that file metadata stayed unchanged: its block comparison ignores modes on files containing approved text hunks. A rebased head can therefore add an executable-bit change to an already edited file and still satisfy _same_blocks. This bypasses the since-approval assessment and carries the approval even when Codex marks the current change critical. An in-memory reproduction with an additional 100644→100755 change produced a successful Codex gate. Validate metadata changes before accepting this carry-over.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 55ec69c: codex.change_signature hashes git diff --binary -M without line numbers, blob ids, similarity indexes and context lines, and keeps modes, renames and binary patches. A real-git test shows that a rebase keeps the signature and that a mode change, a binary file and a rename each change it; with the mode lines dropped, the test is red.
Fixed in `55ec69c`: `codex.change_signature` hashes `git diff --binary -M` without line numbers, blob ids, similarity indexes and context lines, and keeps modes, renames and binary patches. A real-git test shows that a rebase keeps the signature and that a mode change, a binary file and a rename each change it; with the mode lines dropped, the test is red.
This changes the human-approval merge gate. A mistake can allow unreviewed security-sensitive changes to merge, so human review is required.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Rebase carry-over overlooks mode changes alongside approved text hunks at werkrevisor/codexrunner.py:394, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 544024 (cached 475520) / out 3486, 105.6 s, 10 commands; reviewed head `e1070e9f47e3307bcadef297c0c5b417f4d04676`.
<!-- werkrevisor-codex:e1070e9f47e3307bcadef297c0c5b417f4d04676 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the human-approval merge gate. A mistake can allow unreviewed security-sensitive changes to merge, so human review is required.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Rebase carry-over overlooks mode changes alongside approved text hunks** at `werkrevisor/codexrunner.py:394`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 544024 (cached 475520) / out 3486, 105.6 s, 10 commands; reviewed head `e1070e9f47e3307bcadef297c0c5b417f4d04676`.
</sub>
[medium] Missing Diagram: does not hold, no change.
The diagrams show no decision of the Codex review: docs/werkrevisor-pr-workflow.svg has one box "Codex review after werkrevisor, optional; in gate mode it holds the merge", and review_decisions.py and review_inside.py do not name Codex.
YELLOW security concern (injected markers): fixed in 55ec69c with the line comments above.
Codex: critical. Correct: the PR changes when the Codex gate needs a human.
Answer to the review of `e1070e9`:
- **[medium] Missing Diagram:** does not hold, no change.
The diagrams show no decision of the Codex review: `docs/werkrevisor-pr-workflow.svg` has one box "Codex review after werkrevisor, optional; in gate mode it holds the merge", and `review_decisions.py` and `review_inside.py` do not name Codex.
- **YELLOW security concern (injected markers):** fixed in `55ec69c` with the line comments above.
- **Codex: critical.** Correct: the PR changes when the Codex gate needs a human.
werkrevisor: GREEN / PASS for 55ec69c797eea9179fe2e1b4f7200cac6b6708ed.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `55ec69c797eea9179fe2e1b4f7200cac6b6708ed`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:55ec69c797eea9179fe2e1b4f7200cac6b6708ed -->
ai-reviewer
removed review request for mi 2026-10-25 00:10:49 +02:00
[high] Change signatures treat edits to different functions as identical (Codex review)
Removing hunk locations and all context makes identical replacements elsewhere in the same file produce equal signatures. For example, restoring an approved return False → return True edit in a public-view permission function and applying it instead in an account-deletion permission function preserves the signature. _review then skips the since-approval assessment and carries the old approval; gate_state passes even when Codex marks the current head critical. This allows a new authorization change to merge without human approval.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Change signatures treat edits to different functions as identical** (Codex review)
Removing hunk locations and all context makes identical replacements elsewhere in the same file produce equal signatures. For example, restoring an approved `return False` → `return True` edit in a public-view permission function and applying it instead in an account-deletion permission function preserves the signature. `_review` then skips the since-approval assessment and carries the old approval; `gate_state` passes even when Codex marks the current head critical. This allows a new authorization change to merge without human approval.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 25ef3e2: the signature keeps the context lines and the function heading of each hunk; only the line numbers, blob ids and similarity indexes are dropped. The real-git test now applies the same return False → return True in another function and expects another signature; it is red with the old signature. A rebase that changes the context of an edit now needs a new approval.
Fixed in `25ef3e2`: the signature keeps the context lines and the function heading of each hunk; only the line numbers, blob ids and similarity indexes are dropped. The real-git test now applies the same `return False` → `return True` in another function and expects another signature; it is red with the old signature. A rebase that changes the context of an edit now needs a new approval.
This changes the merge gate’s authorization rules by allowing an earlier human approval to authorize a later head. A mistake can bypass required human review of security-sensitive changes.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Change signatures treat edits to different functions as identical at werkrevisor/codex.py:249, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 432911 (cached 374528) / out 3376, 102.6 s, 14 commands; reviewed head `55ec69c797eea9179fe2e1b4f7200cac6b6708ed`.
<!-- werkrevisor-codex:55ec69c797eea9179fe2e1b4f7200cac6b6708ed -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the merge gate’s authorization rules by allowing an earlier human approval to authorize a later head. A mistake can bypass required human review of security-sensitive changes.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Change signatures treat edits to different functions as identical** at `werkrevisor/codex.py:249`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 432911 (cached 374528) / out 3376, 102.6 s, 14 commands; reviewed head `55ec69c797eea9179fe2e1b4f7200cac6b6708ed`.
</sub>
werkrevisor: GREEN / PASS for 25ef3e22af2dbe1acebefcd887ea227f0d668998.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `25ef3e22af2dbe1acebefcd887ea227f0d668998`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:25ef3e22af2dbe1acebefcd887ea227f0d668998 -->
[high] Repeated blocks let different permission changes share an approval (Codex review)
Removing hunk positions and blob IDs makes distinct edits identical when their surrounding context repeats. Reproduced with JSON permission entries: changing allowed for one resource produces the same signature as changing it for another resource whose name lies outside the three context lines. After approval, a commit can restore the first permission and change the second. _review then treats these as the same change, skips the since-approval assessment, and carries the approval even when Codex marks the current change critical.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Repeated blocks let different permission changes share an approval** (Codex review)
Removing hunk positions and blob IDs makes distinct edits identical when their surrounding context repeats. Reproduced with JSON permission entries: changing `allowed` for one resource produces the same signature as changing it for another resource whose name lies outside the three context lines. After approval, a commit can restore the first permission and change the second. `_review` then treats these as the same change, skips the since-approval assessment, and carries the approval even when Codex marks the current change critical.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in e2582ae: the signature is now git diff --raw --full-index -M, i.e. path, modes and blob ids of each changed file before and after, so it compares the files exactly, without positions to lose. A rebase keeps the approval only if the base branch changed none of the PR's files; otherwise a human approves again. The real-git test has your case: JSON entries with repeated context, allowed changed for another resource; it is red with the old signature.
Fixed in `e2582ae`: the signature is now `git diff --raw --full-index -M`, i.e. path, modes and blob ids of each changed file before and after, so it compares the files exactly, without positions to lose. A rebase keeps the approval only if the base branch changed none of the PR's files; otherwise a human approves again. The real-git test has your case: JSON entries with repeated context, `allowed` changed for another resource; it is red with the old signature.
The change relaxes the merge gate by carrying human approvals across commits. An incorrect comparison can authorize critical changes that the human never approved.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Repeated blocks let different permission changes share an approval at werkrevisor/codex.py:253, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 437111 (cached 384640) / out 4330, 115.6 s, 12 commands; reviewed head `25ef3e22af2dbe1acebefcd887ea227f0d668998`.
<!-- werkrevisor-codex:25ef3e22af2dbe1acebefcd887ea227f0d668998 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
The change relaxes the merge gate by carrying human approvals across commits. An incorrect comparison can authorize critical changes that the human never approved.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Repeated blocks let different permission changes share an approval** at `werkrevisor/codex.py:253`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 437111 (cached 384640) / out 4330, 115.6 s, 12 commands; reviewed head `25ef3e22af2dbe1acebefcd887ea227f0d668998`.
</sub>
werkrevisor: GREEN / PASS for 10b62fa6875c9a825b7e923d2c73b3accf7996ea.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `10b62fa6875c9a825b7e923d2c73b3accf7996ea`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:10b62fa6875c9a825b7e923d2c73b3accf7996ea -->
[high] Ignored submodule changes can reuse an old approval (Codex review)
The signature command inherits submodule ignore settings instead of specifying --ignore-submodules=none. If a submodule has ignore=all in .gitmodules or Git configuration, changing its recorded commit can leave the signature unchanged. The runner then carries the old approval through same_change, skips the incremental criticality assessment, and can pass the gate even when the overall review remains critical. Include ignored gitlink changes in the signature.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Ignored submodule changes can reuse an old approval** (Codex review)
The signature command inherits submodule ignore settings instead of specifying --ignore-submodules=none. If a submodule has ignore=all in .gitmodules or Git configuration, changing its recorded commit can leave the signature unchanged. The runner then carries the old approval through same_change, skips the incremental criticality assessment, and can pass the gate even when the overall review remains critical. Include ignored gitlink changes in the signature.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 5eb5a92: --ignore-submodules=none. The real-git test adds a submodule with ignore = all in .gitmodules and moves its commit; the signature changes, and without the flag the test is red.
Fixed in `5eb5a92`: `--ignore-submodules=none`. The real-git test adds a submodule with `ignore = all` in `.gitmodules` and moves its commit; the signature changes, and without the flag the test is red.
This changes the merge authorization gate by allowing an earlier human approval to authorize a later head. A mistake can permit unapproved security-sensitive changes to merge.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Ignored submodule changes can reuse an old approval at werkrevisor/codex.py:242, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 448107 (cached 389888) / out 3142, 94.1 s, 8 commands; reviewed head `10b62fa6875c9a825b7e923d2c73b3accf7996ea`.
<!-- werkrevisor-codex:10b62fa6875c9a825b7e923d2c73b3accf7996ea -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the merge authorization gate by allowing an earlier human approval to authorize a later head. A mistake can permit unapproved security-sensitive changes to merge.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Ignored submodule changes can reuse an old approval** at `werkrevisor/codex.py:242`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 448107 (cached 389888) / out 3142, 94.1 s, 8 commands; reviewed head `10b62fa6875c9a825b7e923d2c73b3accf7996ea`.
</sub>
werkrevisor: GREEN / PASS for 5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2 -->
[high] Text decoding makes distinct paths share an approval signature (Codex review)
change_signature passes Git's NUL-delimited raw output through _git, whose text=True converts carriage returns and CRLF to LF. Thus edits with identical modes and before/after blobs in distinct valid paths such as "policy\r.json" and "policy\n.json" produce identical signatures; this was reproduced without modifying files. Moving the approved edit between those paths makes same_change true, skips the since-approval assessment and carries the approval even when the current review rates the PR critical. Hash the raw subprocess bytes without newline conversion.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Text decoding makes distinct paths share an approval signature** (Codex review)
change_signature passes Git's NUL-delimited raw output through _git, whose text=True converts carriage returns and CRLF to LF. Thus edits with identical modes and before/after blobs in distinct valid paths such as "policy\r.json" and "policy\n.json" produce identical signatures; this was reproduced without modifying files. Moving the approved edit between those paths makes same_change true, skips the since-approval assessment and carries the approval even when the current review rates the PR critical. Hash the raw subprocess bytes without newline conversion.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in bc85826: the signature hashes the raw bytes of git diff --raw -z, without text mode. The real-git test commits p\r.json and p\n.json and expects different signatures; it is red in text mode.
Fixed in `bc85826`: the signature hashes the raw bytes of `git diff --raw -z`, without text mode. The real-git test commits `p\r.json` and `p\n.json` and expects different signatures; it is red in text mode.
The change relaxes a merge gate by allowing earlier human approvals to authorize later heads. A mistake can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Text decoding makes distinct paths share an approval signature at werkrevisor/codex.py:243, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 388134 (cached 331392) / out 2876, 73.7 s, 15 commands; reviewed head `5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2`.
<!-- werkrevisor-codex:5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
The change relaxes a merge gate by allowing earlier human approvals to authorize later heads. A mistake can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Text decoding makes distinct paths share an approval signature** at `werkrevisor/codex.py:243`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 388134 (cached 331392) / out 2876, 73.7 s, 15 commands; reviewed head `5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2`.
</sub>
Gate: approved by werkrevisor; merge waits for 1 open hint(s): reply to each line comment and resolve it, then restart the review or push a commit.
Change kind: mixed.
Reasons
hint approval of werkrevisor for 5eb5a92 not carried over: 3 approved block(s) are missing
hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 9 changed file(s) agree with the PR document according to Jev
hint 1 finding(s) of low severity or rated minor by Jev: werkrevisor/codex.py:261-262
hint model recommends merge with caution
Findings
[medium] Missing Test at werkrevisor/codex.py:261-262, hint, as a line comment
Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 54200, calls 1, tokens in 51974 / out 46591 (reasoning included in out), duration 344.7 s, cost 0.0715 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8889, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 1876, cost 0.0001 USD, PR document check `jev-1.14.0` calls 9, tokens in 37792, cost 0.0016 USD, build file judge `jev-1.14.0` calls 6, tokens in 12040, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 341.9 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).
Reviewed head `bc858266f6d6a4e1601249fab213694141b6db6b` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
<!-- werkrevisor:bc858266f6d6a4e1601249fab213694141b6db6b -->
<!-- werkrevisor-verdict:GREEN -->
<!-- werkrevisor-blocks:7a8fbe7f52c5919879bb818848bddb1e,120abc8b6c520d3651e22156bd3c40d8,be800ae5568998b11c96380845136b43,fdbe83f98dea4a3ab59a4044c3c3cfc1,5b27f011b98f4ef161abf6a6094bdf86,bc85225286b799ef43c6abb00a4391d4,02e08c13875da42b30b662bf2a2fd893,85331beb05536072650bdc096fbcd2e3,9cb9caf8f1e801976beb76374f183c72,9b64b41da8a6843fe5feea88c6fe3a2d,20a53a1b136b74d33e7b216e827fbb67,41e34a9fb2cb2de1041016371b102b14,7c795b70a963944949e6990acb9250d5,0a1dac6a6bdbf5a2d44c6458e856de8a,3e4eb25edfbbd85ad2d8a749fd2df5d5,502bd7af6a897d12bf35de0ff4b3d4a3,bf365a3bcb305a615ff1798fc62a06dd,a11b334f863b00d49e91fa0ecead64c5,398377fcd6e00f58cd441cc2b70977a9,272870f8c25b932beb717bc192a9da73,d04f177c2bacdad342f5f0265d693f94,55eee2ba855de88b046d2dac19593db6,74f9b0b268d3b40040446f24216d1253,3d15e6b72f14924ee30ac447a1e2d066,0ad7f002864c5bf096376c43f1c6dd6c,d35bf98effa731f15175197be963fc9c,fdff8291871acbc8a844ae8bd1bd1785,875282dc370ffa8124eff8ebe3cc5260,1a44d1094c2979f1c1654acfc44bf6f6,f02255b53237a737bb74c11a181799a7,39b3f8dd4433d34de764e4e4572a26b5,cfd7f7f971bd89c45c99e967ada9bf78,ff12758487d861f0d87ff76aaafa22e9,711f7468f2c9778c031752ce3fa111a1,d0c4044ae93461a32181b87933d7addc,f0fe9acc179ab84565920fad0bf57876,819025a78f3f49de94a11a0953302348,dd2f516470158da1ce1d6a3c93b69090,ea228fdf784700df7d0cc5aebe6afedf,f5062072bc93a769c13632054e6a5e80,df83d50b9124754b74f6d41df62ef644,05296b701b8d5b64cdf5ca889f530d6c,d054b597bc009320c3684b0233369e72,c5c37337407c8c3314d4b605a79045d6,d2adb1c8d7570e08f19553ccced8106e,545cf8a2396ffbf9aedfe7d0298e2824,b2100049046f0cebeb19f5e475450af6 -->
<!-- werkrevisor-held-by-line-comments -->
<!-- werkrevisor-folded -->
<details><summary>🟢 werkrevisor GREEN for <code>bc85826</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/144#issuecomment-8085">the result for <code>eb07159</code></a></summary>
## 🟢 werkrevisor: GREEN / PASS
Gate: approved by werkrevisor; merge waits for 1 open hint(s): reply to each line comment and resolve it, then restart the review or push a commit.
Change kind: `mixed`.
### Reasons
- **hint** approval of werkrevisor for `5eb5a92` not carried over: 3 approved block(s) are missing
- **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 9 changed file(s) agree with the PR document according to Jev
- **hint** 1 finding(s) of low severity or rated minor by Jev: werkrevisor/codex.py:261-262
- **hint** model recommends merge with caution
### Findings
1. **[medium] Missing Test** at `werkrevisor/codex.py:261-262`, hint, as a line comment
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 54200, calls 1, tokens in 51974 / out 46591 (reasoning included in out), duration 344.7 s, cost 0.0715 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8889, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 1876, cost 0.0001 USD, PR document check `jev-1.14.0` calls 9, tokens in 37792, cost 0.0016 USD, build file judge `jev-1.14.0` calls 6, tokens in 12040, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 341.9 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).<br>
Reviewed head `bc858266f6d6a4e1601249fab213694141b6db6b` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
</sub>
</details>
werkrevisor: GREEN / PASS for bc858266f6d6a4e1601249fab213694141b6db6b.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `bc858266f6d6a4e1601249fab213694141b6db6b`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:bc858266f6d6a4e1601249fab213694141b6db6b -->
The new refs/reviewed/<number>/<head> ref, which keeps an earlier head available after a force push, has no test. The rebase carry-over depends on it: if the update-ref call is dropped or misspelled, the old head disappears from the mirror and the comparison against it fails silently while the test suite stays green. tests/test_codex.py::test_a_token_reaches_only_its_own_server already records every _git call, so asserting that the ref is created there would cover the behavior.
**[medium] Missing Test** (werkrevisor)
hint; Jev: real 0.70, severity 2.1 of 3
The new `refs/reviewed/<number>/<head>` ref, which keeps an earlier head available after a force push, has no test. The rebase carry-over depends on it: if the `update-ref` call is dropped or misspelled, the old head disappears from the mirror and the comparison against it fails silently while the test suite stays green. `tests/test_codex.py::test_a_token_reaches_only_its_own_server` already records every `_git` call, so asserting that the ref is created there would cover the behavior.
[high] Compare against the base used for the approved review (Codex review)
The approved base is reconstructed from the current base rather than preserved from the approved review. Suppose A was approved against T, then the PR is retargeted to an ancestor B of T and receives an empty commit H. Both signatures now compare against B and match, although B..T was absent from the reviewed diff T..A. The same_change path therefore carries the approval and unlocks even a critical result for H. Preserve and validate the original review base before carrying an approval.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Compare against the base used for the approved review** (Codex review)
The approved base is reconstructed from the current base rather than preserved from the approved review. Suppose A was approved against T, then the PR is retargeted to an ancestor B of T and receives an empty commit H. Both signatures now compare against B and match, although B..T was absent from the reviewed diff T..A. The same_change path therefore carries the approval and unlocks even a critical result for H. Preserve and validate the original review base before carrying an approval.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in eb07159: each Codex result comment records its base in the header marker werkrevisor-codex-base; the carry-over takes the base of the approved review from there, for the rebase comparison and for the comparison by Codex, and a comment without it carries nothing over. codex.checkout keeps a ref of that base too. Test test_the_approved_change_is_taken_from_the_base_of_its_review is red with the current base.
Fixed in `eb07159`: each Codex result comment records its base in the header marker `werkrevisor-codex-base`; the carry-over takes the base of the approved review from there, for the rebase comparison and for the comparison by Codex, and a comment without it carries nothing over. `codex.checkout` keeps a ref of that base too. Test `test_the_approved_change_is_taken_from_the_base_of_its_review` is red with the current base.
This changes merge authorization by allowing earlier human approvals to unlock later heads. An incorrect comparison can permit critical changes that the human never reviewed.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Compare against the base used for the approved review at werkrevisor/codexrunner.py:86, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 462167 (cached 400128) / out 3220, 114.8 s, 5 commands; reviewed head `bc858266f6d6a4e1601249fab213694141b6db6b`.
<!-- werkrevisor-codex:bc858266f6d6a4e1601249fab213694141b6db6b -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes merge authorization by allowing earlier human approvals to unlock later heads. An incorrect comparison can permit critical changes that the human never reviewed.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Compare against the base used for the approved review** at `werkrevisor/codexrunner.py:86`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 462167 (cached 400128) / out 3220, 114.8 s, 5 commands; reviewed head `bc858266f6d6a4e1601249fab213694141b6db6b`.
</sub>
ORANGE 2 finding(s) that Jev rates as probably real and severe: werkrevisor/codexrunner.py:428-430, werkrevisor/codex.py:257-259
YELLOW model reports a security concern: Merge-gate authorization bypass: after a rebase changes the diff, the gate can pass without a human whenever the whole-PR rating is noncritical, despite the documented requirement to wait for approval. Persistent disk-exhaustion risk: every reviewed head and base is retained under permanent refs without cleanup.
hint approval of werkrevisor for bc85826 not carried over: 17 approved block(s) are missing
hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.02, risk 0.00, security 0.02)
hint 9 changed file(s) agree with the PR document according to Jev
hint 1 finding(s) of low severity or rated minor by Jev: docs/werkrevisor.werkbaum:167-168
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
[high] Approval Bypass at werkrevisor/codexrunner.py:428-430, ORANGE, as a line comment
[medium] Ref Growth at werkrevisor/codex.py:257-259, ORANGE, as a line comment
[medium] Stale Diagram at docs/werkrevisor.werkbaum:167-168
hint; Jev: real 0.67, severity 2.0 of 3
This records a new Codex gate carry-over decision, but no corresponding review-flow diagram under docs/ is updated. Repository guidance requires diagrams to change with review decisions; leaving them unchanged will continue to depict the former no-carry-over behavior and can mislead operators.
Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 55828, calls 1, tokens in 53536 / out 42947 (reasoning included in out), duration 338.7 s, cost 0.0676 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8976, cost 0.0004 USD, finding judge `jev-1.14.0` calls 3, tokens in 5815, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 39239, cost 0.0016 USD, build file judge `jev-1.14.0` calls 6, tokens in 12687, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.2 s, model_review 336.0 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).
Reviewed head `eb071591938c74e73bb4112178e12f20b98437d5` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
<!-- werkrevisor:eb071591938c74e73bb4112178e12f20b98437d5 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:9401908dbde51d314d14c9934b9555bf,120abc8b6c520d3651e22156bd3c40d8,abf6f077cfba8b44b7f146b7022961db,fdbe83f98dea4a3ab59a4044c3c3cfc1,5b27f011b98f4ef161abf6a6094bdf86,bc85225286b799ef43c6abb00a4391d4,02e08c13875da42b30b662bf2a2fd893,2f4e94e1753d270ee21874d98d708c77,85331beb05536072650bdc096fbcd2e3,9cb9caf8f1e801976beb76374f183c72,9b64b41da8a6843fe5feea88c6fe3a2d,20a53a1b136b74d33e7b216e827fbb67,41e34a9fb2cb2de1041016371b102b14,c7f8a3adfb9333eb2d1554f67852a2b1,5b9a6e5840d063b53c014589e177bb86,3e4eb25edfbbd85ad2d8a749fd2df5d5,502bd7af6a897d12bf35de0ff4b3d4a3,b30ad6ba658018cf7af5055eab7f7146,bfcd488139d5702725b2545be3427bf2,dab6b66a88bb34c277d9665b42cde467,20db7908e6fa7e66f24063a8d004f4ef,d04f177c2bacdad342f5f0265d693f94,55eee2ba855de88b046d2dac19593db6,74f9b0b268d3b40040446f24216d1253,3d15e6b72f14924ee30ac447a1e2d066,0ad7f002864c5bf096376c43f1c6dd6c,d35bf98effa731f15175197be963fc9c,fdff8291871acbc8a844ae8bd1bd1785,875282dc370ffa8124eff8ebe3cc5260,1a44d1094c2979f1c1654acfc44bf6f6,cbe4b7aed3189bcb3e584ec4d7059c7a,4d5d1dc2ddd093d7ed9d870625509567,cfd7f7f971bd89c45c99e967ada9bf78,ff12758487d861f0d87ff76aaafa22e9,711f7468f2c9778c031752ce3fa111a1,d0c4044ae93461a32181b87933d7addc,f0fe9acc179ab84565920fad0bf57876,cf1e5b36a876def95b01f39b180a64bf,ecbb748d43cb61b3712490863b65046b,ea228fdf784700df7d0cc5aebe6afedf,5e8d70bd73ae2285f55f979afada34a5,5b7cdd9b49cd6348b02bb4b14e84efc3,05296b701b8d5b64cdf5ca889f530d6c,d054b597bc009320c3684b0233369e72,c5c37337407c8c3314d4b605a79045d6,3ddf280aff4c8da1e01858ddf2e0a2ff,f3bb1034aa429ac1453e35e9c6653044,257dba5543ddebc4b3999a7d808e7661 -->
<!-- werkrevisor:diagrams -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>eb07159</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/144#issuecomment-8096">the result for <code>992fbb3</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 2 finding(s) that Jev rates as probably real and severe: werkrevisor/codexrunner.py:428-430, werkrevisor/codex.py:257-259
- **YELLOW** model reports a security concern: Merge-gate authorization bypass: after a rebase changes the diff, the gate can pass without a human whenever the whole-PR rating is noncritical, despite the documented requirement to wait for approval. Persistent disk-exhaustion risk: every reviewed head and base is retained under permanent refs without cleanup.
- **hint** approval of werkrevisor for `bc85826` not carried over: 17 approved block(s) are missing
- **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.02, risk 0.00, security 0.02)
- **hint** 9 changed file(s) agree with the PR document according to Jev
- **hint** 1 finding(s) of low severity or rated minor by Jev: docs/werkrevisor.werkbaum:167-168
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **[high] Approval Bypass** at `werkrevisor/codexrunner.py:428-430`, ORANGE, as a line comment
2. **[medium] Ref Growth** at `werkrevisor/codex.py:257-259`, ORANGE, as a line comment
3. **[medium] Stale Diagram** at `docs/werkrevisor.werkbaum:167-168`
hint; Jev: real 0.67, severity 2.0 of 3
This records a new Codex gate carry-over decision, but no corresponding review-flow diagram under `docs/` is updated. Repository guidance requires diagrams to change with review decisions; leaving them unchanged will continue to depict the former no-carry-over behavior and can mislead operators.
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 55828, calls 1, tokens in 53536 / out 42947 (reasoning included in out), duration 338.7 s, cost 0.0676 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8976, cost 0.0004 USD, finding judge `jev-1.14.0` calls 3, tokens in 5815, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 39239, cost 0.0016 USD, build file judge `jev-1.14.0` calls 6, tokens in 12687, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.2 s, model_review 336.0 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).<br>
Reviewed head `eb071591938c74e73bb4112178e12f20b98437d5` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
</sub>
### Diagrams
Drawn by a model from the diff with [PR Lens](https://github.com/coldteadotai/pr-lens): a view of what the PR touches, not a statement of what it does.


</details>
werkrevisor: ORANGE / FINDINGS for eb071591938c74e73bb4112178e12f20b98437d5.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `eb071591938c74e73bb4112178e12f20b98437d5`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:eb071591938c74e73bb4112178e12f20b98437d5 -->
Every checkout creates permanent refs/reviewed/... refs for the head and base, and no code removes them. Because these refs keep old commits and blobs reachable, repeatedly reviewed or pushed heads cause the runner's bare mirror to grow indefinitely; a contributor can amplify this by repeatedly pushing heads. Retain only refs needed by live approvals or add deterministic pruning.
**[medium] Ref Growth** (werkrevisor)
ORANGE; Jev: real 0.85, severity 2.8 of 3
Every checkout creates permanent `refs/reviewed/...` refs for the head and base, and no code removes them. Because these refs keep old commits and blobs reachable, repeatedly reviewed or pushed heads cause the runner's bare mirror to grow indefinitely; a contributor can amplify this by repeatedly pushing heads. Retain only refs needed by live approvals or add deterministic pruning.
Fixed in 992fbb3: codex.checkout keeps the refs of at most MAX_REVIEWED_HEADS = 20 reviewed heads per PR and deletes older ones with their base; an approval of an older head carries nothing over. The real-git test checks the pruning, and that another PR keeps its refs; it is red without the pruning.
Fixed in `992fbb3`: `codex.checkout` keeps the refs of at most `MAX_REVIEWED_HEADS` = 20 reviewed heads per PR and deletes older ones with their base; an approval of an older head carries nothing over. The real-git test checks the pruning, and that another PR keeps its refs; it is red without the pruning.
A rebased head with a changed diff does not necessarily wait for a human. The since rating is only enabled when the approved head remains an ancestor; after a rebase with a different signature, no carried marker is emitted, so the gate passes whenever Codex rates the whole PR as noncritical. This contradicts the documented rule that a rebase with any other change waits for a human. The new test does not expose this because it always uses a critical whole-PR rating.
**[high] Approval Bypass** (werkrevisor)
ORANGE; Jev: real 0.76, severity 3.0 of 3
A rebased head with a changed diff does not necessarily wait for a human. The `since` rating is only enabled when the approved head remains an ancestor; after a rebase with a different signature, no carried marker is emitted, so the gate passes whenever Codex rates the whole PR as noncritical. This contradicts the documented rule that a rebase with any other change waits for a human. The new test does not expose this because it always uses a critical whole-PR rating.
Does not hold, no change. After a rebase with another change the approval does not carry over: no carried marker, so the gate treats the head as one without an earlier approval. A head that Codex rates as not critical as a whole needs no human in the Codex gate, before this PR and after it; an earlier approval cannot make it stricter than a PR without one. Scenario#144.03 is about a critical PR ("the same PR"), and there the gate waits: test_a_rebase_with_the_same_blocks_keeps_the_approval, case other.
Does not hold, no change. After a rebase with another change the approval does not carry over: no carried marker, so the gate treats the head as one without an earlier approval. A head that Codex rates as not critical as a whole needs no human in the Codex gate, before this PR and after it; an earlier approval cannot make it stricter than a PR without one. Scenario#144.03 is about a critical PR ("the same PR"), and there the gate waits: `test_a_rebase_with_the_same_blocks_keeps_the_approval`, case `other`.
claude-code marked this conversation as resolved
ai-reviewer
scheduled this pull request to auto merge when all checks succeed 2026-10-25 01:28:04 +02:00
ai-reviewer
requested review from mi 2026-10-25 01:28:04 +02:00
The change relaxes the merge gate by allowing approval of an older head to authorize a newer one. A mistake could permit critical changes to merge without human approval.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 238791 (cached 175232) / out 1787, 47.6 s, 10 commands; reviewed head `eb071591938c74e73bb4112178e12f20b98437d5`.
<!-- werkrevisor-codex:eb071591938c74e73bb4112178e12f20b98437d5 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
The change relaxes the merge gate by allowing approval of an older head to authorize a newer one. A mistake could permit critical changes to merge without human approval.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 238791 (cached 175232) / out 1787, 47.6 s, 10 commands; reviewed head `eb071591938c74e73bb4112178e12f20b98437d5`.
</sub>
[high] Approval Bypass: does not hold, answered in its thread; left open for the human.
[medium] Ref Growth: fixed in 992fbb3.
[medium] Stale Diagram: does not hold, no change: the diagrams show no decision of the Codex review, as answered for e1070e9.
Codex: critical, no findings. Correct: the PR changes when the Codex gate needs a human.
Answer to the review of `eb07159`:
- **[high] Approval Bypass:** does not hold, answered in its thread; left open for the human.
- **[medium] Ref Growth:** fixed in `992fbb3`.
- **[medium] Stale Diagram:** does not hold, no change: the diagrams show no decision of the Codex review, as answered for `e1070e9`.
- **Codex: critical, no findings.** Correct: the PR changes when the Codex gate needs a human.
Gate: approved by werkrevisor; merge waits for 3 open hint(s): reply to each line comment and resolve it; answer the 2 hint(s) that stand only in the result comment in a PR comment, then restart the review or push a commit.
Change kind: mixed.
Reasons
hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.02, risk 0.00, security 0.02)
hint 9 changed file(s) agree with the PR document according to Jev
hint 2 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-25-PR#144-codex-carry-over.md, docs/prs/2026-10-25-PR#144-codex-carry-over.md
hint model recommends merge with caution
Findings
[low] Incorrect bound at docs/prs/2026-10-25-PR#144-codex-carry-over.md
hint
The document states that an approval of a head older than the 20 newest reviewed heads carries nothing over. The code only deletes the refs of the pruned heads (_prune_reviewed); the commits themselves stay reachable in the mirror as long as that head is an ancestor of the current head, so has_commit still succeeds and the approval still carries over. Only after a force push or a rebase, where the old head is no longer reachable, does the approval really stop carrying over. Either the documented bound must be corrected or the code must check the refs instead of the objects.
[low] Wrong git mechanism at docs/prs/2026-10-25-PR#144-codex-carry-over.md
hint
The document claims that ignore = all in the .gitmodules of the PR can hide a new submodule commit from the change signature and that --ignore-submodules=none prevents it. In Git, the ignore attribute of .gitmodules only affects git submodule commands and never git diff; only diff.ignoreSubmodules and submodule.<name>.ignore of the repository configuration do, and that configuration belongs to the runner's mirror, not to the PR. The flag is harmless, but the documented scenario does not exist and the new test does not cover what it claims.
Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 56928, calls 1, tokens in 54563 / out 56011 (reasoning included in out), duration 397.0 s, cost 0.0836 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9025, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, PR document check `jev-1.14.0` calls 9, tokens in 40152, cost 0.0017 USD, build file judge `jev-1.14.0` calls 6, tokens in 13159, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 394.5 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).
Reviewed head `992fbb3d7335f188198d721466c4dab38ce0d777` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
<!-- werkrevisor:992fbb3d7335f188198d721466c4dab38ce0d777 -->
<!-- werkrevisor-verdict:GREEN -->
<!-- werkrevisor-blocks:9401908dbde51d314d14c9934b9555bf,120abc8b6c520d3651e22156bd3c40d8,404ec68adcc9a0f9484f7123c41d7295,fdbe83f98dea4a3ab59a4044c3c3cfc1,5b27f011b98f4ef161abf6a6094bdf86,bc85225286b799ef43c6abb00a4391d4,18b8b1ca5104a6e8466aa3f514002f8a,2f4e94e1753d270ee21874d98d708c77,85331beb05536072650bdc096fbcd2e3,9cb9caf8f1e801976beb76374f183c72,9b64b41da8a6843fe5feea88c6fe3a2d,20a53a1b136b74d33e7b216e827fbb67,41e34a9fb2cb2de1041016371b102b14,c7f8a3adfb9333eb2d1554f67852a2b1,5b9a6e5840d063b53c014589e177bb86,3e4eb25edfbbd85ad2d8a749fd2df5d5,502bd7af6a897d12bf35de0ff4b3d4a3,b30ad6ba658018cf7af5055eab7f7146,bfcd488139d5702725b2545be3427bf2,dab6b66a88bb34c277d9665b42cde467,20db7908e6fa7e66f24063a8d004f4ef,d04f177c2bacdad342f5f0265d693f94,55eee2ba855de88b046d2dac19593db6,74f9b0b268d3b40040446f24216d1253,3d15e6b72f14924ee30ac447a1e2d066,0ad7f002864c5bf096376c43f1c6dd6c,d35bf98effa731f15175197be963fc9c,fdff8291871acbc8a844ae8bd1bd1785,875282dc370ffa8124eff8ebe3cc5260,1a44d1094c2979f1c1654acfc44bf6f6,a371fdaa0567661b73c5874b45de5e40,ea22f632e070fa7b7856d7e9a8322327,cfd7f7f971bd89c45c99e967ada9bf78,ff12758487d861f0d87ff76aaafa22e9,711f7468f2c9778c031752ce3fa111a1,d0c4044ae93461a32181b87933d7addc,f0fe9acc179ab84565920fad0bf57876,cf1e5b36a876def95b01f39b180a64bf,ecbb748d43cb61b3712490863b65046b,ea228fdf784700df7d0cc5aebe6afedf,5e8d70bd73ae2285f55f979afada34a5,5b7cdd9b49cd6348b02bb4b14e84efc3,05296b701b8d5b64cdf5ca889f530d6c,d054b597bc009320c3684b0233369e72,c5c37337407c8c3314d4b605a79045d6,3ddf280aff4c8da1e01858ddf2e0a2ff,f3bb1034aa429ac1453e35e9c6653044,257dba5543ddebc4b3999a7d808e7661 -->
<!-- werkrevisor-folded -->
<details><summary>🟢 werkrevisor GREEN for <code>992fbb3</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/144#issuecomment-8103">the result for <code>9567fe5</code></a></summary>
## 🟢 werkrevisor: GREEN / PASS
Gate: approved by werkrevisor; merge waits for 3 open hint(s): reply to each line comment and resolve it; answer the 2 hint(s) that stand only in the result comment in a PR comment, then restart the review or push a commit.
Change kind: `mixed`.
### Reasons
- **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.02, risk 0.00, security 0.02)
- **hint** 9 changed file(s) agree with the PR document according to Jev
- **hint** 2 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-25-PR#144-codex-carry-over.md, docs/prs/2026-10-25-PR#144-codex-carry-over.md
- **hint** model recommends merge with caution
### Findings
1. **[low] Incorrect bound** at `docs/prs/2026-10-25-PR#144-codex-carry-over.md`
hint
The document states that an approval of a head older than the 20 newest reviewed heads carries nothing over. The code only deletes the refs of the pruned heads (`_prune_reviewed`); the commits themselves stay reachable in the mirror as long as that head is an ancestor of the current head, so `has_commit` still succeeds and the approval still carries over. Only after a force push or a rebase, where the old head is no longer reachable, does the approval really stop carrying over. Either the documented bound must be corrected or the code must check the refs instead of the objects.
2. **[low] Wrong git mechanism** at `docs/prs/2026-10-25-PR#144-codex-carry-over.md`
hint
The document claims that `ignore = all` in the `.gitmodules` of the PR can hide a new submodule commit from the change signature and that `--ignore-submodules=none` prevents it. In Git, the `ignore` attribute of `.gitmodules` only affects `git submodule` commands and never `git diff`; only `diff.ignoreSubmodules` and `submodule.<name>.ignore` of the repository configuration do, and that configuration belongs to the runner's mirror, not to the PR. The flag is harmless, but the documented scenario does not exist and the new test does not cover what it claims.
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 56928, calls 1, tokens in 54563 / out 56011 (reasoning included in out), duration 397.0 s, cost 0.0836 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9025, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, PR document check `jev-1.14.0` calls 9, tokens in 40152, cost 0.0017 USD, build file judge `jev-1.14.0` calls 6, tokens in 13159, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 394.5 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).<br>
Reviewed head `992fbb3d7335f188198d721466c4dab38ce0d777` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
</sub>
</details>
werkrevisor: GREEN / PASS for 992fbb3d7335f188198d721466c4dab38ce0d777.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `992fbb3d7335f188198d721466c4dab38ce0d777`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:992fbb3d7335f188198d721466c4dab38ce0d777 -->
ai-reviewer
canceled auto merging this pull request when all checks succeed 2026-10-25 01:38:48 +02:00
This changes a merge gate to accept earlier human approvals for later commits. A mistake could let critical changes merge without the required human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 351336 (cached 291200) / out 2549, 65.6 s, 16 commands; reviewed head `992fbb3d7335f188198d721466c4dab38ce0d777`.
<!-- werkrevisor-codex:992fbb3d7335f188198d721466c4dab38ce0d777 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes a merge gate to accept earlier human approvals for later commits. A mistake could let critical changes merge without the required human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 351336 (cached 291200) / out 2549, 65.6 s, 16 commands; reviewed head `992fbb3d7335f188198d721466c4dab38ce0d777`.
</sub>
[low] Incorrect bound: holds; fixed in 9567fe5. The PR document now says that an older head stays in the mirror while it is an ancestor of the PR head, and that the bound takes effect after a force push.
[low] Wrong git mechanism: does not hold, no change. git diff follows submodule.<name>.ignore from .gitmodules (git-config: "the corresponding setting in .gitmodules" is used when the repository config sets none). The real-git test shows it: the subtest "ignored submodule" is red without --ignore-submodules=none and green with it.
[high] Approval Bypass (round 8): answered in its thread; resolved, as an open thread holds the merge. The human decides with the approval.
Answer to the review of `992fbb3`:
- **[low] Incorrect bound:** holds; fixed in `9567fe5`. The PR document now says that an older head stays in the mirror while it is an ancestor of the PR head, and that the bound takes effect after a force push.
- **[low] Wrong git mechanism:** does not hold, no change. `git diff` follows `submodule.<name>.ignore` from `.gitmodules` (git-config: "the corresponding setting in .gitmodules" is used when the repository config sets none). The real-git test shows it: the subtest "ignored submodule" is red without `--ignore-submodules=none` and green with it.
- **[high] Approval Bypass** (round 8): answered in its thread; resolved, as an open thread holds the merge. The human decides with the approval.
ORANGE 1 finding(s) that Jev rates as probably real and severe: werkrevisor/codexrunner.py:394-400
YELLOW model reports a security concern: Stale-base approval bypass: when an unchanged PR head is retargeted to a different base, the runner reuses the old result and can honor its carried-approval marker without reviewing the changed diff, allowing the merge gate to pass without a human reviewing that difference.
hint approval of werkrevisor for 992fbb3 not carried over: 1 approved block(s) are missing
hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 9 changed file(s) agree with the PR document according to Jev
hint 2 finding(s) of low severity or rated minor by Jev: docs/werkrevisor.werkbaum:167-168, README.md:1071-1072
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
[high] Stale Base at werkrevisor/codexrunner.py:394-400, ORANGE, as a line comment
[medium] Missing Diagrams at docs/werkrevisor.werkbaum:167-168
hint; Jev: real 0.68, severity 1.9 of 3
The new carry-over decision changes the Codex review flow, but no diagram under docs/ is updated. Repository guidance requires diagrams to change whenever review steps or decisions change; leaving them unchanged makes the published flow inconsistent with the new gate behavior.
[low] Retention Claim at README.md:1071-1072
hint
The README says a ref of every reviewed head and base is kept so a force push cannot remove it. The implementation retains only 20 selected heads and deletes both refs for the rest. Qualify the statement with the retention limit so users do not expect carry-over to survive for older approvals.
Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 56958, calls 1, tokens in 54603 / out 37555 (reasoning included in out), duration 339.5 s, cost 0.0614 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9051, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 3999, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 40386, cost 0.0017 USD, build file judge `jev-1.14.0` calls 6, tokens in 13159, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.3 s, model_review 336.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).
Reviewed head `9567fe5e4d56eaa7fcb3d033c5c43262358a1701` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
<!-- werkrevisor:9567fe5e4d56eaa7fcb3d033c5c43262358a1701 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:9401908dbde51d314d14c9934b9555bf,120abc8b6c520d3651e22156bd3c40d8,a974ea9bb3d00c8eb1df6ffc842b92fd,fdbe83f98dea4a3ab59a4044c3c3cfc1,5b27f011b98f4ef161abf6a6094bdf86,bc85225286b799ef43c6abb00a4391d4,18b8b1ca5104a6e8466aa3f514002f8a,2f4e94e1753d270ee21874d98d708c77,85331beb05536072650bdc096fbcd2e3,9cb9caf8f1e801976beb76374f183c72,9b64b41da8a6843fe5feea88c6fe3a2d,20a53a1b136b74d33e7b216e827fbb67,41e34a9fb2cb2de1041016371b102b14,c7f8a3adfb9333eb2d1554f67852a2b1,5b9a6e5840d063b53c014589e177bb86,3e4eb25edfbbd85ad2d8a749fd2df5d5,502bd7af6a897d12bf35de0ff4b3d4a3,b30ad6ba658018cf7af5055eab7f7146,bfcd488139d5702725b2545be3427bf2,dab6b66a88bb34c277d9665b42cde467,20db7908e6fa7e66f24063a8d004f4ef,d04f177c2bacdad342f5f0265d693f94,55eee2ba855de88b046d2dac19593db6,74f9b0b268d3b40040446f24216d1253,3d15e6b72f14924ee30ac447a1e2d066,0ad7f002864c5bf096376c43f1c6dd6c,d35bf98effa731f15175197be963fc9c,fdff8291871acbc8a844ae8bd1bd1785,875282dc370ffa8124eff8ebe3cc5260,1a44d1094c2979f1c1654acfc44bf6f6,a371fdaa0567661b73c5874b45de5e40,ea22f632e070fa7b7856d7e9a8322327,cfd7f7f971bd89c45c99e967ada9bf78,ff12758487d861f0d87ff76aaafa22e9,711f7468f2c9778c031752ce3fa111a1,d0c4044ae93461a32181b87933d7addc,f0fe9acc179ab84565920fad0bf57876,cf1e5b36a876def95b01f39b180a64bf,ecbb748d43cb61b3712490863b65046b,ea228fdf784700df7d0cc5aebe6afedf,5e8d70bd73ae2285f55f979afada34a5,5b7cdd9b49cd6348b02bb4b14e84efc3,05296b701b8d5b64cdf5ca889f530d6c,d054b597bc009320c3684b0233369e72,c5c37337407c8c3314d4b605a79045d6,3ddf280aff4c8da1e01858ddf2e0a2ff,f3bb1034aa429ac1453e35e9c6653044,257dba5543ddebc4b3999a7d808e7661 -->
<!-- werkrevisor:diagrams -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>9567fe5</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/144#issuecomment-8111">the result for <code>153a20f</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 1 finding(s) that Jev rates as probably real and severe: werkrevisor/codexrunner.py:394-400
- **YELLOW** model reports a security concern: Stale-base approval bypass: when an unchanged PR head is retargeted to a different base, the runner reuses the old result and can honor its carried-approval marker without reviewing the changed diff, allowing the merge gate to pass without a human reviewing that difference.
- **hint** approval of werkrevisor for `992fbb3` not carried over: 1 approved block(s) are missing
- **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 9 changed file(s) agree with the PR document according to Jev
- **hint** 2 finding(s) of low severity or rated minor by Jev: docs/werkrevisor.werkbaum:167-168, README.md:1071-1072
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **[high] Stale Base** at `werkrevisor/codexrunner.py:394-400`, ORANGE, as a line comment
2. **[medium] Missing Diagrams** at `docs/werkrevisor.werkbaum:167-168`
hint; Jev: real 0.68, severity 1.9 of 3
The new carry-over decision changes the Codex review flow, but no diagram under `docs/` is updated. Repository guidance requires diagrams to change whenever review steps or decisions change; leaving them unchanged makes the published flow inconsistent with the new gate behavior.
3. **[low] Retention Claim** at `README.md:1071-1072`
hint
The README says a ref of every reviewed head and base is kept so a force push cannot remove it. The implementation retains only 20 selected heads and deletes both refs for the rest. Qualify the statement with the retention limit so users do not expect carry-over to survive for older approvals.
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 56958, calls 1, tokens in 54603 / out 37555 (reasoning included in out), duration 339.5 s, cost 0.0614 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9051, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 3999, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 40386, cost 0.0017 USD, build file judge `jev-1.14.0` calls 6, tokens in 13159, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.3 s, model_review 336.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).<br>
Reviewed head `9567fe5e4d56eaa7fcb3d033c5c43262358a1701` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
</sub>
### Diagrams
Drawn by a model from the diff with [PR Lens](https://github.com/coldteadotai/pr-lens): a view of what the PR touches, not a statement of what it does.


</details>
werkrevisor: ORANGE / FINDINGS for 9567fe5e4d56eaa7fcb3d033c5c43262358a1701.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `9567fe5e4d56eaa7fcb3d033c5c43262358a1701`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:9567fe5e4d56eaa7fcb3d033c5c43262358a1701 -->
Existing result comments are reused for an unchanged head. This new path revalidates only the human review, not the base used for the comparison: the reviewed base marker is never compared with the PR's current merge base. Retarget the PR to an older or unrelated base without changing the head and the old carried marker still passes, although the new diff was never reviewed. Invalidate and rerun the result when its recorded base differs from the current base.
**[high] Stale Base** (werkrevisor)
ORANGE; Jev: real 0.75, severity 3.0 of 3
Existing result comments are reused for an unchanged head. This new path revalidates only the human review, not the base used for the comparison: the reviewed base marker is never compared with the PR's current merge base. Retarget the PR to an older or unrelated base without changing the head and the old carried marker still passes, although the new diff was never reviewed. Invalidate and rerun the result when its recorded base differs from the current base.
Fixed in 153a20f: find_codex_comment takes the current base; a result whose header names another base does not count, so a retargeted head gets a new review. This also closes the gap for a result without a carried approval, which existed before this PR. The newest result of a head counts, so an approval before a new review carries nothing over. Test test_a_result_for_another_base_does_not_count is red without the base.
Fixed in `153a20f`: `find_codex_comment` takes the current base; a result whose header names another base does not count, so a retargeted head gets a new review. This also closes the gap for a result without a carried approval, which existed before this PR. The newest result of a head counts, so an approval before a new review carries nothing over. Test `test_a_result_for_another_base_does_not_count` is red without the base.
claude-code marked this conversation as resolved
ai-reviewer
scheduled this pull request to auto merge when all checks succeed 2026-10-25 01:47:19 +02:00
The change relaxes a merge gate by carrying human approvals to later commits. An error could authorize critical changes that the human never reviewed, so human review is required.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 397769 (cached 353280) / out 2083, 54.0 s, 10 commands; reviewed head `9567fe5e4d56eaa7fcb3d033c5c43262358a1701`.
<!-- werkrevisor-codex:9567fe5e4d56eaa7fcb3d033c5c43262358a1701 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
The change relaxes a merge gate by carrying human approvals to later commits. An error could authorize critical changes that the human never reviewed, so human review is required.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 397769 (cached 353280) / out 2083, 54.0 s, 10 commands; reviewed head `9567fe5e4d56eaa7fcb3d033c5c43262358a1701`.
</sub>
[high] Stale Base: fixed in 153a20f, see its thread.
[low] Retention Claim: fixed in 153a20f: the README names the 20 newest heads.
[medium] Missing Diagrams: does not hold, no change: the diagrams show no decision of the Codex review, as answered for e1070e9.
Answer to the review of `9567fe5`:
- **[high] Stale Base:** fixed in `153a20f`, see its thread.
- **[low] Retention Claim:** fixed in `153a20f`: the README names the 20 newest heads.
- **[medium] Missing Diagrams:** does not hold, no change: the diagrams show no decision of the Codex review, as answered for `e1070e9`.
werkrevisor: GREEN / PASS for 153a20fc25aa01504eab3a0db47d7d1d50a1be1d.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `153a20fc25aa01504eab3a0db47d7d1d50a1be1d`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:153a20fc25aa01504eab3a0db47d7d1d50a1be1d -->
ai-reviewer
removed review request for mi 2026-10-25 02:03:09 +02:00
[high] Failed reviews bypass the new base check (Codex review)
The base check accepts comments without a recorded base, but _review still creates every failed-review comment without base_marker. After a human approves such a failure, retargeting the same head reuses that comment and approval: collect queues no review and update_gate reports success. I reproduced this path. Failure comments must also identify their base so an approval for the previous target cannot unlock the changed PR.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Failed reviews bypass the new base check** (Codex review)
The base check accepts comments without a recorded base, but _review still creates every failed-review comment without base_marker. After a human approves such a failure, retargeting the same head reuses that comment and approval: collect queues no review and update_gate reports success. I reproduced this path. Failure comments must also identify their base so an approval for the previous target cannot unlock the changed PR.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in ec8469b: the comment of a failed review names its base in the header too, so a retargeted head gets a new review and the approval of the failure no longer counts. New test test_a_failed_review_names_its_base; red without the fix.
Fixed in `ec8469b`: the comment of a failed review names its base in the header too, so a retargeted head gets a new review and the approval of the failure no longer counts. New test `test_a_failed_review_names_its_base`; red without the fix.
[high] Invalidate the successful status before queuing a replacement review (Codex review)
When retargeting invalidates a cached review, collect returns a Candidate without resetting the existing successful status. run_repository then calls release before serve sets the status to pending. I reproduced the actual schedule_merge call with the old success still active, allowing a held merge to proceed before the new base is reviewed.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Invalidate the successful status before queuing a replacement review** (Codex review)
When retargeting invalidates a cached review, collect returns a Candidate without resetting the existing successful status. run_repository then calls release before serve sets the status to pending. I reproduced the actual schedule_merge call with the old success still active, allowing a held merge to proceed before the new base is reviewed.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in ec8469b: in gate mode collect sets queued ⏳ before it returns a candidate, so the release in the same run sees pending, not the success for the old base. test_a_result_for_another_base_does_not_count checks it; red without the fix.
Fixed in `ec8469b`: in gate mode `collect` sets `queued ⏳` before it returns a candidate, so the release in the same run sees `pending`, not the success for the old base. `test_a_result_for_another_base_does_not_count` checks it; red without the fix.
This changes the merge gate’s authorization rules by allowing earlier human approvals to unlock later heads. A mistake can permit unreviewed critical changes to merge.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Invalidate the successful status before queuing a replacement review at werkrevisor/codexrunner.py:372, as a line comment
[high] Failed reviews bypass the new base check at werkrevisor/codexrunner.py:166, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 554031 (cached 488320) / out 4075, 102.5 s, 14 commands; reviewed head `153a20fc25aa01504eab3a0db47d7d1d50a1be1d`.
<!-- werkrevisor-codex:153a20fc25aa01504eab3a0db47d7d1d50a1be1d -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the merge gate’s authorization rules by allowing earlier human approvals to unlock later heads. A mistake can permit unreviewed critical changes to merge.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Invalidate the successful status before queuing a replacement review** at `werkrevisor/codexrunner.py:372`, as a line comment
2. **[high] Failed reviews bypass the new base check** at `werkrevisor/codexrunner.py:166`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 554031 (cached 488320) / out 4075, 102.5 s, 14 commands; reviewed head `153a20fc25aa01504eab3a0db47d7d1d50a1be1d`.
</sub>
werkrevisor: GREEN / PASS for ec8469b9fbca3fbddffe595268539e30adfa7098.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `ec8469b9fbca3fbddffe595268539e30adfa7098`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:ec8469b9fbca3fbddffe595268539e30adfa7098 -->
[high] Legacy reviews bypass the retargeting check (Codex review)
The base comparison accepts comments without a recorded base. For an open PR reviewed before this upgrade, retargeting to an older base can introduce critical changes into the diff while preserving the head SHA. Its legacy noncritical result still passes the gate without another review. Reproduced through collect(): it sets success and queues no review. Require a fresh review when the cached result cannot establish its base.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Legacy reviews bypass the retargeting check** (Codex review)
The base comparison accepts comments without a recorded base. For an open PR reviewed before this upgrade, retargeting to an older base can introduce critical changes into the diff while preserving the head SHA. Its legacy noncritical result still passes the gate without another review. Reproduced through collect(): it sets success and queues no review. Require a fresh review when the cached result cannot establish its base.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 0070059: a result without a base no longer counts for the current base, so a head reviewed before 0.74.0 gets a new review; after the deployment each ready PR of a gate or advise repository gets one more Codex review. New test test_a_result_without_its_base_gets_a_new_review; red without the fix.
Fixed in `0070059`: a result without a base no longer counts for the current base, so a head reviewed before 0.74.0 gets a new review; after the deployment each ready PR of a `gate` or `advise` repository gets one more Codex review. New test `test_a_result_without_its_base_gets_a_new_review`; red without the fix.
This changes the merge gate’s authorization rules by allowing earlier human approvals to unlock later commits. A mistake can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Legacy reviews bypass the retargeting check at werkrevisor/codexrunner.py:165, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 427869 (cached 364672) / out 3174, 93.1 s, 11 commands; reviewed head `ec8469b9fbca3fbddffe595268539e30adfa7098`.
<!-- werkrevisor-codex:ec8469b9fbca3fbddffe595268539e30adfa7098 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the merge gate’s authorization rules by allowing earlier human approvals to unlock later commits. A mistake can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Legacy reviews bypass the retargeting check** at `werkrevisor/codexrunner.py:165`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 427869 (cached 364672) / out 3174, 93.1 s, 11 commands; reviewed head `ec8469b9fbca3fbddffe595268539e30adfa7098`.
</sub>
hint approval of werkrevisor for ec8469b not carried over: 4 approved block(s) are missing
hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 9 changed file(s) agree with the PR document according to Jev
hint 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-25-PR#144-codex-carry-over.md:129-131
hint model recommends merge with caution
Findings
[low] Contradictory force-push statement at docs/prs/2026-10-25-PR#144-codex-carry-over.md:129-131
hint
The solution states that after a force push an approval of the older head carries nothing over. This contradicts Scenario#144.03, the README and the code: a rebase is normally pushed with force, and there the approval still carries over whenever codex.change_signature of the approved change and of the current change is equal, while the rating of the change since the approval only requires the approved head to be an ancestor. Please say that only a force push which changes the change itself drops the approval. The same bullet also refers to "review round 9", which does not exist in the document (the list jumps from round 8 to round 10).
Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 60638, calls 1, tokens in 58106 / out 53922 (reasoning included in out), duration 422.4 s, cost 0.0821 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9228, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, PR document check `jev-1.14.0` calls 9, tokens in 43510, cost 0.0018 USD, build file judge `jev-1.14.0` calls 6, tokens in 14645, cost 0.0006 USD, phases: path_judge 0.6 s, classifier 0.5 s, doc_check 0.6 s, context 1.4 s, model_review 418.0 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).
Reviewed head `0070059abfa379ef7d90749ac218c6d9cbaeeabe` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
<!-- werkrevisor:0070059abfa379ef7d90749ac218c6d9cbaeeabe -->
<!-- werkrevisor-verdict:GREEN -->
<!-- werkrevisor-blocks:c2af4ae1d15d42c6ee8fc8cb69ecafda,120abc8b6c520d3651e22156bd3c40d8,4a5345921e99bc5100ebe6422286ca18,fdbe83f98dea4a3ab59a4044c3c3cfc1,5b27f011b98f4ef161abf6a6094bdf86,bc85225286b799ef43c6abb00a4391d4,18b8b1ca5104a6e8466aa3f514002f8a,2f4e94e1753d270ee21874d98d708c77,85331beb05536072650bdc096fbcd2e3,9cb9caf8f1e801976beb76374f183c72,7d1a4849f64d69755442e6fd1d80ee44,dd0e25a9697f7c61e50a73e75c775c59,20a53a1b136b74d33e7b216e827fbb67,41e34a9fb2cb2de1041016371b102b14,c7f8a3adfb9333eb2d1554f67852a2b1,5b9a6e5840d063b53c014589e177bb86,3e4eb25edfbbd85ad2d8a749fd2df5d5,502bd7af6a897d12bf35de0ff4b3d4a3,b30ad6ba658018cf7af5055eab7f7146,bfcd488139d5702725b2545be3427bf2,dab6b66a88bb34c277d9665b42cde467,5e939c3d2052a1993df5d86b7e6ab067,d04f177c2bacdad342f5f0265d693f94,55eee2ba855de88b046d2dac19593db6,74f9b0b268d3b40040446f24216d1253,3d15e6b72f14924ee30ac447a1e2d066,0ad7f002864c5bf096376c43f1c6dd6c,d35bf98effa731f15175197be963fc9c,fdff8291871acbc8a844ae8bd1bd1785,875282dc370ffa8124eff8ebe3cc5260,1a44d1094c2979f1c1654acfc44bf6f6,a371fdaa0567661b73c5874b45de5e40,ea22f632e070fa7b7856d7e9a8322327,cfd7f7f971bd89c45c99e967ada9bf78,ff12758487d861f0d87ff76aaafa22e9,711f7468f2c9778c031752ce3fa111a1,d0c4044ae93461a32181b87933d7addc,f0fe9acc179ab84565920fad0bf57876,cf1e5b36a876def95b01f39b180a64bf,ecbb748d43cb61b3712490863b65046b,e8a7de0b58cf2f36a02598bd40562246,5e8d70bd73ae2285f55f979afada34a5,5b7cdd9b49cd6348b02bb4b14e84efc3,05296b701b8d5b64cdf5ca889f530d6c,d054b597bc009320c3684b0233369e72,b1f2fda71c8584fbcf70322a40cfbe92,3471ec5dfec24aa4e9b255c4bbd75c58,c5c37337407c8c3314d4b605a79045d6,3ddf280aff4c8da1e01858ddf2e0a2ff,f3bb1034aa429ac1453e35e9c6653044,e074a4eff1a7d089afb544109680e5bd,257dba5543ddebc4b3999a7d808e7661 -->
<!-- werkrevisor-folded -->
<details><summary>🟢 werkrevisor GREEN for <code>0070059</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/144#issuecomment-8139">the result for <code>7f6a237</code></a></summary>
## 🟢 werkrevisor: GREEN / PASS
Change kind: `mixed`.
### Reasons
- **hint** approval of werkrevisor for `ec8469b` not carried over: 4 approved block(s) are missing
- **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 9 changed file(s) agree with the PR document according to Jev
- **hint** 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-25-PR#144-codex-carry-over.md:129-131
- **hint** model recommends merge with caution
### Findings
1. **[low] Contradictory force-push statement** at `docs/prs/2026-10-25-PR#144-codex-carry-over.md:129-131`
hint
The solution states that after a force push an approval of the older head carries nothing over. This contradicts Scenario#144.03, the README and the code: a rebase is normally pushed with force, and there the approval still carries over whenever `codex.change_signature` of the approved change and of the current change is equal, while the rating of the change since the approval only requires the approved head to be an ancestor. Please say that only a force push which changes the change itself drops the approval. The same bullet also refers to "review round 9", which does not exist in the document (the list jumps from round 8 to round 10).
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 60638, calls 1, tokens in 58106 / out 53922 (reasoning included in out), duration 422.4 s, cost 0.0821 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9228, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, PR document check `jev-1.14.0` calls 9, tokens in 43510, cost 0.0018 USD, build file judge `jev-1.14.0` calls 6, tokens in 14645, cost 0.0006 USD, phases: path_judge 0.6 s, classifier 0.5 s, doc_check 0.6 s, context 1.4 s, model_review 418.0 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main 568ae2b).<br>
Reviewed head `0070059abfa379ef7d90749ac218c6d9cbaeeabe` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
</sub>
</details>
werkrevisor: GREEN / PASS for 0070059abfa379ef7d90749ac218c6d9cbaeeabe.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `0070059abfa379ef7d90749ac218c6d9cbaeeabe`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:0070059abfa379ef7d90749ac218c6d9cbaeeabe -->
[high] Retargeting during review bypasses the base check (Codex review)
If the PR is retargeted while Codex runs, serve() passes the original PR snapshot to update_gate(), which publishes success without checking the current base. An in-memory reproduction confirmed success despite different reviewed and current bases. Because retargeting preserves the head SHA, that status can unlock a scheduled merge of the unreviewed change before the next poll. Re-fetch the PR and verify its base before publishing success.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Retargeting during review bypasses the base check** (Codex review)
If the PR is retargeted while Codex runs, serve() passes the original PR snapshot to update_gate(), which publishes success without checking the current base. An in-memory reproduction confirmed success despite different reviewed and current bases. Because retargeting preserves the head SHA, that status can unlock a scheduled merge of the unreviewed change before the next poll. Re-fetch the PR and verify its base before publishing success.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 7f6a237: after a review, serve reads the PR again; if its base or head changed during the review, the status stays queued ⏳ and the next run reviews the head for the new base. New test test_a_retarget_during_the_review_sets_no_success; red without the fix.
Fixed in `7f6a237`: after a review, `serve` reads the PR again; if its base or head changed during the review, the status stays `queued ⏳` and the next run reviews the head for the new base. New test `test_a_retarget_during_the_review_sets_no_success`; red without the fix.
This relaxes a merge gate by allowing earlier human approvals to authorize later heads. Incorrect carry-over can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Retargeting during review bypasses the base check at werkrevisor/codexrunner.py:409, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 491955 (cached 425216) / out 3376, 104.5 s, 12 commands; reviewed head `0070059abfa379ef7d90749ac218c6d9cbaeeabe`.
<!-- werkrevisor-codex:0070059abfa379ef7d90749ac218c6d9cbaeeabe -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This relaxes a merge gate by allowing earlier human approvals to authorize later heads. Incorrect carry-over can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Retargeting during review bypasses the base check** at `werkrevisor/codexrunner.py:409`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 491955 (cached 425216) / out 3376, 104.5 s, 12 commands; reviewed head `0070059abfa379ef7d90749ac218c6d9cbaeeabe`.
</sub>
[low] Contradictory force-push statement: holds; fixed in 7f6a237. The PR document says now that only a force push that removes the head from the PR can drop its pruned commit, and no longer refers to a round 9.
Codex [high] Retargeting during review: fixed in 7f6a237, see its thread.
Answer to the review of `0070059`:
- **[low] Contradictory force-push statement:** holds; fixed in `7f6a237`. The PR document says now that only a force push that removes the head from the PR can drop its pruned commit, and no longer refers to a round 9.
- **Codex [high] Retargeting during review:** fixed in `7f6a237`, see its thread.
werkrevisor: GREEN / PASS for 7f6a23797ff7a4c1c8ef8333db38527565772c7b.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `7f6a23797ff7a4c1c8ef8333db38527565772c7b`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:7f6a23797ff7a4c1c8ef8333db38527565772c7b -->
[high] Revalidate the current target before accepting a cached review (Codex review)
If a PR is retargeted after list_open_prs() returns, collect() matches its cached review against the old snapshot and calls update_gate() without checking the current target. The unchanged_target() check only runs after a new review in serve(). I reproduced collect() publishing success and queuing no review while unchanged_target() returned false. Because the head SHA stays unchanged, this can unlock a merge against an unreviewed base. Apply the target check to cached results before publishing success.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Revalidate the current target before accepting a cached review** (Codex review)
If a PR is retargeted after list_open_prs() returns, collect() matches its cached review against the old snapshot and calls update_gate() without checking the current target. The unchanged_target() check only runs after a new review in serve(). I reproduced collect() publishing success and queuing no review while unchanged_target() returned false. Because the head SHA stays unchanged, this can unlock a merge against an unreviewed base. Apply the target check to cached results before publishing success.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 48b7482: the check moved from serve into update_gate, so every success first reads the PR again, also for a result from before; with another base or head the status stays queued ⏳. New test test_a_retarget_after_the_list_of_prs_sets_no_success; red without the fix. Between that read and setting the status, a retarget within milliseconds stays possible; Gitea offers no conditional status, and the next run corrects it.
Fixed in `48b7482`: the check moved from `serve` into `update_gate`, so every `success` first reads the PR again, also for a result from before; with another base or head the status stays `queued ⏳`. New test `test_a_retarget_after_the_list_of_prs_sets_no_success`; red without the fix. Between that read and setting the status, a retarget within milliseconds stays possible; Gitea offers no conditional status, and the next run corrects it.
This changes a merge gate to accept earlier human approvals for later heads. A mistake can permit critical changes to merge without human approval.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Revalidate the current target before accepting a cached review at werkrevisor/codexrunner.py:376, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 603582 (cached 551040) / out 3435, 111.3 s, 11 commands; reviewed head `7f6a23797ff7a4c1c8ef8333db38527565772c7b`.
<!-- werkrevisor-codex:7f6a23797ff7a4c1c8ef8333db38527565772c7b -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes a merge gate to accept earlier human approvals for later heads. A mistake can permit critical changes to merge without human approval.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Revalidate the current target before accepting a cached review** at `werkrevisor/codexrunner.py:376`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 603582 (cached 551040) / out 3435, 111.3 s, 11 commands; reviewed head `7f6a23797ff7a4c1c8ef8333db38527565772c7b`.
</sub>
werkrevisor: GREEN / PASS for 48b7482c7f311dfd3b4aad2b1d705a54165809ad.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `48b7482c7f311dfd3b4aad2b1d705a54165809ad`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:48b7482c7f311dfd3b4aad2b1d705a54165809ad -->
The change relaxes the merge gate by allowing an earlier human approval to authorize a later head. An error in approval reuse could permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 476464 (cached 431104) / out 2747, 72.5 s, 9 commands; reviewed head `48b7482c7f311dfd3b4aad2b1d705a54165809ad`.
<!-- werkrevisor-codex:48b7482c7f311dfd3b4aad2b1d705a54165809ad -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
The change relaxes the merge gate by allowing an earlier human approval to authorize a later head. An error in approval reuse could permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 476464 (cached 431104) / out 2747, 72.5 s, 9 commands; reviewed head `48b7482c7f311dfd3b4aad2b1d705a54165809ad`.
</sub>
claude-code
changed title from werkrevisor 0.74.0: the Codex gate keeps a human approval when the change since then is not critical to werkrevisor 0.76.0: the Codex gate keeps a human approval when the change since then is not critical2026-10-25 10:38:37 +01:00
werkrevisor: GREEN / PASS for 368bb7d00d04182639d60dd776ee9fe050242c89.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `368bb7d00d04182639d60dd776ee9fe050242c89`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:368bb7d00d04182639d60dd776ee9fe050242c89 -->
[high] Retargeting back reuses an approval for a different base (Codex review)
The search skips newer results for other bases and accepts an older matching result. If head H is reviewed against B1, then reviewed and human-approved against B2, retargeting back to B1 selects the original result. update_gate accepts the B2 approval because its head matches and its timestamp follows that older result. I reproduced a success status for this sequence: critical changes present only against B1 can pass without approval of that change. A newer review for another base must invalidate reuse of the older result.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Retargeting back reuses an approval for a different base** (Codex review)
The search skips newer results for other bases and accepts an older matching result. If head H is reviewed against B1, then reviewed and human-approved against B2, retargeting back to B1 selects the original result. update_gate accepts the B2 approval because its head matches and its timestamp follows that older result. I reproduced a success status for this sequence: critical changes present only against B1 can pass without approval of that change. A newer review for another base must invalidate reuse of the older result.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 1c964b0: only the newest result of a head counts; when it names another base, find_codex_comment returns none and the head gets a new review, so a retarget back to B1 does not revive the older B1 result. New test test_a_retarget_back_needs_a_new_review with your sequence; red when an older result for the base still counts.
Fixed in `1c964b0`: only the newest result of a head counts; when it names another base, `find_codex_comment` returns none and the head gets a new review, so a retarget back to B1 does not revive the older B1 result. New test `test_a_retarget_back_needs_a_new_review` with your sequence; red when an older result for the base still counts.
This changes the authorization enforced by a merge gate, allowing older human approvals to unlock later heads. A mistake could permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
[high] Retargeting back reuses an approval for a different base at werkrevisor/codexrunner.py:170, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 469709 (cached 419968) / out 3352, 162.7 s, 11 commands; reviewed head `368bb7d00d04182639d60dd776ee9fe050242c89`.
<!-- werkrevisor-codex:368bb7d00d04182639d60dd776ee9fe050242c89 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the authorization enforced by a merge gate, allowing older human approvals to unlock later heads. A mistake could permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
### Findings
1. **[high] Retargeting back reuses an approval for a different base** at `werkrevisor/codexrunner.py:170`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 469709 (cached 419968) / out 3352, 162.7 s, 11 commands; reviewed head `368bb7d00d04182639d60dd776ee9fe050242c89`.
</sub>
werkrevisor: GREEN / PASS for 1c964b0ca53f956d53ea9d6f2b798488a1a05302.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `1c964b0ca53f956d53ea9d6f2b798488a1a05302`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:1c964b0ca53f956d53ea9d6f2b798488a1a05302 -->
The change relaxes a merge gate by allowing earlier human approvals to authorize later heads. An incorrect carry-over decision could permit unapproved security-sensitive changes to merge, which the tests cannot fully rule out.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 419493 (cached 351360) / out 3613, 159.6 s, 10 commands; reviewed head `1c964b0ca53f956d53ea9d6f2b798488a1a05302`.
<!-- werkrevisor-codex:1c964b0ca53f956d53ea9d6f2b798488a1a05302 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
The change relaxes a merge gate by allowing earlier human approvals to authorize later heads. An incorrect carry-over decision could permit unapproved security-sensitive changes to merge, which the tests cannot fully rule out.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 419493 (cached 351360) / out 3613, 159.6 s, 10 commands; reviewed head `1c964b0ca53f956d53ea9d6f2b798488a1a05302`.
</sub>
werkrevisor: GREEN / PASS for ea1441774afebef566325f1b2c42c3dde20b4e9b.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `ea1441774afebef566325f1b2c42c3dde20b4e9b`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:ea1441774afebef566325f1b2c42c3dde20b4e9b -->
This changes the merge gate’s authorization logic by allowing an earlier human approval to unlock a later head. A mistake could permit critical, unapproved changes to merge despite passing tests.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 355026 (cached 292992) / out 3306, 157.4 s, 9 commands; reviewed head `ea1441774afebef566325f1b2c42c3dde20b4e9b`.
<!-- werkrevisor-codex:ea1441774afebef566325f1b2c42c3dde20b4e9b -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the merge gate’s authorization logic by allowing an earlier human approval to unlock a later head. A mistake could permit critical, unapproved changes to merge despite passing tests.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 355026 (cached 292992) / out 3306, 157.4 s, 9 commands; reviewed head `ea1441774afebef566325f1b2c42c3dde20b4e9b`.
</sub>
claude-code
changed title from werkrevisor 0.76.0: the Codex gate keeps a human approval when the change since then is not critical to werkrevisor 0.77.0: the Codex gate keeps a human approval when the change since then is not critical2026-10-25 11:41:36 +01:00
werkrevisor: GREEN / PASS for faa3318743422fa2808fd44121bd3e5ea6c82c0b.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `faa3318743422fa2808fd44121bd3e5ea6c82c0b`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:faa3318743422fa2808fd44121bd3e5ea6c82c0b -->
This changes the merge gate to accept an earlier human approval for a later head. A mistake could let a critical change merge without fresh human review, and tests cannot establish the correctness of every model judgment.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 621955 (cached 544768) / out 3264, 126.1 s, 13 commands; reviewed head `faa3318743422fa2808fd44121bd3e5ea6c82c0b`.
<!-- werkrevisor-codex:faa3318743422fa2808fd44121bd3e5ea6c82c0b -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the merge gate to accept an earlier human approval for a later head. A mistake could let a critical change merge without fresh human review, and tests cannot establish the correctness of every model judgment.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 621955 (cached 544768) / out 3264, 126.1 s, 13 commands; reviewed head `faa3318743422fa2808fd44121bd3e5ea6c82c0b`.
</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The Codex gate carries a human approval over to a later head when Codex rates the commits since then as not critical, or when a rebase kept the same blocks.
See
docs/prs/for the details.🤖 Generated with Claude Code
1301d7febatocca713b961🟠 werkrevisor ORANGE for
cca713b, superseded by the result fore1070e9🟠 werkrevisor: ORANGE / FINDINGS
Gate: the approval of mi came before this result; review requested again, please approve again after reading it; merge scheduled for when the checks succeed.
Change kind:
mixed.Reasons
Findings
werkrevisor/codexrunner.py:433-435, ORANGE, as a line commentClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46152, calls 1, tokens in 44341 / out 27742 (reasoning included in out), duration 244.9 s, cost 0.0466 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8341, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 1807, cost 0.0001 USD, PR document check `jev-1.14.0` calls 9, tokens in 28725, cost 0.0012 USD, build file judge `jev-1.14.0` calls 6, tokens in 8912, cost 0.0004 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 242.7 s, finding_judge 0.1 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `cca713b961f14a726802a701eaca289ed28a4e56` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
werkrevisor: ORANGE / FINDINGS for
cca713b961f14a726802a701eaca289ed28a4e56.The result comment of werkrevisor holds the details.
@@ -382,2 +431,3 @@unattached = [i for i in range(len(findings)) if i not in attached]body = result_comment(head, record, set(attached), unattached, mode)answer = record["answer"]carried = None[high] Delta ignored (werkrevisor)
ORANGE; Jev: real 0.81, severity 3.0 of 3
The new delta rating is used only to decide whether to create a carried marker; it does not determine whether the gate requires a human.
result_commentstill derives the critical marker solely from the overall answer. For example, if an approved head adds a critical protection and the new head removes it, the net PR can be ratedcritical: falsewhile the delta is correctly ratedsince_approval_critical: true. No carried marker is created in that case, andgate_statesees a non-critical marker and passes without a current-head approval. Use the delta rating to require human review, or reject such answers, and add this net-revert scenario to the runner tests.Fixed in
e1070e9:codex.needs_humanis true when Codex rates the change since the approval as critical, also in a PR that is not critical as a whole; the result comment names the rating. New testtest_a_critical_change_since_the_approval_needs_a_human_also_in_an_uncritical_pr, red without the fix.Codex review of
cca713b961f14a726802a701eaca289ed28a4e56@@ -85,1 +93,4 @@SINCE_APPROVAL = """4. A human approved the earlier head {approved} after its review.The commits since then are `git log -p --cc {approved}..HEAD ^{base}`:[high] Combined diffs hide security-relevant merge resolutions (Codex review)
git log --cc omits resolutions that select one parent's content. If main adds a security check and a merge retains the PR parent's version without that check, this command shows no patch for the removal; main's commits are also excluded. The incremental assessment can consequently report no critical change and carry the approval even when the full review correctly marks the removal as critical.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
e1070e9: Codex now compares the change of the PR then and now,git diff <merge base of approved and base>..<approved>againstgit diff <base>..HEAD, also in binary files, modes and renames. A merge that keeps the PR side and drops a check ofmainshows as a removal in the second diff.@@ -79,0 +81,4 @@"""After a rebase: werkrevisor found the same changed blocks for both heads."""found = [carryover.parse_marker((report.find_existing(comments, sha, login)[0] or {}).get("body"))for sha in (approved_head, head)]return None not in found and found[0] == found[1][high] Block equality overlooks new binary changes (Codex review)
The markers contain only text-hunk fingerprints; binary changes, pure renames and mode-only changes contribute no blocks. After a rebase, retaining the approved text edits while adding an executable binary therefore satisfies this comparison. I reproduced the resulting Codex gate success despite critical=true and no current-head approval.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
e1070e9: after a rebase the runner no longer compares the block fingerprints itself. It needs werkrevisor's own carry-over of the same human approval, "the changed blocks are those of the approved head"; werkrevisor counts a file without text hunks, e.g. a binary file, a mode change or a rename, as a new file and carries nothing over then. The test with equal fingerprints but no such carry-over is red with the old comparison.Codex review: critical
This changes the human-approval requirement of a merge gate. Mistakes can authorize critical changes that no human reviewed.
Security effect: weakens; fails closed: no; a test of the closed case: no.
Findings
werkrevisor/codexrunner.py:84, as a line commentwerkrevisor/codex.py:96, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 375904 (cached 318848) / out 3555, 102.3 s, 8 commands; reviewed head `cca713b961f14a726802a701eaca289ed28a4e56`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟠 werkrevisor ORANGE for
e1070e9, superseded by the result for55ec69c🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
Findings
[high] Marker Injection at
werkrevisor/codexrunner.py:98-100, ORANGE, as a line comment[high] Prose Authorization at
werkrevisor/codexrunner.py:80-87, ORANGE, as a line comment[medium] Missing Diagram at
docs/werkrevisor.werkbaum:167-168hint; Jev: real 0.78, severity 2.1 of 3
This adds a new Codex carry-over decision, but the PR does not update the review diagrams under
docs/.AGENTS.mdexplicitly requires diagram updates whenever review steps or decisions change; leaving the diagrams unchanged documents the old rule that an earlier approval never unlocksai-review/codex. Update the relevant diagrams in this PR.Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 47899, calls 1, tokens in 46032 / out 37758 (reasoning included in out), duration 325.7 s, cost 0.0591 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8545, cost 0.0004 USD, finding judge `jev-1.14.0` calls 3, tokens in 5254, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 31183, cost 0.0013 USD, build file judge `jev-1.14.0` calls 6, tokens in 9540, cost 0.0004 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 323.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main2 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `e1070e9f47e3307bcadef297c0c5b417f4d04676` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
werkrevisor: ORANGE / FINDINGS for
e1070e9f47e3307bcadef297c0c5b417f4d04676.The result comment of werkrevisor holds the details.
@@ -76,10 +77,29 @@ def load_config(path):int(data.get("max_reviews_per_run", 3)), servers)def _same_blocks(comments, approval, head, login):[high] Prose Authorization (werkrevisor)
ORANGE; Jev: real 0.89, severity 3.0 of 3
_same_blocks()treats an English sentence anywhere in the werkrevisor result as proof that a rebase preserved approved blocks. That result also contains model-generated reasons, so if the model follows a crafted instruction, an author can make it emit the exact sentence and SHA; the runner then skips the Codex comparison and carries the old approval even for a critical rebased change. Represent this fact with a dedicated machine-readable marker in a fixed section.Fixed in
55ec69c: the runner no longer reads the werkrevisor result for the rebase case. It compares the change itself in git,codex.change_signatureof<base then>..<approved>and<base>..HEAD.@@ -83,0 +95,4 @@return f"<!-- werkrevisor-codex-carried:{sha} -->"def carried_head(body):[high] Marker Injection (werkrevisor)
ORANGE; Jev: real 0.79, severity 3.0 of 3
carried_head()searches the entire result comment for a carry marker, butresult_comment()places model-produced fields such ascritical_reasonandsince_approval_reasonin that same comment. A crafted PR description can ask Codex to echo a marker containing the known approved SHA; if the model complies,update_gate()treats it as a genuine carried approval andgate_state()suppresses the critical rating. Parse only a fixed bot-generated marker line, never model-produced text.Fixed in
55ec69c: the runner reads its markers only fromheader, the marker lines at the start of its own comment; Codex text comes after the heading. This also covers the older markerswerkrevisor-codex-unattachedand the head marker. Testtest_text_of_codex_never_passes_for_a_markerforges all three in the reasons; it is red with the old substring search.Codex review of
e1070e9f47e3307bcadef297c0c5b417f4d04676@@ -344,1 +392,3 @@ok = record["exit_code"] == 0 and codex.valid_answer(record["answer"])approval, comments = self.earlier_approval(server, gitea, number, head, login)since = approval.head_sha if approval and codex.is_ancestor(work, approval.head_sha) else Nonesame_blocks = bool(approval) and not since and _same_blocks(comments, approval, head, login)[high] Rebase carry-over overlooks mode changes alongside approved text hunks (Codex review)
The werkrevisor carry-over message does not prove that file metadata stayed unchanged: its block comparison ignores modes on files containing approved text hunks. A rebased head can therefore add an executable-bit change to an already edited file and still satisfy _same_blocks. This bypasses the since-approval assessment and carries the approval even when Codex marks the current change critical. An in-memory reproduction with an additional 100644→100755 change produced a successful Codex gate. Validate metadata changes before accepting this carry-over.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
55ec69c:codex.change_signaturehashesgit diff --binary -Mwithout line numbers, blob ids, similarity indexes and context lines, and keeps modes, renames and binary patches. A real-git test shows that a rebase keeps the signature and that a mode change, a binary file and a rename each change it; with the mode lines dropped, the test is red.Codex review: critical
This changes the human-approval merge gate. A mistake can allow unreviewed security-sensitive changes to merge, so human review is required.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codexrunner.py:394, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 544024 (cached 475520) / out 3486, 105.6 s, 10 commands; reviewed head `e1070e9f47e3307bcadef297c0c5b417f4d04676`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.Answer to the review of
e1070e9:The diagrams show no decision of the Codex review:
docs/werkrevisor-pr-workflow.svghas one box "Codex review after werkrevisor, optional; in gate mode it holds the merge", andreview_decisions.pyandreview_inside.pydo not name Codex.55ec69cwith the line comments above.🟢 werkrevisor GREEN for
55ec69c, superseded by the result for25ef3e2🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 52853, duration 724.9 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8738, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 35933, cost 0.0015 USD, build file judge `jev-1.14.0` calls 6, tokens in 11555, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 722.6 s, config: defaults + host + repository (.werkrevisor.toml at main2 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `55ec69c797eea9179fe2e1b4f7200cac6b6708ed` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
55ec69c797eea9179fe2e1b4f7200cac6b6708ed.The result comment of werkrevisor holds the details.
Codex review of
55ec69c797eea9179fe2e1b4f7200cac6b6708ed@@ -195,0 +246,4 @@if line.startswith(b"index "):# "index <old>..<new> <mode>": the mode of an unchanged mode stays.kept.append(b"index " + b" ".join(line.split(b" ")[2:]))elif line.startswith(b"@@"):[high] Change signatures treat edits to different functions as identical (Codex review)
Removing hunk locations and all context makes identical replacements elsewhere in the same file produce equal signatures. For example, restoring an approved
return False→return Trueedit in a public-view permission function and applying it instead in an account-deletion permission function preserves the signature._reviewthen skips the since-approval assessment and carries the old approval;gate_statepasses even when Codex marks the current head critical. This allows a new authorization change to merge without human approval.Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
25ef3e2: the signature keeps the context lines and the function heading of each hunk; only the line numbers, blob ids and similarity indexes are dropped. The real-git test now applies the samereturn False→return Truein another function and expects another signature; it is red with the old signature. A rebase that changes the context of an edit now needs a new approval.Codex review: critical
This changes the merge gate’s authorization rules by allowing an earlier human approval to authorize a later head. A mistake can bypass required human review of security-sensitive changes.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codex.py:249, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 432911 (cached 374528) / out 3376, 102.6 s, 14 commands; reviewed head `55ec69c797eea9179fe2e1b4f7200cac6b6708ed`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
25ef3e2, superseded by the result for10b62fa🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
55ec69cnot carried over: 4 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 53514, duration 724.8 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8799, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 36701, cost 0.0015 USD, build file judge `jev-1.14.0` calls 6, tokens in 11768, cost 0.0005 USD, phases: path_judge 0.4 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 722.5 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `25ef3e22af2dbe1acebefcd887ea227f0d668998` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
25ef3e22af2dbe1acebefcd887ea227f0d668998.The result comment of werkrevisor holds the details.
Codex review of
25ef3e22af2dbe1acebefcd887ea227f0d668998@@ -195,0 +250,4 @@kept.append(b"index " + b" ".join(line.split(b" ")[2:]))elif line.startswith(b"@@"):# "@@ -1,2 +3,4 @@ heading": the heading names the function of the hunk.kept.append(b"@@" + line.split(b"@@", 2)[-1])[high] Repeated blocks let different permission changes share an approval (Codex review)
Removing hunk positions and blob IDs makes distinct edits identical when their surrounding context repeats. Reproduced with JSON permission entries: changing
allowedfor one resource produces the same signature as changing it for another resource whose name lies outside the three context lines. After approval, a commit can restore the first permission and change the second._reviewthen treats these as the same change, skips the since-approval assessment, and carries the approval even when Codex marks the current change critical.Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
e2582ae: the signature is nowgit diff --raw --full-index -M, i.e. path, modes and blob ids of each changed file before and after, so it compares the files exactly, without positions to lose. A rebase keeps the approval only if the base branch changed none of the PR's files; otherwise a human approves again. The real-git test has your case: JSON entries with repeated context,allowedchanged for another resource; it is red with the old signature.Codex review: critical
The change relaxes the merge gate by carrying human approvals across commits. An incorrect comparison can authorize critical changes that the human never approved.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codex.py:253, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 437111 (cached 384640) / out 4330, 115.6 s, 12 commands; reviewed head `25ef3e22af2dbe1acebefcd887ea227f0d668998`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
10b62fa, superseded by the result for5eb5a92🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
25ef3e2not carried over: 4 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 53369, duration 725.0 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8811, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 36738, cost 0.0015 USD, build file judge `jev-1.14.0` calls 6, tokens in 11688, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.2 s, model_review 722.6 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `10b62fa6875c9a825b7e923d2c73b3accf7996ea` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
10b62fa6875c9a825b7e923d2c73b3accf7996ea.The result comment of werkrevisor holds the details.
Codex review of
10b62fa6875c9a825b7e923d2c73b3accf7996ea@@ -195,0 +239,4 @@e.g. after a rebase onto a base branch that changed other files.A comparison without positions failed twice: an edit elsewhere with the same lines passed as the same change."""raw = _git("diff", "--raw", "--full-index", "--no-abbrev", "-M", "-z", base, head, cwd=work)[high] Ignored submodule changes can reuse an old approval (Codex review)
The signature command inherits submodule ignore settings instead of specifying --ignore-submodules=none. If a submodule has ignore=all in .gitmodules or Git configuration, changing its recorded commit can leave the signature unchanged. The runner then carries the old approval through same_change, skips the incremental criticality assessment, and can pass the gate even when the overall review remains critical. Include ignored gitlink changes in the signature.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
5eb5a92:--ignore-submodules=none. The real-git test adds a submodule withignore = allin.gitmodulesand moves its commit; the signature changes, and without the flag the test is red.Codex review: critical
This changes the merge authorization gate by allowing an earlier human approval to authorize a later head. A mistake can permit unapproved security-sensitive changes to merge.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codex.py:242, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 448107 (cached 389888) / out 3142, 94.1 s, 8 commands; reviewed head `10b62fa6875c9a825b7e923d2c73b3accf7996ea`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
5eb5a92, superseded by the result forbc85826🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
10b62fanot carried over: 3 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 53894, duration 725.0 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8855, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 37365, cost 0.0016 USD, build file judge `jev-1.14.0` calls 6, tokens in 11919, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 722.6 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2.The result comment of werkrevisor holds the details.
Codex review of
5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2@@ -195,0 +240,4 @@A comparison without positions failed twice: an edit elsewhere with the same lines passed as the same change."""# A submodule with "ignore = all" in the .gitmodules of the PR must not hide a new commit of it.raw = _git("diff", "--raw", "--full-index", "--no-abbrev", "-M", "-z", "--ignore-submodules=none", base, head,[high] Text decoding makes distinct paths share an approval signature (Codex review)
change_signature passes Git's NUL-delimited raw output through _git, whose text=True converts carriage returns and CRLF to LF. Thus edits with identical modes and before/after blobs in distinct valid paths such as "policy\r.json" and "policy\n.json" produce identical signatures; this was reproduced without modifying files. Moving the approved edit between those paths makes same_change true, skips the since-approval assessment and carries the approval even when the current review rates the PR critical. Hash the raw subprocess bytes without newline conversion.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
bc85826: the signature hashes the raw bytes ofgit diff --raw -z, without text mode. The real-git test commitsp\r.jsonandp\n.jsonand expects different signatures; it is red in text mode.Codex review: critical
The change relaxes a merge gate by allowing earlier human approvals to authorize later heads. A mistake can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codex.py:243, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 388134 (cached 331392) / out 2876, 73.7 s, 15 commands; reviewed head `5eb5a92d557843cbd0acf2b20b0723cf8c7e6ce2`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
bc85826, superseded by the result foreb07159🟢 werkrevisor: GREEN / PASS
Gate: approved by werkrevisor; merge waits for 1 open hint(s): reply to each line comment and resolve it, then restart the review or push a commit.
Change kind:
mixed.Reasons
5eb5a92not carried over: 3 approved block(s) are missingFindings
werkrevisor/codex.py:261-262, hint, as a line commentClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 54200, calls 1, tokens in 51974 / out 46591 (reasoning included in out), duration 344.7 s, cost 0.0715 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8889, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 1876, cost 0.0001 USD, PR document check `jev-1.14.0` calls 9, tokens in 37792, cost 0.0016 USD, build file judge `jev-1.14.0` calls 6, tokens in 12040, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 341.9 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `bc858266f6d6a4e1601249fab213694141b6db6b` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
bc858266f6d6a4e1601249fab213694141b6db6b.The result comment of werkrevisor holds the details.
@@ -204,6 +258,8 @@ def checkout(base_url, owner, repo, number, head, base, token, root):_git("clone", "--bare", "--quiet", url, mirror, env=git_env)_git("fetch", "--quiet", url, f"+refs/pull/{number}/head:refs/pull/{number}/head", head, base,cwd=mirror, env=git_env)# Keeps a reviewed head after a force push, so that a rebase can be compared with it.[medium] Missing Test (werkrevisor)
hint; Jev: real 0.70, severity 2.1 of 3
The new
refs/reviewed/<number>/<head>ref, which keeps an earlier head available after a force push, has no test. The rebase carry-over depends on it: if theupdate-refcall is dropped or misspelled, the old head disappears from the mirror and the comparison against it fails silently while the test suite stays green.tests/test_codex.py::test_a_token_reaches_only_its_own_serveralready records every_gitcall, so asserting that the ref is created there would cover the behavior.Fixed in
eb07159:test_a_token_reaches_only_its_own_servernow checks the refsrefs/reviewed/<n>/<head>andrefs/reviewed/<n>/<head>-base.Codex review of
bc858266f6d6a4e1601249fab213694141b6db6b@@ -79,0 +83,4 @@if approval is None or not codex.has_commit(work, approval.head_sha):return None, Falsetry:approved_base = codex.merge_base(work, approval.head_sha, base)[high] Compare against the base used for the approved review (Codex review)
The approved base is reconstructed from the current base rather than preserved from the approved review. Suppose A was approved against T, then the PR is retargeted to an ancestor B of T and receives an empty commit H. Both signatures now compare against B and match, although B..T was absent from the reviewed diff T..A. The same_change path therefore carries the approval and unlocks even a critical result for H. Preserve and validate the original review base before carrying an approval.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
eb07159: each Codex result comment records its base in the header markerwerkrevisor-codex-base; the carry-over takes the base of the approved review from there, for the rebase comparison and for the comparison by Codex, and a comment without it carries nothing over.codex.checkoutkeeps a ref of that base too. Testtest_the_approved_change_is_taken_from_the_base_of_its_reviewis red with the current base.Codex review: critical
This changes merge authorization by allowing earlier human approvals to unlock later heads. An incorrect comparison can permit critical changes that the human never reviewed.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codexrunner.py:86, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 462167 (cached 400128) / out 3220, 114.8 s, 5 commands; reviewed head `bc858266f6d6a4e1601249fab213694141b6db6b`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟠 werkrevisor ORANGE for
eb07159, superseded by the result for992fbb3🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
bc85826not carried over: 17 approved block(s) are missingFindings
[high] Approval Bypass at
werkrevisor/codexrunner.py:428-430, ORANGE, as a line comment[medium] Ref Growth at
werkrevisor/codex.py:257-259, ORANGE, as a line comment[medium] Stale Diagram at
docs/werkrevisor.werkbaum:167-168hint; Jev: real 0.67, severity 2.0 of 3
This records a new Codex gate carry-over decision, but no corresponding review-flow diagram under
docs/is updated. Repository guidance requires diagrams to change with review decisions; leaving them unchanged will continue to depict the former no-carry-over behavior and can mislead operators.Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 55828, calls 1, tokens in 53536 / out 42947 (reasoning included in out), duration 338.7 s, cost 0.0676 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8976, cost 0.0004 USD, finding judge `jev-1.14.0` calls 3, tokens in 5815, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 39239, cost 0.0016 USD, build file judge `jev-1.14.0` calls 6, tokens in 12687, cost 0.0005 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.2 s, model_review 336.0 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `eb071591938c74e73bb4112178e12f20b98437d5` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
werkrevisor: ORANGE / FINDINGS for
eb071591938c74e73bb4112178e12f20b98437d5.The result comment of werkrevisor holds the details.
@@ -204,6 +254,9 @@ def checkout(base_url, owner, repo, number, head, base, token, root):_git("clone", "--bare", "--quiet", url, mirror, env=git_env)_git("fetch", "--quiet", url, f"+refs/pull/{number}/head:refs/pull/{number}/head", head, base,cwd=mirror, env=git_env)# Keeps a reviewed head and its base after a force push or a new target, so that a later head can be compared.[medium] Ref Growth (werkrevisor)
ORANGE; Jev: real 0.85, severity 2.8 of 3
Every checkout creates permanent
refs/reviewed/...refs for the head and base, and no code removes them. Because these refs keep old commits and blobs reachable, repeatedly reviewed or pushed heads cause the runner's bare mirror to grow indefinitely; a contributor can amplify this by repeatedly pushing heads. Retain only refs needed by live approvals or add deterministic pruning.Fixed in
992fbb3:codex.checkoutkeeps the refs of at mostMAX_REVIEWED_HEADS= 20 reviewed heads per PR and deletes older ones with their base; an approval of an older head carries nothing over. The real-git test checks the pruning, and that another PR keeps its refs; it is red without the pruning.@@ -345,0 +425,4 @@approval, approved_review_base = self.earlier_approval(server, gitea, number, head, login)approved_base, same_change = _compare(work, approval, approved_review_base, base, head)# After a rebase only the same change keeps the approval; Codex judges a difference only on top of it.since = approval.head_sha if approved_base and not same_change and codex.is_ancestor([high] Approval Bypass (werkrevisor)
ORANGE; Jev: real 0.76, severity 3.0 of 3
A rebased head with a changed diff does not necessarily wait for a human. The
sincerating is only enabled when the approved head remains an ancestor; after a rebase with a different signature, no carried marker is emitted, so the gate passes whenever Codex rates the whole PR as noncritical. This contradicts the documented rule that a rebase with any other change waits for a human. The new test does not expose this because it always uses a critical whole-PR rating.Does not hold, no change. After a rebase with another change the approval does not carry over: no carried marker, so the gate treats the head as one without an earlier approval. A head that Codex rates as not critical as a whole needs no human in the Codex gate, before this PR and after it; an earlier approval cannot make it stricter than a PR without one. Scenario#144.03 is about a critical PR ("the same PR"), and there the gate waits:
test_a_rebase_with_the_same_blocks_keeps_the_approval, caseother.Codex review: critical
The change relaxes the merge gate by allowing approval of an older head to authorize a newer one. A mistake could permit critical changes to merge without human approval.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 238791 (cached 175232) / out 1787, 47.6 s, 10 commands; reviewed head `eb071591938c74e73bb4112178e12f20b98437d5`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.Answer to the review of
eb07159:992fbb3.e1070e9.🟢 werkrevisor GREEN for
992fbb3, superseded by the result for9567fe5🟢 werkrevisor: GREEN / PASS
Gate: approved by werkrevisor; merge waits for 3 open hint(s): reply to each line comment and resolve it; answer the 2 hint(s) that stand only in the result comment in a PR comment, then restart the review or push a commit.
Change kind:
mixed.Reasons
Findings
[low] Incorrect bound at
docs/prs/2026-10-25-PR#144-codex-carry-over.mdhint
The document states that an approval of a head older than the 20 newest reviewed heads carries nothing over. The code only deletes the refs of the pruned heads (
_prune_reviewed); the commits themselves stay reachable in the mirror as long as that head is an ancestor of the current head, sohas_commitstill succeeds and the approval still carries over. Only after a force push or a rebase, where the old head is no longer reachable, does the approval really stop carrying over. Either the documented bound must be corrected or the code must check the refs instead of the objects.[low] Wrong git mechanism at
docs/prs/2026-10-25-PR#144-codex-carry-over.mdhint
The document claims that
ignore = allin the.gitmodulesof the PR can hide a new submodule commit from the change signature and that--ignore-submodules=noneprevents it. In Git, theignoreattribute of.gitmodulesonly affectsgit submodulecommands and nevergit diff; onlydiff.ignoreSubmodulesandsubmodule.<name>.ignoreof the repository configuration do, and that configuration belongs to the runner's mirror, not to the PR. The flag is harmless, but the documented scenario does not exist and the new test does not cover what it claims.Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 56928, calls 1, tokens in 54563 / out 56011 (reasoning included in out), duration 397.0 s, cost 0.0836 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9025, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, PR document check `jev-1.14.0` calls 9, tokens in 40152, cost 0.0017 USD, build file judge `jev-1.14.0` calls 6, tokens in 13159, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 394.5 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `992fbb3d7335f188198d721466c4dab38ce0d777` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
992fbb3d7335f188198d721466c4dab38ce0d777.The result comment of werkrevisor holds the details.
Codex review: critical
This changes a merge gate to accept earlier human approvals for later commits. A mistake could let critical changes merge without the required human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 351336 (cached 291200) / out 2549, 65.6 s, 16 commands; reviewed head `992fbb3d7335f188198d721466c4dab38ce0d777`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.Answer to the review of
992fbb3:9567fe5. The PR document now says that an older head stays in the mirror while it is an ancestor of the PR head, and that the bound takes effect after a force push.git difffollowssubmodule.<name>.ignorefrom.gitmodules(git-config: "the corresponding setting in .gitmodules" is used when the repository config sets none). The real-git test shows it: the subtest "ignored submodule" is red without--ignore-submodules=noneand green with it.New commits pushed, approval review dismissed automatically according to repository settings
🟠 werkrevisor ORANGE for
9567fe5, superseded by the result for153a20f🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
992fbb3not carried over: 1 approved block(s) are missingFindings
[high] Stale Base at
werkrevisor/codexrunner.py:394-400, ORANGE, as a line comment[medium] Missing Diagrams at
docs/werkrevisor.werkbaum:167-168hint; Jev: real 0.68, severity 1.9 of 3
The new carry-over decision changes the Codex review flow, but no diagram under
docs/is updated. Repository guidance requires diagrams to change whenever review steps or decisions change; leaving them unchanged makes the published flow inconsistent with the new gate behavior.[low] Retention Claim at
README.md:1071-1072hint
The README says a ref of every reviewed head and base is kept so a force push cannot remove it. The implementation retains only 20 selected heads and deletes both refs for the rest. Qualify the statement with the retention limit so users do not expect carry-over to survive for older approvals.
Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 56958, calls 1, tokens in 54603 / out 37555 (reasoning included in out), duration 339.5 s, cost 0.0614 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9051, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 3999, cost 0.0002 USD, PR document check `jev-1.14.0` calls 9, tokens in 40386, cost 0.0017 USD, build file judge `jev-1.14.0` calls 6, tokens in 13159, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.3 s, model_review 336.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `9567fe5e4d56eaa7fcb3d033c5c43262358a1701` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
werkrevisor: ORANGE / FINDINGS for
9567fe5e4d56eaa7fcb3d033c5c43262358a1701.The result comment of werkrevisor holds the details.
@@ -328,3 +393,2 @@number, head = pr["number"], pr["head"]["sha"]approved = human_approval(gitea.list_reviews(number), head, server.human_reviewers,since=comment.get("created_at")) is not Nonereviews = gitea.list_reviews(number)[high] Stale Base (werkrevisor)
ORANGE; Jev: real 0.75, severity 3.0 of 3
Existing result comments are reused for an unchanged head. This new path revalidates only the human review, not the base used for the comparison: the reviewed base marker is never compared with the PR's current merge base. Retarget the PR to an older or unrelated base without changing the head and the old carried marker still passes, although the new diff was never reviewed. Invalidate and rerun the result when its recorded base differs from the current base.
Fixed in
153a20f:find_codex_commenttakes the current base; a result whose header names another base does not count, so a retargeted head gets a new review. This also closes the gap for a result without a carried approval, which existed before this PR. The newest result of a head counts, so an approval before a new review carries nothing over. Testtest_a_result_for_another_base_does_not_countis red without the base.Codex review: critical
The change relaxes a merge gate by carrying human approvals to later commits. An error could authorize critical changes that the human never reviewed, so human review is required.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 397769 (cached 353280) / out 2083, 54.0 s, 10 commands; reviewed head `9567fe5e4d56eaa7fcb3d033c5c43262358a1701`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.Answer to the review of
9567fe5:153a20f, see its thread.153a20f: the README names the 20 newest heads.e1070e9.🟢 werkrevisor GREEN for
153a20f, superseded by the result forec8469b🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 58138, duration 725.8 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 9114, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 41503, cost 0.0017 USD, build file judge `jev-1.14.0` calls 6, tokens in 13670, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 722.9 s, config: defaults + host + repository (.werkrevisor.toml at main2 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `153a20fc25aa01504eab3a0db47d7d1d50a1be1d` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
153a20fc25aa01504eab3a0db47d7d1d50a1be1d.The result comment of werkrevisor holds the details.
Codex review of
153a20fc25aa01504eab3a0db47d7d1d50a1be1d@@ -111,0 +163,4 @@lines = header(c.get("body"))recorded = reviewed_base(c.get("body"))if (_own(c, login) and (head_marker(head) in lines or error_marker(head) in lines)and not (base and recorded and recorded != base)):[high] Failed reviews bypass the new base check (Codex review)
The base check accepts comments without a recorded base, but _review still creates every failed-review comment without base_marker. After a human approves such a failure, retargeting the same head reuses that comment and approval: collect queues no review and update_gate reports success. I reproduced this path. Failure comments must also identify their base so an approval for the previous target cannot unlock the changed PR.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
ec8469b: the comment of a failed review names its base in the header too, so a retargeted head gets a new review and the approval of the failure no longer counts. New testtest_a_failed_review_names_its_base; red without the fix.@@ -300,3 +370,3 @@number, head = pr["number"], pr["head"]["sha"]comments = gitea.list_issue_comments(number)comment, failed = find_codex_comment(comments, head, login)comment, failed = find_codex_comment(comments, head, login, pr.get("merge_base") or pr["base"]["sha"])[high] Invalidate the successful status before queuing a replacement review (Codex review)
When retargeting invalidates a cached review, collect returns a Candidate without resetting the existing successful status. run_repository then calls release before serve sets the status to pending. I reproduced the actual schedule_merge call with the old success still active, allowing a held merge to proceed before the new base is reviewed.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
ec8469b: in gate modecollectsetsqueued ⏳before it returns a candidate, so the release in the same run seespending, not the success for the old base.test_a_result_for_another_base_does_not_countchecks it; red without the fix.Codex review: critical
This changes the merge gate’s authorization rules by allowing earlier human approvals to unlock later heads. A mistake can permit unreviewed critical changes to merge.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codexrunner.py:372, as a line commentwerkrevisor/codexrunner.py:166, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 554031 (cached 488320) / out 4075, 102.5 s, 14 commands; reviewed head `153a20fc25aa01504eab3a0db47d7d1d50a1be1d`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
ec8469b, superseded by the result for0070059🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
153a20fnot carried over: 3 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 60178, duration 731.6 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 9170, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 42794, cost 0.0018 USD, build file judge `jev-1.14.0` calls 6, tokens in 14457, cost 0.0006 USD, phases: path_judge 0.4 s, classifier 0.6 s, doc_check 0.5 s, context 0.4 s, model_review 727.6 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `ec8469b9fbca3fbddffe595268539e30adfa7098` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
ec8469b9fbca3fbddffe595268539e30adfa7098.The result comment of werkrevisor holds the details.
Codex review of
ec8469b9fbca3fbddffe595268539e30adfa7098@@ -111,0 +162,4 @@for c in reversed(list(comments or ())):lines = header(c.get("body"))recorded = reviewed_base(c.get("body"))if (_own(c, login) and (head_marker(head) in lines or error_marker(head) in lines)[high] Legacy reviews bypass the retargeting check (Codex review)
The base comparison accepts comments without a recorded base. For an open PR reviewed before this upgrade, retargeting to an older base can introduce critical changes into the diff while preserving the head SHA. Its legacy noncritical result still passes the gate without another review. Reproduced through collect(): it sets success and queues no review. Require a fresh review when the cached result cannot establish its base.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
0070059: a result without a base no longer counts for the current base, so a head reviewed before 0.74.0 gets a new review; after the deployment each ready PR of agateoradviserepository gets one more Codex review. New testtest_a_result_without_its_base_gets_a_new_review; red without the fix.Codex review: critical
This changes the merge gate’s authorization rules by allowing earlier human approvals to unlock later commits. A mistake can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codexrunner.py:165, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 427869 (cached 364672) / out 3174, 93.1 s, 11 commands; reviewed head `ec8469b9fbca3fbddffe595268539e30adfa7098`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
0070059, superseded by the result for7f6a237🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
ec8469bnot carried over: 4 approved block(s) are missingFindings
[low] Contradictory force-push statement at
docs/prs/2026-10-25-PR#144-codex-carry-over.md:129-131hint
The solution states that after a force push an approval of the older head carries nothing over. This contradicts Scenario#144.03, the README and the code: a rebase is normally pushed with force, and there the approval still carries over whenever
codex.change_signatureof the approved change and of the current change is equal, while the rating of the change since the approval only requires the approved head to be an ancestor. Please say that only a force push which changes the change itself drops the approval. The same bullet also refers to "review round 9", which does not exist in the document (the list jumps from round 8 to round 10).Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 60638, calls 1, tokens in 58106 / out 53922 (reasoning included in out), duration 422.4 s, cost 0.0821 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 9228, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, PR document check `jev-1.14.0` calls 9, tokens in 43510, cost 0.0018 USD, build file judge `jev-1.14.0` calls 6, tokens in 14645, cost 0.0006 USD, phases: path_judge 0.6 s, classifier 0.5 s, doc_check 0.6 s, context 1.4 s, model_review 418.0 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `0070059abfa379ef7d90749ac218c6d9cbaeeabe` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
0070059abfa379ef7d90749ac218c6d9cbaeeabe.The result comment of werkrevisor holds the details.
Codex review of
0070059abfa379ef7d90749ac218c6d9cbaeeabe@@ -331,1 +406,4 @@still = sha and carryover.human_approval(reviews, lambda: gitea.list_timeline(number), head,server.human_reviewers)carried = bool(still) and still.head_sha == shaself.set_status(gitea, head, *gate_state(comment, failed, open_codex_line_comments(gitea, number, login),[high] Retargeting during review bypasses the base check (Codex review)
If the PR is retargeted while Codex runs, serve() passes the original PR snapshot to update_gate(), which publishes success without checking the current base. An in-memory reproduction confirmed success despite different reviewed and current bases. Because retargeting preserves the head SHA, that status can unlock a scheduled merge of the unreviewed change before the next poll. Re-fetch the PR and verify its base before publishing success.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
7f6a237: after a review,servereads the PR again; if its base or head changed during the review, the status staysqueued ⏳and the next run reviews the head for the new base. New testtest_a_retarget_during_the_review_sets_no_success; red without the fix.Codex review: critical
This relaxes a merge gate by allowing earlier human approvals to authorize later heads. Incorrect carry-over can permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codexrunner.py:409, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 491955 (cached 425216) / out 3376, 104.5 s, 12 commands; reviewed head `0070059abfa379ef7d90749ac218c6d9cbaeeabe`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.Answer to the review of
0070059:7f6a237. The PR document says now that only a force push that removes the head from the PR can drop its pruned commit, and no longer refers to a round 9.7f6a237, see its thread.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
7f6a237, superseded by the result for48b7482🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
0070059not carried over: 3 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 61683, duration 725.9 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 9287, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 44562, cost 0.0019 USD, build file judge `jev-1.14.0` calls 6, tokens in 15166, cost 0.0006 USD, phases: path_judge 0.4 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 722.7 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `7f6a23797ff7a4c1c8ef8333db38527565772c7b` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
7f6a23797ff7a4c1c8ef8333db38527565772c7b.The result comment of werkrevisor holds the details.
Codex review of
7f6a23797ff7a4c1c8ef8333db38527565772c7b@@ -300,3 +374,3 @@number, head = pr["number"], pr["head"]["sha"]comments = gitea.list_issue_comments(number)comment, failed = find_codex_comment(comments, head, login)comment, failed = find_codex_comment(comments, head, login, pr.get("merge_base") or pr["base"]["sha"])[high] Revalidate the current target before accepting a cached review (Codex review)
If a PR is retargeted after list_open_prs() returns, collect() matches its cached review against the old snapshot and calls update_gate() without checking the current target. The unchanged_target() check only runs after a new review in serve(). I reproduced collect() publishing success and queuing no review while unchanged_target() returned false. Because the head SHA stays unchanged, this can unlock a merge against an unreviewed base. Apply the target check to cached results before publishing success.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
48b7482: the check moved fromserveintoupdate_gate, so everysuccessfirst reads the PR again, also for a result from before; with another base or head the status staysqueued ⏳. New testtest_a_retarget_after_the_list_of_prs_sets_no_success; red without the fix. Between that read and setting the status, a retarget within milliseconds stays possible; Gitea offers no conditional status, and the next run corrects it.Codex review: critical
This changes a merge gate to accept earlier human approvals for later heads. A mistake can permit critical changes to merge without human approval.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codexrunner.py:376, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 603582 (cached 551040) / out 3435, 111.3 s, 11 commands; reviewed head `7f6a23797ff7a4c1c8ef8333db38527565772c7b`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
48b7482, superseded by the result for368bb7d🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
7f6a237not carried over: 5 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 62032, duration 725.5 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 9329, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 45070, cost 0.0019 USD, build file judge `jev-1.14.0` calls 6, tokens in 15296, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.4 s, doc_check 0.5 s, context 0.3 s, model_review 722.6 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
568ae2b).Reviewed head `48b7482c7f311dfd3b4aad2b1d705a54165809ad` against base `568ae2b309698457383611a4db0f6b9502340425`, werkrevisor 0.72.0.
werkrevisor: GREEN / PASS for
48b7482c7f311dfd3b4aad2b1d705a54165809ad.The result comment of werkrevisor holds the details.
Codex review: critical
The change relaxes the merge gate by allowing an earlier human approval to authorize a later head. An error in approval reuse could permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 476464 (cached 431104) / out 2747, 72.5 s, 9 commands; reviewed head `48b7482c7f311dfd3b4aad2b1d705a54165809ad`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.werkrevisor 0.74.0: the Codex gate keeps a human approval when the change since then is not criticalto werkrevisor 0.76.0: the Codex gate keeps a human approval when the change since then is not critical48b7482c7fto368bb7d00dNew commits pushed, approval review dismissed automatically according to repository settings
New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
368bb7d, superseded by the result for1c964b0🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
48b7482not carried over: 4 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 62036, duration 725.7 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 9341, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 45178, cost 0.0019 USD, build file judge `jev-1.14.0` calls 6, tokens in 15296, cost 0.0006 USD, phases: path_judge 0.3 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 722.7 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
4f9319c).Reviewed head `368bb7d00d04182639d60dd776ee9fe050242c89` against base `4f9319cefe72e806fa2a843515be7dfd97bd792f`, werkrevisor 0.74.1.
werkrevisor: GREEN / PASS for
368bb7d00d04182639d60dd776ee9fe050242c89.The result comment of werkrevisor holds the details.
Codex review of
368bb7d00d04182639d60dd776ee9fe050242c89@@ -111,0 +167,4 @@lines = header(c.get("body"))recorded = reviewed_base(c.get("body"))if (_own(c, login) and (head_marker(head) in lines or error_marker(head) in lines)and not (base and recorded != base)):[high] Retargeting back reuses an approval for a different base (Codex review)
The search skips newer results for other bases and accepts an older matching result. If head H is reviewed against B1, then reviewed and human-approved against B2, retargeting back to B1 selects the original result. update_gate accepts the B2 approval because its head matches and its timestamp follows that older result. I reproduced a success status for this sequence: critical changes present only against B1 can pass without approval of that change. A newer review for another base must invalidate reuse of the older result.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
1c964b0: only the newest result of a head counts; when it names another base,find_codex_commentreturns none and the head gets a new review, so a retarget back to B1 does not revive the older B1 result. New testtest_a_retarget_back_needs_a_new_reviewwith your sequence; red when an older result for the base still counts.Codex review: critical
This changes the authorization enforced by a merge gate, allowing older human approvals to unlock later heads. A mistake could permit critical changes to merge without human review.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
Findings
werkrevisor/codexrunner.py:170, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 469709 (cached 419968) / out 3352, 162.7 s, 11 commands; reviewed head `368bb7d00d04182639d60dd776ee9fe050242c89`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
1c964b0, superseded by the result forea14417🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
368bb7dnot carried over: 3 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 62822, duration 725.5 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 9388, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 45933, cost 0.0019 USD, build file judge `jev-1.14.0` calls 6, tokens in 15628, cost 0.0007 USD, phases: path_judge 0.4 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 722.5 s, config: defaults + host + repository (.werkrevisor.toml at main5 answers ignored because of a confidence below [classifier] min_confidence.
4f9319c).Reviewed head `1c964b0ca53f956d53ea9d6f2b798488a1a05302` against base `4f9319cefe72e806fa2a843515be7dfd97bd792f`, werkrevisor 0.74.1.
werkrevisor: GREEN / PASS for
1c964b0ca53f956d53ea9d6f2b798488a1a05302.The result comment of werkrevisor holds the details.
Codex review: critical
The change relaxes a merge gate by allowing earlier human approvals to authorize later heads. An incorrect carry-over decision could permit unapproved security-sensitive changes to merge, which the tests cannot fully rule out.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 419493 (cached 351360) / out 3613, 159.6 s, 10 commands; reviewed head `1c964b0ca53f956d53ea9d6f2b798488a1a05302`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.1c964b0ca5toea1441774aNew commits pushed, approval review dismissed automatically according to repository settings
New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor GREEN for
ea14417, superseded by the result forfaa3318🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
1c964b0not carried over: 3 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 62758, duration 727.8 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 9380, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 45861, cost 0.0019 USD, build file judge `jev-1.14.0` calls 6, tokens in 15628, cost 0.0007 USD, phases: path_judge 0.4 s, classifier 0.5 s, doc_check 0.5 s, context 0.3 s, model_review 722.9 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
9131a08).Reviewed head `ea1441774afebef566325f1b2c42c3dde20b4e9b` against base `9131a08453ece0bf5bb1e78fa26852d1c8620410`, werkrevisor 0.75.0.
werkrevisor: GREEN / PASS for
ea1441774afebef566325f1b2c42c3dde20b4e9b.The result comment of werkrevisor holds the details.
Codex review: critical
This changes the merge gate’s authorization logic by allowing an earlier human approval to unlock a later head. A mistake could permit critical, unapproved changes to merge despite passing tests.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 355026 (cached 292992) / out 3306, 157.4 s, 9 commands; reviewed head `ea1441774afebef566325f1b2c42c3dde20b4e9b`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.werkrevisor 0.76.0: the Codex gate keeps a human approval when the change since then is not criticalto werkrevisor 0.77.0: the Codex gate keeps a human approval when the change since then is not criticalea1441774atofaa3318743New commits pushed, approval review dismissed automatically according to repository settings
🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
ea14417not carried over: 4 approved block(s) are missingClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 62705, duration 726.0 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 9380, cost 0.0004 USD, PR document check `jev-1.14.0` calls 9, tokens in 45861, cost 0.0019 USD, build file judge `jev-1.14.0` calls 6, tokens in 15628, cost 0.0007 USD, phases: path_judge 0.4 s, classifier 0.5 s, doc_check 0.5 s, context 0.2 s, model_review 722.5 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
4230f62).Reviewed head `faa3318743422fa2808fd44121bd3e5ea6c82c0b` against base `4230f6262da5f7c2964b6158a9db1368c2ff25ef`, werkrevisor 0.76.1.
werkrevisor: GREEN / PASS for
faa3318743422fa2808fd44121bd3e5ea6c82c0b.The result comment of werkrevisor holds the details.
Codex review: critical
This changes the merge gate to accept an earlier human approval for a later head. A mistake could let a critical change merge without fresh human review, and tests cannot establish the correctness of every model judgment.
Security effect: mixed; fails closed: no; a test of the closed case: yes.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 621955 (cached 544768) / out 3264, 126.1 s, 13 commands; reviewed head `faa3318743422fa2808fd44121bd3e5ea6c82c0b`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.