Skip to content

PR shape — when to split, when `large-pr-ok` applies (A/B-validated 2026-05-18)

PR shape — when to split, when large-pr-ok applies

Section titled “PR shape — when to split, when large-pr-ok applies”

The rig’s size gate (src/pr-size-check.js in rig-agent-runtime) blocks PRs that exceed 20 changed files OR 500 added+deleted lines. The gate offers two paths to unblock:

  1. Split into focused PRs — the default rig path. Each PR has one intent and reviews independently.
  2. large-pr-ok label — bypasses the gate. Originally intended for migrations, codemods, dependency bumps, and generated code.

This doc captures today’s A/B evidence showing the label has been over-used for feature work that decomposes cleanly, and documents the decision tree the orchestrator should follow.

PathWhen to use
Two-PR splitDefault for any feature slice >500 LOC. Required when the PR touches both src/ConductorE.Core/** AND src/ConductorE.Api/**.
large-pr-okReserved for genuinely non-decomposable changes: migrations, codemods, dependency bumps, generated code, and the rare single-concern PR that crosses the line. Must include a one-line justification in the PR body.

The exact same code — a workflow-class-cr-stalled detector for rc#1104 — was shipped twice today in two different shapes:

AttemptSizeLabelReview-E outcome
rc#1135 (closed)554-LOC single PRlarge-pr-okOne-shot APPROVE, zero code-level feedback
rc#1136 + rc#1141 (split)320 + 184 LOCnone3 real bugs caught in 2 review rounds

The 3 bugs caught in the split path:

  1. Test files placed at tests/ConductorE.Core.Tests/ root instead of the convention tests/ConductorE.Core.Tests/SelfImprovement/Policies/ sibling-dir.
  2. Missing Closes #N directive in PR A’s body (the multi-PR-split sub-issue convention).
  3. Logic gap: the watcher’s LivePrStates = ["in_progress", "in_review"] array excluded "changes_requested" — but that’s exactly the state Review-E’s REVIEW_DISPUTED event puts an issue in. The watcher would have silently missed the canonical rc#272 incident it was built to catch.

All three would have shipped silently under the large-pr-ok shortcut. The label cashes in Review-E’s per-PR scrutiny: a smaller diff gets deeper attention because more context fits in the reviewer’s window.

Decision tree (run BEFORE applying any large-pr-ok label)

Section titled “Decision tree (run BEFORE applying any large-pr-ok label)”
1. Does the PR touch BOTH `src/ConductorE.Core/**` AND `src/ConductorE.Api/**`?
→ YES → SPLIT. (Clean Architecture says policy + adapter are
separable. Today's structural-split nudge in
rar#492 will surface the specific PR A / PR B
shape on the size-gate message.)
2. Does the PR include a new domain/policy type alongside its caller(s)?
→ YES → SPLIT (same shape).
3. Is the PR mechanically generated?
- codemod
- dependency bump (Dependabot)
- mass-rename refactor with no behavior change
- generated code (e.g. nswag, protoc, openapi)
→ YES → `large-pr-ok` OK, include justification in body.
4. Is the PR genuinely non-decomposable?
- single-file fix
- single-method refactor
- webhook handler edit with co-located test
- migration script that must land atomically
→ YES (and >500 LOC, unusual) → `large-pr-ok` OK, include justification.
5. Otherwise → SPLIT.

Every PR needs Closes #N per the rig-wide rule. For a multi-PR split where the parent issue must stay open until the final PR:

  • File a per-slice sub-issue scoped to PR A. Title pattern: track: ship <ThingName> Core slice (rc#<parent> PR A).
  • PR A body uses Closes #<sub> + Refs #<parent>.
  • PR B body uses Closes #<parent> (closes the work).
  • The sub-issue auto-closes when PR A merges; the parent closes when PR B merges.

The PR-body gate blocks PRs without Closes. The large-pr-ok shortcut bypasses both the body check AND the deep review — that was today’s failure mode.

The rig now has three complementary layers to keep this rule load-bearing:

  1. Memory entry (claude-3/memory/feedback_pr_split_for_feature_work.md) — HARD RULE loaded at every Claude-3 session start. The decision tree above lives there verbatim.
  2. Rig-side size-gate enhancement (rar#492) — when a blocked PR touches both Core/ and Api/, the size-gate message includes the specific PR A / PR B split shape. The large-pr-ok mention now requires a one-line justification.
  3. This research doc — durable artifact citable in BRAIN.md ## Conventions, surfaceable via gh issue create / Discord / PR comments. The empirical A/B data lives here so future agents can ground the rule in evidence.
  • Hard-blocking large-pr-ok without justification — the rar#492 nudge is soft. A follow-up could enforce by parsing the PR body for the justification line and re-blocking if absent.
  • Auto-creating the two split PRs from a single source branch — too magical. The orchestrator/dev-e should do this manually.
  • Tuning the 500-LOC threshold — separate question. The threshold has been stable; the gap was in HOW the gate is bypassed, not the threshold value.