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:
- Split into focused PRs — the default rig path. Each PR has one intent and reviews independently.
large-pr-oklabel — 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.
| Path | When to use |
|---|---|
| Two-PR split | Default for any feature slice >500 LOC. Required when the PR touches both src/ConductorE.Core/** AND src/ConductorE.Api/**. |
large-pr-ok | Reserved 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. |
A/B evidence (2026-05-18)
Section titled “A/B evidence (2026-05-18)”The exact same code — a workflow-class-cr-stalled detector for rc#1104 — was shipped twice today in two different shapes:
| Attempt | Size | Label | Review-E outcome |
|---|---|---|---|
| rc#1135 (closed) | 554-LOC single PR | large-pr-ok | One-shot APPROVE, zero code-level feedback |
| rc#1136 + rc#1141 (split) | 320 + 184 LOC | none | 3 real bugs caught in 2 review rounds |
The 3 bugs caught in the split path:
- Test files placed at
tests/ConductorE.Core.Tests/root instead of the conventiontests/ConductorE.Core.Tests/SelfImprovement/Policies/sibling-dir. - Missing
Closes #Ndirective in PR A’s body (the multi-PR-split sub-issue convention). - Logic gap: the watcher’s
LivePrStates = ["in_progress", "in_review"]array excluded"changes_requested"— but that’s exactly the state Review-E’sREVIEW_DISPUTEDevent 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.Sub-issue convention for two-PR splits
Section titled “Sub-issue convention for two-PR splits”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.
Layers of enforcement
Section titled “Layers of enforcement”The rig now has three complementary layers to keep this rule load-bearing:
- 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. - Rig-side size-gate enhancement (rar#492) — when a blocked PR touches both
Core/andApi/, the size-gate message includes the specific PR A / PR B split shape. Thelarge-pr-okmention now requires a one-line justification. - This research doc — durable artifact citable in BRAIN.md
## Conventions, surfaceable viagh issue create/ Discord / PR comments. The empirical A/B data lives here so future agents can ground the rule in evidence.
Out of scope
Section titled “Out of scope”- Hard-blocking
large-pr-okwithout 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.
See also
Section titled “See also”- rc#1137 — design issue with the original A/B evidence comment
- rar#492 — rig-side enforcement: structural-split suggestion in size-gate message
- Orchestrator-local memory entry
feedback_pr_split_for_feature_work.mdlives outside this repo (in the Claude-3 workspace) and is not linkable from the rendered site. Future agents looking for the rule should find this doc via the BRAIN.md## Conventionsbullet first. - The detector/actuator pair pattern, formalized in the same 2026-05-18 session, is what made the policy + adapter split natural in the first place — see the rc#1106 / rc#1125 / rc#1130 / rc#1141 PR series for examples.