fix(ci): stop fork GPT lane hiding a verdict behind an incomplete run (#8292) - #8350
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS A real verdict-visibility bug, solved at the right layer with a fail-safe asymmetry, and the merge gate stays untouched. The no-touch-on-incomplete choice is structurally justified for this lane: its concurrency group is keyed per Suggestions
[DESIGN-REVIEWED] 3511c94 |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of I have everything I need. The verification confirmed: (1) claude-review.yml already ships a smaller shape for this exact job — an incomplete run posts nothing at all (line 456–459); (2) the author's sibling census undercounts — the unconditional-PATCH-of-a-non-verdict-body construct lives in seven other lanes, not the one named in the follow-up; (3) the PR's own rationale for never-touch contradicts the preserve-and-prepend fix #8342 landed in codex-review.yml. Final review follows. First-Principles-Verdict: CONCERNS The fix is real and cause-level, but half its new machinery guards a placeholder claude-review.yml already deletes, and the sibling census names 1 of 7 unfixed lanes. What this change shipsIntent: stop an incomplete fork-GPT rerun from burying a posted verdict comment (#8292) — a FIX.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 3511c94 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsNo findings. The lone candidate requires [OPUS-REVIEWED] 3511c94 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
6d77fd7 to
8289555
Compare
8289555 to
6f4226c
Compare
6f4226c to
acd4fb7
Compare
acd4fb7 to
5d016b7
Compare
1e4ffa1 to
f7658ec
Compare
f7658ec to
d34beea
Compare
…body The fork-gpt-review.yml 'Post/update summary comment' step PATCHed the bot's marker comment unconditionally, so an incomplete rerun overwrote a posted [GPT-REVIEWED]/[BLOCK-MERGE] verdict with a 'review incomplete' body, burying the finding in GraphQL edit history no REST reader consults (#8292). Mirror PR #8342's #8292 fix into the fork lane, but stricter: an incomplete run NEVER modifies an existing comment. Overlapping runs for different SHAs can read the comment before a newer run PATCHes its verdict in, so even a PATCH that only preserved the verdict and prepended a stale notice would restore the older body it read, clobbering the newer verdict. So when the run is incomplete and a bot comment already exists, leave it untouched regardless of whether it carries [GPT-REVIEWED]. Only blocked/clear kinds PATCH; the create-new path is unchanged. The fail-closed Finalize check-run step is untouched, so an incomplete run is never mistaken for an approval. Fork lane omits the /ai-review override footer since it has no human-override path. Adds TestForkGptVerdictVisibility running the real step bash with a stubbed gh; the step's temp writes are redirected under tmp_path so the test touches no shared host file. Co-authored-by: Kiro Crew <noreply@kirodotdev.github.io>
d34beea to
3511c94
Compare
|
Reply to First Principles Review (advisory CONCERNS on
Readiness is green; this reply answers the advisory verdict. Happy to adopt the "post nothing on incomplete" shape here if a maintainer prefers it over the placeholder. |
Port the merged #8342 incomplete-body guard from codex-review.yml to the eight remaining marker-keyed comment upsert sites: fork GPT, fork Opus, design, UX, first-principles, and their fork twins. A body carrying no current-head verdict stamp never overwrites a comment bearing the lane's verdict stamp; the posted verdict is preserved and one dated stale-verdict notice is prepended. A previous notice is recognized only at its deterministic position (whole-line begin marker at line 2) and dropped only when its end marker closes within the head window; an unterminated block is restored verbatim (pre-push GPT 5.6 + Opus 5 convergent BLOCKING: a position-blind window strip deletes prose lines 6-8 when a finding quotes the bare begin marker, the very #8292 data-loss class). Completed verdicts, skip notices, and override notes still replace wholesale, and a completed verdict whose lookup fails still falls through to CREATE. The guard is a byte-identical guarded_comment_upsert() bash function in every lane, pinned to one canonical body by test so it cannot drift; a checkout-based shared script would leave open fork PRs (base_sha predating the merge) unguarded and break the if:always() posting steps on checkout failure. Lookup captures paginated output first and selects off the captured value (no head -n1 SIGPIPE under pipefail, per #8350). Comment body files move from fixed /tmp paths to RUNNER_TEMP, matching the codex template. docs/ci/ci-and-reviews.md updated in the same commit. Closes #8344 Co-authored-by: Kiro Crew <kirocrew@users.noreply.github.com>
chenmingwei23
left a comment
There was a problem hiding this comment.
Approving: PR Readiness green (the repo's only required check), no failing lanes, MERGEABLE.
Port the merged #8342 incomplete-body guard from codex-review.yml to the eight remaining marker-keyed comment upsert sites: fork GPT, fork Opus, design, UX, first-principles, and their fork twins. A body carrying no current-head verdict stamp never overwrites a comment bearing the lane's verdict stamp; the posted verdict is preserved and one dated stale-verdict notice is prepended. A previous notice is recognized only at its deterministic position (whole-line begin marker at line 2) and dropped only when its end marker closes within the head window; an unterminated block is restored verbatim (pre-push GPT 5.6 + Opus 5 convergent BLOCKING: a position-blind window strip deletes prose lines 6-8 when a finding quotes the bare begin marker, the very #8292 data-loss class). Completed verdicts, skip notices, and override notes still replace wholesale, and a completed verdict whose lookup fails still falls through to CREATE. The guard is a byte-identical guarded_comment_upsert() bash function in every lane, pinned to one canonical body by test so it cannot drift; a checkout-based shared script would leave open fork PRs (base_sha predating the merge) unguarded and break the if:always() posting steps on checkout failure. Lookup captures paginated output first and selects off the captured value (no head -n1 SIGPIPE under pipefail, per #8350). Comment body files move from fixed /tmp paths to RUNNER_TEMP, matching the codex template. docs/ci/ci-and-reviews.md updated in the same commit. Closes #8344 Co-authored-by: Kiro Crew <kirocrew@users.noreply.github.com>
Summary
Follow-up to #8342. The same verdict-visibility bug (#8292) also affects the fork lane:
fork-gpt-review.yml'sPost/update summary commentstep PATCHed the bot's marker comment unconditionally, so an incomplete rerun overwrote a posted[GPT-REVIEWED]/[BLOCK-MERGE]verdict with a short "review incomplete" body. The prior verdict survives only in the GraphQLuserContentEditsedit history, which the REST comments API (what tooling andgh apireach for) does not expose. The finding becomes recoverable in principle and invisible in practice.This is a visibility bug, not a data-loss or merge-safety bug: the fail-closed check-run gate already refuses to pass on an incomplete verdict.
What changed
.github/workflows/fork-gpt-review.yml—Post/update summary commentstep only:github-actions[bot]marker comment already exists and this run'skindisincomplete, the step emits a diagnostic log line and makes no edit of any kind — no PATCH, no create — regardless of whether the existing body carries[GPT-REVIEWED].blocked/clearkinds PATCH the full new body; the create-newgh pr commentpath (no existing marker) is unchanged.head -n1cannot SIGPIPE the API call underset -o pipefailand be misread as a failure). A genuine lookup failure suppresses the update only for an incomplete run — where posting would risk a spurious marker over a live verdict. A completed (blocked/clear) run whose lookup fails falls through to create and publishes the verdict rather than staying silent, since a duplicate comment is more recoverable than an unposted verdict.github-actions[bot]author filter on the comment lookup is preserved, so a PR commenter cannot plant the marker and have this step PATCH their comment with the write-scoped token.The
Finalize check-run (fail closed)step (the fork analogue ofGate on findings) is untouched, so the merge gate stays fail-closed and decoupled from the comment — an incomplete run is never mistaken for an approval.docs/ci/ci-and-reviews.md— a paragraph documenting this lane's no-touch-on-incomplete behavior and why the overlapping-run race makes even a preserving PATCH unsafe.test/test_ai_review_workflows.py— newTestForkGptVerdictVisibilityrunning the real step bash with a stubbedgh; the step's hardcoded/tmp/fork-codex-comment.mdwrite is redirected undertmp_pathso the test touches no shared host file.Scope
This PR changes only the fork lane (
fork-gpt-review.yml). It does not touchcodex-review.yml— that same-repo lane's #8292 fix is sibling PR #8342, which owns it.Testing
python -m pytest test/test_ai_review_workflows.py -q=> all green (adjudication tests from main coexist with the new fork-lane class).test/test_workflow_secret_and_cache_scope.py=> all green (the workflow edit does not alter secret/cache scope).fork-gpt-review.ymlOK.Follow-up (not in this PR's scope)
The same unconditional-PATCH-on-incomplete construct still lives in seven other review lanes:
fork-opus-review.yml,design-review.yml,fork-design-review.yml,ux-review.yml,fork-ux-review.yml,first-principles-review.yml, andfork-first-principles-review.yml(pluscodex-review.yml, owned by sibling PR #8342;claude-review.ymlalready posts nothing on an incomplete run). Sweeping those sibling lanes is a tracked follow-up, kept out of this PR to hold its diff to the fork GPT lane and avoid re-arming the Fork workflow-change guard on unrelated workflow files.Pattern harvest
Rule candidate: when one lane of a duplicated CI mechanism is fixed, sweep every sibling lane that carries the same construct in the same or an immediately-tracked follow-up — a fix landing on only one of N copies leaves the invariant false everywhere else while reading as "fixed." Here the unconditional
PATCH .../issues/comments/$existingon anincompleterun is shared byfork-gpt-review.yml(this PR),codex-review.yml(sibling PR #8342), andfork-opus-review.yml(tracked follow-up). A cross-lane census at fix time (grep the sharedPost/updatecomment step and the unconditional PATCH) surfaces every copy up front rather than relying on a hand-filed note.Not in scope
The gate's fail-closed behaviour on an incomplete verdict is correct and is unchanged.
no linked issue: #8292 is closed by sibling PR #8342 (the same-repo lane); this fork-lane follow-up intentionally carries no closing keyword to avoid a duplicate/premature close.