Decisions

ADR-115: Principle-aware PR reviewers

The 2026-04-26 PR cycle produced two `--admin` overrides on the same wrong-premise pattern: Marcus blocked `devtools#126` citing the "docs/shared/ propagation rule" against a repo-root README, where the rule plainly…

#115

ADR-115: Principle-aware PR reviewers

Status: Proposed Date: 2026-04-26 Related: ADR-083 (defense-in-depth-rule-promotion), ADR-090 (ai-as-scaffold-pipelines-as-runtime), ADR-104 (dry-run-on-history-adaptive-gate), ADR-110 (observe-and-file-dominant-pattern), ADR-112 (principles tier — sibling PR), ADR-117 (module guidelines — sibling)

Context

The 2026-04-26 PR cycle produced two --admin overrides on the same wrong-premise pattern: Marcus blocked devtools#126 citing the "docs/shared/ propagation rule" against a repo-root README, where the rule plainly does not apply (the README is authoritative source, not generated artifact); Priya blocked dark-factory#465 citing input-validation principles against ansible -e on an operator-controlled controller, where the trust boundary is shell access, not network. In both cases the operator authorised the --admin with an audit reason naming why the reviewer was wrong, per the feedback_admin_override_with_audit.md memory rule.

The root cause is structural: each persona reviews from training priors, re-deriving judgement per PR with no shared context about the project's structural decisions. The same wrong-premise pattern recurs whenever a similar diff shape lands, because the personas have no memory and the project has no canonical priors for them to consult. This is the recurring --admin loop the operator named in warp #341 ("PR reviews that can consistently and stay on course with an idea").

The principles tier (warp #339, ADR-112 sibling) creates the data layer. This ADR is the operational counterpart: reviewers consume that data layer before judging.

Decision

Each persona's CLAUDE.md prompt (gyrum-priya, gyrum-marcus, gyrum-lin, gyrum-dana) opens with a load-bearing "Principles applicable to this PR" section that fetches relevant principles from the warp API before reviewing:

curl -sH "Authorization: Bearer $WARP_TOKEN" \
  "$WARP_URL/api/v1/principles?applies_to_pattern=<file-path-glob>"

The personas filter by per-persona principles_scope (Priya → security/scope-creep/trust-boundaries; Marcus → architecture/abstractions/layering; Lin → testing/idioms/contracts; Dana → docs/UX) plus cross-area principles ("structural beats prose") visible to all. Every blocking objection MUST cite a principle id with a one-line summary; concerns without an applicable principle surface as ADVISORY, not BLOCKING. If the reviewer believes a structural principle should exist but cannot find one, they file a kind: principle, status: proposed ticket and surface the concern as advisory this round.

Anti-examples — the cases where a principle looks like it applies but does not — are first-class on the principle record. Today's two false positives become the seed corpus: the docs/shared/ rule gains an anti-example for repo-root READMEs; the input-validation principle gains one for operator-controlled ansible -e. When a future --admin override fires with audit reason naming "false positive on premise X", the system auto-files a principle anti-example proposal so the next PR with the same diff shape does not hit the same wall.

The operator override path is unchanged: --admin remains available for the case where the operator disagrees with reviewer + principles together. Override frequency is telemetry — the r7 sub-ticket alerts when overrides cluster on a missing principle, signalling a gap to fill rather than a reviewer to scold.

Consequences

What becomes easier.

  • Reviewers are consistent across PRs of the same shape. The "what does Marcus think today" variance collapses to "what do these principles say about this change".
  • Recurring --admin overrides become signal, not noise. A cluster of overrides on the same audit reason means a missing or wrong principle, and the anti-example auto-file path closes the loop without operator effort beyond writing the audit reason once.
  • Reviewer reasoning becomes auditable. Every block cites a principle id; later we can ask "which principles are most cited on blocks?" and "which are most overridden?" and tune accordingly.
  • The persona-PAT rubber-stamp risk drops. Today a persona with no priors and a vague prompt can produce reviewer-themed prose; with cited-principle-or-advisory-only as a hard rule, the prose has to land on a real anchor.

What becomes harder.

  • Each persona pays a +2-3s principle fetch per review. Acceptable: review wall-clock is dominated by the model call already, and the principles endpoint is small and indexed.
  • Stale or wrong principles cascade into wrong reviews. Mitigation: the principle-revision workflow (warp #339 r7) gives a clear path to amend, and the status: superseded field with superseded_by keeps reviewers off retired rules.
  • Over-citation risk: reviewers might cite principles for everything, drowning the real concerns. Mitigation: the applies_to pattern on each principle is checked against the diff before citation is allowed; principles without a matching applies_to glob cannot be cited as blocking.
  • Bypass-via-blindness is replaced by bypass-via-worship as the failure mode to watch. Mitigation: ADVISORY surface for non-principle concerns (genuine reviewer judgement still gets a voice) and the operator override path always available.

What we sign up to maintain.

  • The GET /api/v1/principles?applies_to_pattern= endpoint and its index for sub-second lookups.
  • Per-persona principles_scope defaults and the cross-area "visible to all" tag set.
  • The auto-file-on-override pipeline that turns audit reasons into principle anti-example proposals.
  • The override-rate telemetry per persona, with the alert threshold on weekly clustering.

Alternatives considered

  • Embed principles in the persona prompt at build time. Rejected: every principle change requires a persona-prompt redeploy; the data layer (warp #339) exists exactly so the rules can evolve without coordinated releases.
  • Single shared "review checklist" instead of per-persona principles_scope. Rejected: collapses the four-persona signal into one. The whole point of the four personas is that security, architecture, testing, and docs are different lenses; the principle set has the same shape.
  • Make principle citation advisory, not required, for blocks. Rejected: that is today, with extra ceremony. The bug being fixed is "blocks without principle backing"; making the backing optional re-introduces the bug.
  • Skip personas, run a single principle-checker bot. Rejected: principles cover structural concerns, but reviewers also catch concerns no principle predicts (subtle bugs, naming, test gaps). The personas remain; the principles constrain their blocking criteria, not their advisory voice.

Supersedes: none Superseded by: none yet

Amendment — 2026-04-29 (warp#698 / ADR-124)

Marcus's principles_scope (architecture/abstractions/layering) gains the block-composition principle from ADR-124. PRs introducing a new monolithic step under cmd/server/pipeline/**, pkg/pipeline/**, or pkg/block/** — or any new directory importing gyrum-pipelines/pkg/block — must be blocked with a citation to ADR-124. Concerns raised against legacy pipelines pre-dating ADR-124 surface as ADVISORY (the graduation campaign in warp#707 owns the migration), not BLOCKING.