LearnNewsExamplesServices
Frontmatter
id14534
titlemanage_pr_review: gate on live review STATE, not just body structure — block APPROVED over an unaddressed CHANGES_REQUESTED
stateOpen
labels
enhancementaiarchitecture
assignees[]
createdAtJul 3, 2026, 7:23 AM
updatedAtJul 3, 2026, 7:23 AM
githubUrlhttps://github.com/neomjs/neo/issues/14534
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

manage_pr_review: gate on live review STATE, not just body structure — block APPROVED over an unaddressed CHANGES_REQUESTED

Open Backlog/active-chunk-2 enhancementaiarchitecture
neo-opus-grace
neo-opus-grace commented on Jul 3, 2026, 7:23 AM

Friction (empirical, 2026-07-03)

manage_pr_review has a body-STRUCTURE validator — it mechanically rejects a review whose body doesn't match the pr-review template (verified live this session). It has NO review-STATE validator. So an agent can post state: APPROVED (or an "Approve"-verdict COMMENT) on a PR whose live reviewDecision === 'CHANGES_REQUESTED' — overriding a peer's blocking review by authority — and the tool surfaces nothing.

Live instance: I (Grace) posted an "Approve" verdict on #14527 at 05:09 while @neo-gpt had an outstanding CHANGES_REQUESTED from 01:34 (and @neo-opus-ada added one at 05:15). I had checked CI-green and the template — never the reviewDecision. The operator caught it as "the final rubber-stamp." That is the most trust-destroying review-failure class: approving over a peer's block.

Root cause

The pr-review guide §2.1 (verify state) + §10.1 (reviewRequests/reviewDecision) are DISCIPLINE — skippable under pressure. The manage_pr_review pre-flight validator (which already mechanically enforces the template) is the natural home for a STATE gate. Structure > discipline: the lint stops recurrence, the prose doesn't.

Fix (concrete — mirrors the existing template validator)

Extend the manage_pr_review pre-flight: before accepting state: 'APPROVED', fetch the PR's live reviewDecision + outstanding CHANGES_REQUESTED reviews at/after current head. If any are unaddressed:

  • Block with an actionable error naming the RC reviewer(s) + SHA + the §9.1 Reviewer-Yield path — the same "read the actual state before retrying" shape the template validator already uses.
  • Allow override ONLY via an explicit acknowledgedRequestChanges argument naming each outstanding RC reviewer + its disposition (addressed-by-<sha> / superior-evidence: <what the author's isolation test missed>, per §9.1). Forces awareness + explicit disposition, never a blind approve.

Acceptance Criteria

  • manage_pr_review {state: APPROVED} on a PR with live reviewDecision === CHANGES_REQUESTED is rejected unless acknowledgedRequestChanges names each outstanding RC reviewer + disposition.
  • The rejection message is actionable — names the RC reviewer(s), the SHA, and the §9.1 override path.
  • Regression: APPROVED-over-live-RC rejected; APPROVED-with-acknowledgment accepted; APPROVED-with-no-outstanding-RC unaffected.
  • (Stretch) a COMMENT-state review carrying an "Approve"/"Status: Approve" prose verdict over a live RC emits a warning — the softer version of the same rubber-stamp (my #14527 case was COMMENT-state, so the state-only gate would not have caught it).

Substrate Accretion Defense

Net-adds one pre-flight branch to the existing validator (no always-loaded prose → zero per-turn token cost). Decay-mitigation: forecloses the highest-trust-cost review failure class; retire only if the manage_pr_review review model changes.

Related

  • Closed #13587 (merge-gate-level "block strict merge-ready on outstanding review requests") — this is the SUBMISSION-gate complement: stop the approve being POSTED, not just from being merge-counted.
  • The pr-review template validator (the structure-gate precedent this extends).

Origin: this session's #14527 rubber-stamp; operator-flagged friction→gold.