fix(622): bind H10 merge consent to the reviewed sha via a required commit status #630

Merged
timothy merged 6 commits from feat/622-verdict-status-check into main 2026-07-25 23:33:16 +02:00
Owner

fixes #622

The hole

pretooluse-merge-consent.sh proves all three consent conditions at the moment the merge tool is called. With merge_when_checks_succeed=true, condition (a) is delegated to Gitea — which then merges whatever head is green at its merge time, while (b) Done-when and (c) the head-referencing verdict were proven against the head at scheduling time. Commits pushed in between merge unreviewed. The gate isn't bypassed; it's satisfied against a snapshot that stops being true.

Correction to the issue's premise

#622's motivating anecdote is wrong, and this PR corrects it rather than repeating it. PR #619 does carry Review-verdict: MERGEABLE @ 02c82b35, posted at 20:36:02 — six seconds before the 20:36:08 merge — explicitly re-reviewing the two follow-up commits. The issue was filed off a lagging API read. Caught by the independent reviewer, after I had already propagated the claim into a commit message, a decision record and two docs.

The hole is real anyway, and structural: nothing forced that re-review inside the 45-minute window Gitea would have merged in. It held by diligence. This makes it hold by construction.

Demonstrated, not asserted

Controlled A/B on throwaway branches (main's protection never touched), with a CI check left pending so Gitea genuinely waits:

arm required contexts unreviewed commit pushed after scheduling
without this change ci/fake ⚠️ merged
with this change ci/fake, review-verdict/h10 refused
then review the new head merges — blocked, not deadlocked

Also verified against live Gitea 1.25.4: a missing required context blocks (an unrelated status event forced a real re-evaluation and it still refused), and .status — not .state — is the field the status-list endpoint populates.

The fix

A commit status belongs to one sha, so a status written for a parent cannot be inherited by a child that didn't exist yet. review-verdict/h10 becomes a required check: push a new head and the context is simply absent. No re-evaluation hook, no polling, and it covers merge paths the PreToolUse hook never sees (UI, raw API, another session).

  • scripts/post-review-verdict.sh — writes comment + status together; re-reads head afterwards and writes no status if a commit slipped in, rather than retargeting the verdict at unread code.
  • .gitea/workflows/review-verdict.yml — auto-passes Renovate-authored and docs-only PRs (Renovate uses platformAutomerge, so it would otherwise stall); pending otherwise. Exemptions void for .claude/, .gitea/, .husky/, scripts/, docker/ci/.
  • Hook refuses to schedule an auto-merge without a green verdict status, and no longer claims "CI green" on the one path where it never read CI.

Two silent false negatives, found by verifying rather than reasoning

Both fixed at both call sites — the new workflow and the hook's pre-existing docs-only carve-out:

  1. The files endpoint caps at 50 rows and ignores a larger limit. PR #619 has 194 changed files; ?limit=100 returns 50. A single page saw zero protected paths there where full enumeration finds ten. Both now page to exhaustion and withhold the exemption if they can't complete.
  2. A rename is one row whose filename is the destination, source only in previous_filename. Verified live: .gitea/workflows/renovate.ymldocs/innocuous-note.md presented as docs-only with no protected path visible. Both now read both sides. (An initial check against #619 suggested Gitea exposed no rename data — a false negative, since that PR contained no renames. A constructed positive case settled it.)

Limits, stated rather than papered over

Base changes leave the head sha (and so the verdict) unchanged; and a PR editing review-verdict.yml is judged by its own edited copy if Gitea runs pull_request workflows from the head — so PROTECTED is a guardrail against accident, not a tamper-proof control. Fine for a two-account repo; documented in the decision record.

Verification

95 script tests green, shellcheck clean, decisions validator + catalog --check + kickoff guard OK. New suite mutation-proven: removing the TOCTOU guard, flipping BLOCKED→success, and posting on a short sha each fail a test. Tests cross-check the emitted comment against the hook's own condition-(c) regexes so the two implementations can't drift.

review-verdict/h10 is not yet a required context — I add it to branch protection after this merges, so this PR isn't gated by a check that doesn't exist on main yet.

fixes #622 ## The hole `pretooluse-merge-consent.sh` proves all three consent conditions **at the moment the merge tool is called**. With `merge_when_checks_succeed=true`, condition (a) is delegated to Gitea — which then merges whatever head is green at *its* merge time, while (b) Done-when and (c) the head-referencing verdict were proven against the head at **scheduling** time. Commits pushed in between merge unreviewed. The gate isn't bypassed; it's **satisfied against a snapshot that stops being true**. ## Correction to the issue's premise **#622's motivating anecdote is wrong, and this PR corrects it rather than repeating it.** PR #619 *does* carry `Review-verdict: MERGEABLE @ 02c82b35`, posted at 20:36:02 — six seconds before the 20:36:08 merge — explicitly re-reviewing the two follow-up commits. The issue was filed off a lagging API read. Caught by the independent reviewer, after I had already propagated the claim into a commit message, a decision record and two docs. The hole is real anyway, and **structural**: nothing *forced* that re-review inside the 45-minute window Gitea would have merged in. It held by diligence. This makes it hold by construction. ## Demonstrated, not asserted Controlled A/B on throwaway branches (`main`'s protection never touched), with a CI check left pending so Gitea genuinely waits: | arm | required contexts | unreviewed commit pushed after scheduling | |---|---|---| | without this change | `ci/fake` | ⚠️ **merged** | | with this change | `ci/fake`, `review-verdict/h10` | ✅ **refused** | | then review the new head | | merges — blocked, **not** deadlocked | Also verified against live Gitea 1.25.4: a **missing** required context blocks (an unrelated status event forced a real re-evaluation and it still refused), and `.status` — not `.state` — is the field the status-list endpoint populates. ## The fix A commit status belongs to **one sha**, so a status written for a parent cannot be inherited by a child that didn't exist yet. `review-verdict/h10` becomes a required check: push a new head and the context is simply absent. No re-evaluation hook, no polling, and it covers merge paths the PreToolUse hook never sees (UI, raw API, another session). - `scripts/post-review-verdict.sh` — writes comment + status together; re-reads head afterwards and writes **no** status if a commit slipped in, rather than retargeting the verdict at unread code. - `.gitea/workflows/review-verdict.yml` — auto-passes Renovate-authored and docs-only PRs (Renovate uses `platformAutomerge`, so it would otherwise stall); `pending` otherwise. Exemptions **void** for `.claude/`, `.gitea/`, `.husky/`, `scripts/`, `docker/ci/`. - Hook refuses to *schedule* an auto-merge without a green verdict status, and no longer claims "CI green" on the one path where it never read CI. ## Two silent false negatives, found by verifying rather than reasoning Both fixed at **both** call sites — the new workflow and the hook's pre-existing docs-only carve-out: 1. **The files endpoint caps at 50 rows and ignores a larger `limit`.** PR #619 has 194 changed files; `?limit=100` returns 50. A single page saw **zero** protected paths there where full enumeration finds **ten**. Both now page to exhaustion and withhold the exemption if they can't complete. 2. **A rename is one row** whose `filename` is the destination, source only in `previous_filename`. Verified live: `.gitea/workflows/renovate.yml` → `docs/innocuous-note.md` presented as docs-only with no protected path visible. Both now read **both** sides. (An initial check against #619 suggested Gitea exposed no rename data — a false negative, since that PR contained no renames. A constructed positive case settled it.) ## Limits, stated rather than papered over Base changes leave the head sha (and so the verdict) unchanged; and a PR editing `review-verdict.yml` is judged by its own edited copy if Gitea runs `pull_request` workflows from the head — so `PROTECTED` is a guardrail against **accident**, not a tamper-proof control. Fine for a two-account repo; documented in the decision record. ## Verification 95 script tests green, shellcheck clean, decisions validator + catalog `--check` + kickoff guard OK. New suite mutation-proven: removing the TOCTOU guard, flipping `BLOCKED`→success, and posting on a short sha each fail a test. Tests cross-check the emitted comment against the hook's *own* condition-(c) regexes so the two implementations can't drift. `review-verdict/h10` is **not yet** a required context — I add it to branch protection after this merges, so this PR isn't gated by a check that doesn't exist on `main` yet.
timothy added 1 commit 2026-07-25 21:52:03 +02:00
fix(622): bind H10 merge consent to the reviewed sha via a required commit status
review-verdict/h10 Awaiting review verdict for 628ae46
Review verdict / Set review-verdict status (pull_request) Successful in 9s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 11s
PR Gates / Docs update reminder (pull_request) Successful in 15s
PR Gates / decisions lifecycle (pull_request) Successful in 26s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Failing after 1m28s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 9s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 7s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 16m59s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 19m5s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
628ae46f7f
`pretooluse-merge-consent.sh` proves all three consent conditions at the moment
the merge tool is called. With `merge_when_checks_succeed=true`, condition (a) is
delegated to Gitea, which then merges whatever head is green at ITS merge time —
while (b) Done-when and (c) the head-referencing verdict were proven against the
head at SCHEDULING time. Every commit pushed in between merges unreviewed. The
gate is not bypassed; it is satisfied against a snapshot that stops being true.

Demonstrated as a controlled A/B rather than inferred, with a CI check left
pending so Gitea waits as it really does:

  without a required verdict context: unreviewed commit B MERGED
  with it:                            same sequence REFUSED
  after reviewing B and posting it:   merges — blocked, not deadlocked

NOTE the anecdote in #622 is wrong and is corrected in the docs rather than
repeated: PR #619 does carry `Review-verdict: MERGEABLE @ 02c82b35`, posted six
seconds before the merge, explicitly re-reviewing the follow-up commits. #622 was
filed off a lagging API read. The hole is real regardless, and structural —
nothing FORCED that re-review inside the window Gitea would have merged in. This
turns a property that held by diligence into one that holds by construction.

The fix is the sha, not a smarter check. A Gitea commit status belongs to exactly
one commit, so a status written for a parent cannot be inherited by a child that
did not exist yet. `review-verdict/h10` becomes a REQUIRED status check on main:
push a new head and the context is simply absent, which Gitea reads as
not-passing (verified against 1.25.4: missing AND pending both block, and
auto-merge re-checks the current head). It also covers merge paths the hook never
sees — Gitea UI, raw API, another agent's session.

- scripts/post-review-verdict.sh writes the comment and the status together so
  they cannot drift, and re-reads head after commenting: if a commit landed
  mid-flight it writes NO status and exits non-zero rather than retargeting the
  verdict at a commit nobody read.
- .gitea/workflows/review-verdict.yml auto-passes the two exempt classes that
  would otherwise deadlock — Renovate-authored (platformAutomerge) and docs-only
  — and marks everything else pending. Exemptions are void when the PR touches
  .claude/, .gitea/, .husky/, scripts/ or docker/ci/.
- The hook refuses to SCHEDULE an auto-merge unless that status is green on head,
  and no longer claims "CI green" on the mwcs path, where it never read CI.

Two silent false negatives in the exemption path, both found by verifying rather
than reasoning, both fixed at BOTH call sites (workflow and the hook's
pre-existing docs-only carve-out):

- The files endpoint caps at 50 rows and IGNORES a larger `limit` — PR #619 has
  194 changed files and `?limit=100` returns 50. A single page saw ZERO protected
  paths there where the full enumeration finds ten. Both now page to exhaustion
  and withhold the exemption if they cannot complete.
- A rename is ONE row whose `filename` is the destination, the source only in
  `previous_filename`. Verified live: `.gitea/workflows/renovate.yml` ->
  `docs/innocuous-note.md` presented as docs-only with no protected path visible.
  Both now read BOTH sides.

Limits are documented, not papered over: base changes leave the head sha (and so
the verdict) unchanged, and a PR editing the workflow is judged by its own edited
copy — so PROTECTED is a guardrail against accident, not a tamper-proof control.

fixes #622
timothy added 1 commit 2026-07-25 21:54:02 +02:00
docs(622): record head-execution as verified, not conditional
review-verdict/h10 Awaiting review verdict for a9e3e23
Review verdict / Set review-verdict status (pull_request) Successful in 2s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 12s
PR Gates / Docs update reminder (pull_request) Successful in 15s
PR Gates / decisions lifecycle (pull_request) Successful in 25s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m15s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 9s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 6s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 17m12s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 20m15s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
a9e3e23abf
The workflow this PR introduces does not exist on `main`, yet its job ran on
#630 and posted a status — so Gitea runs `pull_request` workflows from the PR
HEAD. Limitation 4 was written as a conditional ("if Gitea runs..."); it is now
a confirmed property, which makes PROTECTED definitively a guardrail against
accident rather than a control against a PR that rewrites the classifier.

Same run also confirms the good case: #630 was correctly judged non-exempt on
its `.claude/`+`scripts/` paths and got `review-verdict/h10=pending`.

Decisions-Edit: yes
timothy added 1 commit 2026-07-25 22:04:22 +02:00
fix(622): make the exemption paging fail CLOSED; read statuses latest-per-context
review-verdict/h10 Awaiting review verdict for f6d01c4
Review verdict / Set review-verdict status (pull_request) Successful in 3s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 14s
PR Gates / Docs update reminder (pull_request) Successful in 15s
PR Gates / decisions lifecycle (pull_request) Successful in 22s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m59s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 10s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 7s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 16m9s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 20m51s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
f6d01c4924
Cross-family re-review returned BLOCKED on the fix commit, correctly. Two of the
three findings were on the FAILURE path, which is why every happy-path run and
the whole first test suite looked fine.

1. (High/Medium) Both paging loops treated a failed or non-array page as a
   legitimate short final page: empty output counts as zero rows, zero rows reads
   as "end of list", and the enumeration completed from a PARTIAL list — failing
   OPEN at exactly the point the guard exists to fail closed, while the decision
   record claimed the opposite. Each page is now validated as a JSON array before
   its rows are counted; anything else withholds the exemption.

2. (High) The workflow's "is there already a verdict" pre-check read
   `/statuses/{sha}`, which returns one row per status POST rather than per
   context and pages at 50 — so a head with a few CI reruns can push a real
   verdict off the first page (this PR's head already carries 15 rows for 11
   contexts). Missing it there is not benign: the job would post `pending`, or an
   exemption `success`, OVER a human verdict. Both the workflow and the hook now
   read `/commits/{sha}/status` (latest-per-context), and the workflow refuses to
   post anything when that read fails rather than inferring "no verdict yet".
   Credit: raised independently by the parallel session on this issue.

3. (Low) The record claimed both callers withhold the exemption when enumeration
   cannot complete. True only after (1); the claim and the code now agree.

Adds scripts/tests/test_merge_consent_exemption.py — 9 tests over the hook's
exemption path via a curl shim, asserting behaviour when a page ERRORS or returns
a non-array body, not merely when it returns data. Mutation-proven: reverting the
array validation fails both failure-path tests; dropping `previous_filename`
fails the rename test.

One test I wrote was wrong and the run caught it: I asserted a
`.gitea/... -> docs/...` rename must break the hook's exemption, but that hook
deliberately exempts `.claude/`/`.gitea/`/`.husky/` too (to a human PROMPT, never
an auto-grant), so both sides legitimately match. The rename guard matters there
for a genuinely non-exempt source; the stricter PROTECTED semantics belong to the
workflow. Both cases are now pinned, including a positive control that a
docs->docs rename stays exempt.

Decisions-Edit: yes
timothy added 1 commit 2026-07-25 22:23:59 +02:00
fix(622): validate file pages per ROW, and de-vacuum the exemption tests
review-verdict/h10 Awaiting review verdict for fc547b2
Review verdict / Set review-verdict status (pull_request) Successful in 11s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 15s
PR Gates / Docs update reminder (pull_request) Successful in 23s
PR Gates / decisions lifecycle (pull_request) Successful in 30s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 18s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 15s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m8s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 15m20s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 15m56s
fc547b2d46
Second BLOCKED verdict, also correct. My previous fix validated only the
TOP-LEVEL type of each page, which left the same hole one level down: `[{}]` is
a well-formed JSON array whose rows carry no `filename`, so it contributes zero
paths, looks like a short final page, and completes the enumeration from a
PARTIAL list — the exact failure the guard exists to prevent, just wearing a
valid-looking wrapper. An array of scalars additionally tripped `set -e` during
extraction without producing a decision.

Both call sites now require every row to carry a non-empty string `filename`.
An empty array remains valid, since that is what a real end-of-pagination is.
Predicate checked against nine shapes before adopting: `[]` and well-formed rows
valid; `[{}]`, `[{"filename":""}]`, `[{"filename":null}]`, `["x"]`, `[1,2]`, a
bare error object, and a mixed good/bad array all rejected.

The reviewer also caught that my `test_first_page_failure_*` was VACUOUS: with
page 1 failing the path list is empty, and the hook independently withholds the
exemption on an empty list, so it passed with the guard removed. It is kept as a
smoke case with that limitation stated in its docstring, and three tests that
actually pin the behaviour were added — malformed rows and scalar rows after a
FULL page 1 (so the path list is non-empty and the exemption would really fire),
plus MAX_PAGES exhaustion.

Added a positive control too: an empty final page must still exempt. Without it,
an over-strict guard would make every "withholds the exemption" test pass for the
wrong reason — the suite would be asserting "never exempt", which is not the
contract. Verified by mutation: top-level-only validation fails 2 tests,
an over-strict guard fails the positive control, and widening MAX_PAGES fails the
exhaustion test.

Decisions-Edit: yes
timothy added 1 commit 2026-07-25 22:43:33 +02:00
fix(622): validate rename rows too; name the real blocker when only the verdict is pending
review-verdict/h10 Awaiting review verdict for 07e1e8c
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 12s
Review verdict / Set review-verdict status (pull_request) Successful in 10s
PR Gates / decisions lifecycle (pull_request) Successful in 21s
PR Gates / Docs update reminder (pull_request) Successful in 19s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 1m19s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 1m28s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m5s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 15m31s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 15m44s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
07e1e8cfbc
Third BLOCKED verdict, third legitimate finding. The per-row guard validated the
DESTINATION only, so `{"filename":"docs/x.md","status":"renamed"}` with no
`previous_filename` passed while its source silently vanished — precisely the
path-hiding that collecting both sides exists to prevent. A rename row must now
carry a non-empty `previous_filename`.

Required for rename rows ONLY. Demanding it globally would reject every ordinary
modified/added row and make the gate refuse all exemptions — which every
"withholds the exemption" test would happily pass through, so that direction gets
its own positive control. Predicate checked against ten shapes before adopting;
mutation-verified in both directions (dropping the clause fails 2 tests, applying
it globally fails 3 including the controls).

Also fixes a wart this PR introduced. `review-verdict/h10` is itself folded into
the COMBINED status, so a PR awaiting its verdict reports combined 'pending' and
the hook's condition (a) reported it as a CI problem — sending a reader to build
logs when the missing thing is the review, and exiting before the H10 branch that
would have said so. The message now names the outstanding contexts, and says
plainly when the verdict is the only one left.

Scope boundary, stated in the record: real Gitea populates `previous_filename` on
renames and returns well-formed pages. Everything past this point defends against
shapes with no evidence of existing, so the guard's claim stays "any page we
cannot fully classify withholds the exemption" rather than growing to cover
unobserved responses.

Decisions-Edit: yes
timothy added 1 commit 2026-07-25 23:16:33 +02:00
fix(622): classify every non-success CI state, and word a negative verdict correctly
Review verdict / Set review-verdict status (pull_request) Successful in 10s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 10s
PR Gates / Docs update reminder (pull_request) Successful in 21s
PR Gates / decisions lifecycle (pull_request) Successful in 33s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 1m23s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 1m31s
review-verdict/h10 Review-verdict: MERGEABLE @ 9f5317b
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m20s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 16m3s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 16m14s
9f5317bfa5
Fourth review round, two Low findings, both in the condition-(a) message change
I added last round. Both are message-only — every path still denies — but both
would have printed something flatly false at the moment someone is trying to
understand why a merge is blocked.

1. The filter selected only `pending` and `failure`, but Gitea also has `error`
   (and `warning`). With `review-verdict/h10=pending` and a build in `error`, the
   errored build fell out of the set, leaving the verdict as the lone entry — and
   the hook then claimed "every CI check is green" while a build was erroring.
   Non-green is now anything that is not `success`.

   `skipped` is deliberately still counted as GREEN: the image-push job skips on
   every PR (#593 — a skipped context is not red), so treating it as non-green
   would have permanently suppressed the H10-specific message in the one
   situation it exists for.

2. If the sole non-green context was `review-verdict/h10=failure` — i.e. someone
   reviewed this head and REJECTED it — the message said no verdict existed yet
   and told the reader to post MERGEABLE. It now branches on the verdict's own
   state: pending means nobody has reviewed this head, failure/error means it was
   reviewed and rejected, so resolve the findings.

Also folds the two combined-status fetches into one.

Verified against eight payloads: verdict-only-pending, verdict-pending-plus-error,
verdict-failure-alone, skipped-plus-verdict-pending, two-pending, a real build
failure, a warning state, and an unreadable body — each producing the intended
message and no other.

Both review channels independently confirmed this round that the rename
validation is correct at both call sites, that the tests meaningfully pin it, and
that no ALLOW/GRANT path became more permissive.
Author
Owner

Review-verdict: MERGEABLE @ 9f5317b

Fifth cross-family pass, scoped to the condition-(a) diff: no new defects. Confirms the skipped-is-green carve-out is safe (a genuinely failed upstream job still surfaces its own failure/error context), that both jq filters share one predicate so vonly can never disagree with nongreen, that the single-fetch refactor still routes an unreadable response to ask, and that the change is strictly message-only with no allow/grant path made more permissive.

Prior rounds (all BLOCKED, all fixed): paging failed OPEN on a mid-pagination error; top-level-only validation missed [{}] rows; rename rows validated only the destination; the non-green filter missed Gitea's error state and mis-worded a negative verdict. Plus the corrected #619 narrative and the statuses/{sha} truncation raised by the parallel session.

Posted with scripts/post-review-verdict.sh — its own first live use.

Review-verdict: MERGEABLE @ 9f5317b Fifth cross-family pass, scoped to the condition-(a) diff: no new defects. Confirms the `skipped`-is-green carve-out is safe (a genuinely failed upstream job still surfaces its own failure/error context), that both jq filters share one predicate so `vonly` can never disagree with `nongreen`, that the single-fetch refactor still routes an unreadable response to `ask`, and that the change is strictly message-only with no allow/grant path made more permissive. Prior rounds (all BLOCKED, all fixed): paging failed OPEN on a mid-pagination error; top-level-only validation missed `[{}]` rows; rename rows validated only the destination; the non-green filter missed Gitea's `error` state and mis-worded a negative verdict. Plus the corrected #619 narrative and the `statuses/{sha}` truncation raised by the parallel session. Posted with scripts/post-review-verdict.sh — its own first live use.
timothy merged commit 98f9e6ec34 into main 2026-07-25 23:33:16 +02:00
timothy deleted branch feat/622-verdict-status-check 2026-07-25 23:33:16 +02:00
Sign in to join this conversation.