LearnNewsExamplesServices
Frontmatter
id17163
titlePer-family review budget: charge the submitting family, refuse an unnameable one, and make the repair-minted re-entry checkable
stateClosed
labels
enhancementaiarchitecture
assignees[]
createdAtAug 15, 2026, 11:38 AM
updatedAtAug 15, 2026, 2:53 PM
githubUrlhttps://github.com/neomjs/neo/issues/17163
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 15, 2026, 2:53 PM

Per-family review budget: charge the submitting family, refuse an unnameable one, and make the repair-minted re-entry checkable

Closed Backlog/active-chunk-16 enhancementaiarchitecture
neo-opus-grace
neo-opus-grace commented on Aug 15, 2026, 11:38 AM

Problem

Narrow leaf split from #17141, which spans two surfaces with different shapes: the runtime admission in PullRequestService.managePrReview(), and the skill payload (pr-review templates, Round-2 asset, byte budget). This ticket is the runtime half only, so it can carry an honest close target while #17141 stays open for the rest.

The shipped budget counts CHANGES_REQUESTED reviews globally against a ceiling of two, and its escape hatch accepts any non-empty single line as a reason.

Both measure the cheap thing. The global count is wrong in two directions at once: one family's two rounds silence a family that has never seen the PR, and a single family can spend both rounds itself and still call it a budget. The free-text override accepts "Operator-declared release safety exception" — a sentence that asserts nothing checkable and is indistinguishable from the ordinary later discovery the exception exists to refuse.

The Architectural Reality

  • managePrReview() is the existing atomic admission point and stays so — no second review tracker.
  • Reviewer-family facts live in ai/services/graph/agentFamilyResolution.mjs (getCoreSwarmAgentFamilies()), which reads the identity graph. Budget classification consumes that authority rather than inferring from login prefixes.
  • The service could not name its own reviewer: fetchAndCacheViewerPermission asked GitHub who the viewer was, read the permission off the answer, and discarded the identity. A budget cannot charge a round without it.

The Fix

  1. Count per canonical reviewer family, ceiling of one, across heads and retractions.
  2. Fail closed on a submitter the identity graph cannot place — an unrostered login would otherwise spend nobody's budget and review without limit.
  3. Replace the free-text override with a repair-minted receipt naming old-head, new-head, prior-fact, repair-coordinate, where old-head is verified against the PR's own review population. That clause is the one a mistaken or invented receipt cannot satisfy by being better written.
  4. One re-entry per family, terminal — otherwise the exception becomes the budget.

Acceptance Criteria

  • One ordinary CHANGES_REQUESTED from a family spends that family's budget across later heads and retractions; another active family retains its independent round.
  • A submitter the identity graph cannot classify is refused, not granted an unbounded round.
  • The repair-minted receipt names four facts, and old-head matches a head some prior review was actually submitted against; new-head must be the head under review; identical heads refuse.
  • A second repair-minted re-entry from the same family refuses.
  • The reviewer identity is the authenticated viewer login, cached beside the permission it was fetched with, never parsed from review prose.

Out of Scope

Everything in #17141 that touches .agents/skills/pr-review/** — the Round-2 disposition asset, the follow-up-template replacement, the byte-budget net-decrease, the A+FU counterfactual, and the cohort receipt. Those land separately; this ticket does not weaken the cross-family approval requirement or the human-only merge gate.

Evidence class

Source-verified at dev: PullRequestService.mjs budget validator and reviewBudgetOrdinaryRcLimit, RepositoryService.fetchAndCacheViewerPermission, agentFamilyResolution.mjs. Intake for the parent #17141 recorded at https://github.com/neomjs/neo/issues/17141#issuecomment-5301088070 and is inherited here — this is a scope split, not a new premise.

Related: #17141

Authored by Grace (Claude Opus 5, Claude Code). Session b17338dd-b474-494f-b08c-683044de2ddb.

tobiu referenced in commit b027c0c - "feat(ai): charge the review budget per reviewer family, and make the exception checkable (#17163) (#17164) on Aug 15, 2026, 2:53 PM
tobiu closed this issue on Aug 15, 2026, 2:53 PM