What happened
Today's merge train validated nine PRs and merged five. Three of the four held back failed the same way, and it is worth naming once rather than in three separate PR threads that three authors read separately.
The shape: a /review finding from 2026-09-14 sat unanswered, and the PR read as Ready again anyway.
Both ejected PRs were re-validated from scratch at full lane depth before anyone noticed the findings were the same ones. That is roughly an hour of agent time per PR, spent twice, to rediscover what was already written down.
Why it happens
reviewDecision only moves when a reviewer submits a new review. A /review posted as a comment never touches it, so a PR carrying unaddressed findings is byte-identical, in every field the queue view reads, to one nobody has looked at. /open-pull-requests showed all three under ✅ Ready this morning, correctly by its own rules.
The merge train catches it, but only by paying full validation cost first.
Suggested change
Cheapest fix that would have worked: when /review or /validate-pr returns NEEDS-FIX or EJECT on a PR, the finding is posted and the PR gets a label — status-needs-fix or similar — that /open-pull-requests sorts out of ✅ Ready and /merge-train Phase 0 treats like a hold. The author's next push removes it.
That makes "a finding is outstanding" a queue-visible fact rather than something a reader has to reconstruct by reading comment threads in date order against the branch head timestamp.
Alternative, if a label is too heavy: have Phase 0's hold grep also compare the newest /review comment's timestamp against the branch head date, which is the derivation the train already does by hand for CHANGES_REQUESTED in Step 3.0 (responded). Extending that to comment-shaped reviews is a small change to an existing rule.
Acceptance criteria
What happened
Today's merge train validated nine PRs and merged five. Three of the four held back failed the same way, and it is worth naming once rather than in three separate PR threads that three authors read separately.
The shape: a
/reviewfinding from 2026-09-14 sat unanswered, and the PR read as Ready again anyway.stt_capability_service.py, raised 2026-09-14 16:46Z. Branch head predates the review. Unchanged at the next train.Both ejected PRs were re-validated from scratch at full lane depth before anyone noticed the findings were the same ones. That is roughly an hour of agent time per PR, spent twice, to rediscover what was already written down.
Why it happens
reviewDecisiononly moves when a reviewer submits a new review. A/reviewposted as a comment never touches it, so a PR carrying unaddressed findings is byte-identical, in every field the queue view reads, to one nobody has looked at./open-pull-requestsshowed all three under ✅ Ready this morning, correctly by its own rules.The merge train catches it, but only by paying full validation cost first.
Suggested change
Cheapest fix that would have worked: when
/reviewor/validate-prreturns NEEDS-FIX or EJECT on a PR, the finding is posted and the PR gets a label —status-needs-fixor similar — that/open-pull-requestssorts out of ✅ Ready and/merge-trainPhase 0 treats like a hold. The author's next push removes it.That makes "a finding is outstanding" a queue-visible fact rather than something a reader has to reconstruct by reading comment threads in date order against the branch head timestamp.
Alternative, if a label is too heavy: have Phase 0's hold grep also compare the newest
/reviewcomment's timestamp against the branch head date, which is the derivation the train already does by hand forCHANGES_REQUESTEDin Step 3.0 (responded). Extending that to comment-shaped reviews is a small change to an existing rule.Acceptance criteria
/open-pull-requests./merge-trainPhase 0 excludes it without spending Phase 2 validation on it.