Context
Operator @tobiu's framing 2026-05-10 in response to PR #11114 (Issue Chunk Migration, #11113):
"the idea for an epic was e.g. keeping code changes and data migration output inside separate commits. makes reviews easier. might be a friction gold topic."
PR #11114 surfaced this empirically: it bundled (a) 5 wire-format consumer code updates (~15 LOC of recursive-readdir mirror logic) AND (b) ~4190 ticket-file moves from flat → chunked nested structure in the SAME PR. Cycle-1 review struggled to isolate the code-change correctness from the data-migration noise. Even after cycle-1 cleanup (which removed accidental chore-sync + stray drafts + discussion drift — separate friction class), cycle-2's clean diff still has 5 code files mixed with 95+ data-rename files.
Same shape applies retroactively to #11113 epic design: the substrate-correct sub-ticket structure would have split this into:
- Code sub: recursive-readdir mirror across 5 wire-format consumers (small, reviewable in isolation)
- Data sub: 4190-ticket flat→chunked migration (mechanical, separate review)
The Problem
Mixed-scope PRs (code-change + data-migration in one diff) create review friction:
- Signal-to-noise asymmetry: 15 LOC of code logic drowned in 4190 file renames. Reviewer scrolls through data movement to verify code correctness.
- Squash-merge git history pollution: github's squash collapses ALL commits → final dev commit body contains the full mixed-scope diff. Future
git blame / archeology mixes "what changed in the code" with "what data moved."
- Revertability cost: if the code change has a regression, reverting the squash-commit also reverts the data migration (or requires manual surgery to separate). Asymmetric blast-radius for the rollback decision.
- Review attention dilution: even attentive reviewers fatigue scrolling through mechanical data renames. Code-change defects get less scrutiny per LOC.
PR #11114 made this concrete enough that operator surfaced it as a substrate-evolution candidate.
The Fix
Add a Stage 3.5 (or augment Stage 3 sub-structure-coherence check) in epic-review-workflow.md that fires for epics involving both code changes AND data-migration:
Concrete patch shape — .agents/skills/epic-review/references/epic-review-workflow.md (Stage 3 region):
+### Stage 3.5: Code-vs-Data-Migration Sub-Split Audit
+
+Trigger: epic body or sub-ticket prescriptions describe BOTH (a) code changes
+(updates to `.mjs` / `.ts` / `.js` / build-script logic) AND (b) data migration
+(file renames / mass content relocation / large structured data moves).
+
+Audit: verify sub-ticket structure splits these into SEPARATE sub-tickets:
+- Code sub-ticket(s) — small, reviewable in isolation; enables the new shape
+- Data-migration sub-ticket(s) — mechanical, separate review pass; consumes
+ the new shape after code lands
+
+Sequencing rule: code sub MUST land before data-migration sub (so the data
+lands in a substrate that already supports it). For dependency-cycle epics
+where this isn't possible, document the rationale explicitly.
+
+If the epic prescribes BOTH concerns in the SAME sub-ticket: flag as
+Required Action; recommend split. Empirical anchor: epic #11113 / PR #11114
+— mixed-scope diff (15 LOC code + 4190 file renames) creates review-friction
+that operator surfaced as friction-gold-candidate.
Acceptance Criteria
Out of Scope
- Pre-PR lint detecting mixed-scope diff at commit-time — too mechanical; hard to detect "data migration" semantically without false positives. Stage 3.5 at epic-review time is the right tier.
- Post-merge revert primitive that lets reviewers undo just data-migration without touching code — scope-creep; doesn't fix the review-friction problem at source.
- Other PR-shape disciplines (e.g., separate commits within same PR) — squash-merge collapses these anyway; the right fix is at sub-ticket / PR-scope tier, not commit tier.
- Retroactive split of PR #11114 — too late + low value; the cleanup cycle-1 already addressed the contamination. Future epics benefit from Stage 3.5.
Avoided Traps / Gold Standards Rejected
Decision Matrix
Stage 3.5 in epic-review-workflow.md (Selected): Fires at the right tier (epic design time, BEFORE sub-tickets get filed against mixed-scope shape). Lowest implementation cost. Aligns with the substrate-evolution family of recent skill-payload calibrations (#11102 Verdict-Authority, #11105 template-adherence, Discussion #11112 epic-level V-B-A).
Author-side discipline only (no skill-payload codification): Rejected. Per #11114 empirical anchor: discipline alone fails. PR #11114 author clearly knew about the data-migration scope but bundled with code anyway. Skill-payload codification is the gate that makes the discipline persist across agent-context-windows.
Pre-PR commit-time lint: Rejected. Detecting "data-migration vs code change" semantically is hard (false-positive risk on ANY large refactor that touches many files). Stage 3.5 at epic-design time is upstream of this and avoids the false-positive class.
Post-PR commit-shape requirement (separate commits within same PR): Rejected. Squash-merge collapses commits into one dev commit. Per-commit separation provides cycle-N review value but not post-merge git-history value. The right tier is sub-ticket / PR scope, not commit scope.
Per-PR title-prefix convention (e.g., feat(code): vs feat(data):): Rejected as cosmetic. Conventional Commits scope already exists; adding a code-vs-data sub-class doesn't fix the underlying review-friction at the diff level.
- Trap: Treating "small code change is the entire fix" when the same epic has data-migration scope. Rejection: PR #11114 demonstrates the failure mode — small code change buried in data-rename noise. Stage 3.5 catches this at epic-review time.
- Trap: Letting operator-pushback be the substrate gate. Rejection: Operator surfaced this on PR #11114; the substrate-correct fix is to codify it so future epics don't need operator-policing.
Related
- PR #11114 (#11113) — canonical empirical anchor; mixed-scope diff (15 LOC code + 4190 file renames) made cycle-1 review high-friction
- Operator's framing 2026-05-10 (current session, A2A-only —
[paraphrase] for peer corroboration)
epic-review skill payload (the substrate this ticket evolves)
- AGENTS.md §13.2 friction → gold core value (this ticket IS the friction → gold conversion artifact)
- #11102 (sister substrate-evolution: epic-resolution Verdict-Authority calibration)
- #11105 (sister substrate-evolution: author-side template-adherence)
- Discussion #11112 (sister substrate-evolution: epic-level V-B-A enforcement; this ticket plus #11102 + #11105 form a coherent skill-payload-calibration cluster)
- Distinct from: chore-sync contamination pattern (3rd time across PR #11106 / #11109 / #11114) — that's a separate friction class about local-branch hygiene; THIS ticket is about epic design shape
Origin Session ID: c2912891-b459-4a03-b2af-154d5e264df1
Retrieval Hint: "code-vs-data-migration sub-split", "epic-review Stage 3.5", "PR #11114 friction-gold anchor"
Context
Operator @tobiu's framing 2026-05-10 in response to PR #11114 (Issue Chunk Migration, #11113):
PR #11114 surfaced this empirically: it bundled (a) 5 wire-format consumer code updates (~15 LOC of recursive-readdir mirror logic) AND (b) ~4190 ticket-file moves from flat → chunked nested structure in the SAME PR. Cycle-1 review struggled to isolate the code-change correctness from the data-migration noise. Even after cycle-1 cleanup (which removed accidental chore-sync + stray drafts + discussion drift — separate friction class), cycle-2's clean diff still has 5 code files mixed with 95+ data-rename files.
Same shape applies retroactively to #11113 epic design: the substrate-correct sub-ticket structure would have split this into:
The Problem
Mixed-scope PRs (code-change + data-migration in one diff) create review friction:
git blame/ archeology mixes "what changed in the code" with "what data moved."PR #11114 made this concrete enough that operator surfaced it as a substrate-evolution candidate.
The Fix
Add a Stage 3.5 (or augment Stage 3 sub-structure-coherence check) in
epic-review-workflow.mdthat fires for epics involving both code changes AND data-migration:Concrete patch shape —
.agents/skills/epic-review/references/epic-review-workflow.md(Stage 3 region):+### Stage 3.5: Code-vs-Data-Migration Sub-Split Audit + +Trigger: epic body or sub-ticket prescriptions describe BOTH (a) code changes +(updates to `.mjs` / `.ts` / `.js` / build-script logic) AND (b) data migration +(file renames / mass content relocation / large structured data moves). + +Audit: verify sub-ticket structure splits these into SEPARATE sub-tickets: +- Code sub-ticket(s) — small, reviewable in isolation; enables the new shape +- Data-migration sub-ticket(s) — mechanical, separate review pass; consumes + the new shape after code lands + +Sequencing rule: code sub MUST land before data-migration sub (so the data +lands in a substrate that already supports it). For dependency-cycle epics +where this isn't possible, document the rationale explicitly. + +If the epic prescribes BOTH concerns in the SAME sub-ticket: flag as +Required Action; recommend split. Empirical anchor: epic #11113 / PR #11114 +— mixed-scope diff (15 LOC code + 4190 file renames) creates review-friction +that operator surfaced as friction-gold-candidate.Acceptance Criteria
epic-review-workflow.mdpatched with Stage 3.5 (or augmented Stage 3) per the diff aboveticket-create-workflow.mdStage 3 (Prescription Layer) gets a parallel reminder for epic-shape tickets specifically[paraphrase]since it's session-A2A not publicly visibleOut of Scope
Avoided Traps / Gold Standards Rejected
Decision Matrix
Stage 3.5 in
epic-review-workflow.md(Selected): Fires at the right tier (epic design time, BEFORE sub-tickets get filed against mixed-scope shape). Lowest implementation cost. Aligns with the substrate-evolution family of recent skill-payload calibrations (#11102 Verdict-Authority, #11105 template-adherence, Discussion #11112 epic-level V-B-A).Author-side discipline only (no skill-payload codification): Rejected. Per #11114 empirical anchor: discipline alone fails. PR #11114 author clearly knew about the data-migration scope but bundled with code anyway. Skill-payload codification is the gate that makes the discipline persist across agent-context-windows.
Pre-PR commit-time lint: Rejected. Detecting "data-migration vs code change" semantically is hard (false-positive risk on ANY large refactor that touches many files). Stage 3.5 at epic-design time is upstream of this and avoids the false-positive class.
Post-PR commit-shape requirement (separate commits within same PR): Rejected. Squash-merge collapses commits into one dev commit. Per-commit separation provides cycle-N review value but not post-merge git-history value. The right tier is sub-ticket / PR scope, not commit scope.
Per-PR title-prefix convention (e.g.,
feat(code):vsfeat(data):): Rejected as cosmetic. Conventional Commits scope already exists; adding a code-vs-data sub-class doesn't fix the underlying review-friction at the diff level.Related
[paraphrase]for peer corroboration)epic-reviewskill payload (the substrate this ticket evolves)Origin Session ID: c2912891-b459-4a03-b2af-154d5e264df1 Retrieval Hint: "code-vs-data-migration sub-split", "epic-review Stage 3.5", "PR #11114 friction-gold anchor"