diff --git a/.sisyphus/plans/post-epic-deferrals.md b/.sisyphus/plans/post-epic-deferrals.md
new file mode 100644
index 0000000..240b880
--- /dev/null
+++ b/.sisyphus/plans/post-epic-deferrals.md
@@ -0,0 +1,665 @@
+# Post-Epic Deferrals — Design + Execution Plan
+
+> **Scope**: Four independent features previously deferred during or after
+> the rule-variants epic. Each ships as its own mini-epic with verification
+> gate. The features are sequenced so the SMALLEST / HIGHEST-VALUE item
+> lands first.
+>
+> **Prior art**: `.sisyphus/plans/rule-variants-v2.md` for execution
+> discipline, commit conventions, and the Final Verification Wave
+> pattern. This plan mirrors that structure.
+
+---
+
+## TL;DR
+
+> Ship **4 deferred items** in sequence:
+>
+> 1. **Multiplayer color choice** — Lobby UI + protocol field + server
+> assignment logic. High user value, isolated surface, ~1 session.
+> 2. **Extinction-chess UI target cycling** — chip UI inside RulesDrawer
+> to pick which piece type is the extinction target, without opening
+> the preset-state API. ~½ session.
+> 3. **Berolina en-passant** — rule decision + implementation for pawn
+> en-passant under `berolina-pawns` and `berolina-pawns-2`. Pure
+> `overridePieceMoves` extension. ~½ session.
+> 4. **Tier 3 royalty-transfer** — new preset family for "crown" moves
+> that relocate royalty mid-game. Requires new engine surface
+> (likely a lifecycle hook) + UI for target selection. Biggest
+> risk + longest. ~1-2 sessions.
+>
+> **Recommendation**: work items 1-3 sequentially (each is small and
+> independent). Defer item 4 into its own follow-up session after 1-3
+> ship. The plan below fully specifies all four so any of them can be
+> started independently.
+
+---
+
+## Feature 1 — Multiplayer Color Choice
+
+### Current state (audited)
+
+- **Server hardcodes** creator=white, joiner=black in
+ `packages/server/src/rooms.ts:212,255`.
+- **Protocol** has NO color preference field on `room.create` /
+ `room.join`; the server only RETURNS the assigned color in
+ `room.created` / `room.joined`.
+- **Lobby UI** has no color selector — creating a room gives you white,
+ joining gives you black.
+- **Tests** (`packages/chess/e2e/multiplayer.spec.ts`) assume the
+ hardcoded order: pageA=white, pageB=black.
+- No rematch / swap mechanism exists.
+
+### Design
+
+Add a 3-option host-side preference: `white | black | random`. The
+creator picks; the server resolves `random` at room-creation time; the
+joiner gets whatever color the creator didn't take.
+
+**Explicitly OUT of scope for this feature**:
+- Joiner-side preference (complex: what if both want white?). Defer.
+- Rematch-with-swapped-colors. Defer — separate feature.
+- Color re-selection after room creation. Defer — needs server-side
+ swap handling for an existing WS session.
+
+### Protocol changes
+
+1. **`packages/server/src/protocol.ts`**:
+ - Add `preferredColor: z.enum(["white", "black", "random"]).optional()`
+ to `RoomCreatePayloadSchema`. Omitting it = `"white"` (current
+ behaviour, backward compat).
+ - No change to `RoomJoinPayloadSchema` (joiner takes the remaining
+ color).
+ - No change to response payloads — they already return the resolved
+ color in `room.created` / `room.joined`.
+
+2. **`packages/chess/src/net/types.ts`**: mirror the new field on the
+ client `RoomCreatePayload` interface.
+
+3. **Zod v3↔v4 parity test**: add to
+ `packages/server/src/custom-modifier-wire-parity.test.ts` sibling
+ (or create `lobby-wire-parity.test.ts`) — ensures the new field
+ round-trips across the server/client schema boundary. Pattern:
+ T3's Q4.2 parity test.
+
+### Server changes
+
+**`packages/server/src/rooms.ts`**:
+
+```typescript
+createRoom(
+ rulesetIds: string[] = [],
+ layout?: StartingLayout,
+ profile?: ModifierProfile,
+ preferredColor: "white" | "black" | "random" = "white",
+): { code, token, color, layout, profile? } {
+ // Resolve random at creation time.
+ const creatorColor: "white" | "black" =
+ preferredColor === "random"
+ ? (Math.random() < 0.5 ? "white" : "black")
+ : preferredColor;
+
+ // Store both the resolved creator color AND the joiner color
+ // on the room so joinRoom() returns the right one.
+ const joinerColor: "white" | "black" =
+ creatorColor === "white" ? "black" : "white";
+
+ // ... rest of createRoom, using creatorColor instead of the
+ // hardcoded "white" literal. Room now has a `joinerColor` field.
+}
+
+joinRoom(code: string): JoinResult {
+ // Read `room.joinerColor` instead of hardcoding "black".
+ const player: RoomPlayer = {
+ token,
+ color: room.joinerColor,
+ // ...
+ };
+}
+```
+
+**Random-seed determinism**: use `crypto.randomBytes` or `Math.random`?
+`Math.random` is fine for non-security randomness here. No test
+flakiness concern — the color is known AFTER room creation.
+
+**Backward compat**: clients sending the old payload (no
+`preferredColor`) get the default `"white"`, matching current
+hardcoded behaviour exactly.
+
+### UI changes
+
+**`packages/chess/src/ui/Lobby.tsx`**:
+
+Add a color selector in the "Host Game" section, between the
+LayoutPicker and the Modifier Profile picker:
+
+```tsx
+
+```
+
+State: `const [preferredColor, setPreferredColor] = useState<"white" | "black" | "random">("white");`
+
+`handleCreate()` includes `createPayload.preferredColor = preferredColor`.
+
+Visual style matches the existing modifier-profile chip selector pattern
+for consistency.
+
+### Tests
+
+**Unit tests** in `packages/server/src/rooms.test.ts`:
+- Default (no `preferredColor`) → creator=white, joiner=black (back-compat).
+- `preferredColor: "white"` → creator=white.
+- `preferredColor: "black"` → creator=black, joiner=white.
+- `preferredColor: "random"` → both outcomes sampled across 20 rooms.
+
+**E2E tests** in `packages/chess/e2e/multiplayer.spec.ts`:
+- Update existing "Scholar's Mate checkmate" test to still pass with
+ the new default — should need zero changes.
+- Add NEW test: host picks "black" → pageA=black, pageB=white. Verify
+ color assignment end-to-end.
+- Add NEW test: host picks "random" → one of the two valid
+ assignments; test asserts "each client has exactly one of
+ {white, black}" and they differ.
+
+**Playwright UI test**: verify the color selector renders, all three
+options clickable, default is "white".
+
+### Execution checklist
+
+- [ ] F1.1. Add `preferredColor` to `RoomCreatePayloadSchema` (zod
+ server) + client type mirror + parity test.
+- [ ] F1.2. Update `RoomRegistry.createRoom` signature; store
+ `joinerColor` on the room.
+- [ ] F1.3. Update `RoomRegistry.joinRoom` to read `room.joinerColor`.
+- [ ] F1.4. Add color preference UI to Lobby.tsx with `data-testid`
+ on each option.
+- [ ] F1.5. Update `Lobby.handleCreate()` to include
+ `preferredColor` in the payload.
+- [ ] F1.6. Unit tests on rooms.ts (4 scenarios).
+- [ ] F1.7. E2E tests: add 2 new multiplayer scenarios for
+ black-host + random-host.
+- [ ] F1.8. Run full Playwright (expect 85/85).
+- [ ] F1.9. **Commit**: `feat(multiplayer): host color preference
+ (white/black/random)`
+
+### Risk register
+
+- **Test flakiness from random**: ZERO risk — tests assert either
+ color assignment works, not a specific outcome.
+- **Backward compat**: backfilled default = `"white"` makes old
+ clients indistinguishable.
+- **UI footprint**: 3-button group; no new dependencies; matches
+ existing Tailwind patterns.
+
+---
+
+## Feature 2 — Extinction-Chess UI Target Cycling
+
+### Current state
+
+`extinction-chess` preset (shipped commit `08b8e0f`) uses
+`engine.presetState<{ targetType: PieceType }>("extinction-chess")`
+for configuration. Default seeded to `"pawn"`. No UI exposure — users
+must set via code or dev console.
+
+### Design
+
+Add a chip cycler inside the RulesDrawer's extinction-chess card.
+Appears ONLY when extinction-chess is active. Clicking cycles through
+the 6 piece types: pawn → knight → bishop → rook → queen → king → pawn.
+
+The card should visually indicate "Target: Pawns" (or similar) above
+the chip so inactive-mode users still see the default.
+
+### UI surface
+
+**`packages/chess/src/ui/RulesDrawer.tsx`**: inside the
+`data-preset="extinction-chess"` block, after the normal
+description/toggle/deps rendering, insert a configuration row:
+
+```tsx
+{preset.id === "extinction-chess" && isOn && (
+
+
+ Target:
+
+
+
+)}
+```
+
+The `cycleExtinctionTarget` callback is passed in as a prop (parent
+owns the state). RulesDrawer reads the current target from
+`engine.presetState<{ targetType: PieceType }>("extinction-chess")`
+via a new prop `extinctionTarget: PieceType` and calls
+`setExtinctionTarget(next)` on click.
+
+### State flow
+
+The host manages `extinctionTarget` in GameView or via a dedicated
+hook:
+- On preset activation: read current from `engine.presetState`.
+- On cycler click: compute next type in the sequence, update
+ preset state, and force a re-render of the RulesDrawer.
+- In multiplayer: changing the target is an authoritative action that
+ must propagate via a new WS message (see below).
+
+### Protocol changes (multiplayer support)
+
+Adding a `custom-modifier-config.update` style message would over-engineer
+this. Piggyback on the existing preset-activation rebroadcast by
+including the target type in a new lightweight message, OR bake it
+into the existing preset activation request.
+
+**Recommended approach**: Reuse the `modifier-profile.propose` /
+`modifier-profile.update` mechanism's pattern but for preset state.
+This is a bigger lift. ALTERNATIVE: defer multiplayer sync of the
+target to follow-up. For this feature, support is **local-only
+(solo play)** and multiplayer games use whatever target was set at
+room-creation time.
+
+**DECISION**: ship solo-only target cycling in v1. If user demand
+follows, add a `preset-config.update` WS message in v2. Document the
+solo-only limitation in-UI ("Target: Pawns (set before multiplayer
+game starts)" hint).
+
+### Tests
+
+**Unit**: none needed — the preset logic is already covered.
+
+**Component/integration**: a RulesDrawer test verifying the cycler
+renders when extinction-chess is active, cycles through all 6 types,
+and updates the engine's preset state.
+
+**E2E** (Playwright): solo scenario — activate extinction-chess,
+click cycler 3 times, verify displayed target is now "rook" (or
+whatever 3 clicks from pawn yields).
+
+### Execution checklist
+
+- [ ] F2.1. Add `extinctionTarget` prop + `onExtinctionTargetCycle`
+ callback to RulesDrawer props.
+- [ ] F2.2. Render cycler inside extinction-chess card when the
+ preset is active.
+- [ ] F2.3. GameView manages the state and threads props to
+ RulesDrawer.
+- [ ] F2.4. E2E test in `rule-variants.spec.ts` (additive).
+- [ ] F2.5. Documentation: add a "Target cycling" note to
+ `extinction-chess.ts` docblock referencing the UI.
+- [ ] F2.6. **Commit**: `feat(ui): extinction-chess target cycler in
+ rules drawer`
+
+### Risk register
+
+- **Multiplayer sync gap**: shipped solo-only. Room-creation-time
+ target is fixed for the session. Acceptable because changing
+ extinction targets mid-game is unusual.
+- **Chip state-sync bug**: engine preset state is the source of truth.
+ RulesDrawer reads on every render; no local component state.
+
+---
+
+## Feature 3 — Berolina En-Passant
+
+### Current state
+
+`berolina-pawns` + `berolina-pawns-2` (shipped commits `5393b96`,
+`ee08e20`) explicitly defer en-passant. Current behavior: no
+en-passant captures exist for Berolina pawns. Pawns that
+double-diagonal-push past a square an enemy pawn could have
+captured it on cannot be ep-captured — the opportunity is lost.
+
+### Design
+
+Classic Berolina rule variant: if a Berolina pawn double-pushes via a
+diagonal, and an enemy Berolina pawn is adjacent on the SKIPPED
+square's file (the middle square of the diagonal), the enemy can
+capture **orthogonally forward** onto the skipped square, removing the
+double-pushed pawn (standard en-passant semantics reflected through
+Berolina geometry).
+
+**Rule authority**: multiple Berolina rule sets exist; we're picking
+the most common (Parton 1952 variant). Document explicitly in the
+preset docblock that other variants exist and can be added as
+additional presets if demand emerges (e.g. `berolina-pawns-3` with
+different ep semantics).
+
+### Implementation path
+
+Since `berolina-pawns` uses `overridePieceMoves` (not composes with
+default en-passant), we must synthesize ep moves in the override
+itself. The engine already tracks `EnPassantTarget` in the game-level
+facts; our override can read it and emit the ep move.
+
+**`packages/chess/src/presets/berolina-pawns.ts`**:
+
+1. In `overridePieceMoves`, after computing push/capture/promotion:
+ - Read `engine.session.get(GAME_ENTITY, "EnPassantTarget")`.
+ - If non-null and this pawn can capture (orthogonally forward) to
+ the ep square → emit the ep capture move.
+2. Engine's `applyMove` already updates `EnPassantTarget` on every
+ move; no engine change needed. BUT — the engine sets it based on
+ FIDE-style pawn double-push (rank+2 orthogonal). For Berolina
+ double-push (diagonal), we need to override this.
+
+**Engine-side extension** (minor, in-scope): add a hook or expose
+`setEnPassantTarget` so the preset can update the target on its own
+double-push path. OR: handle the ep target update inside the
+preset's `onAfterMove` — cleaner since the preset owns the
+mechanics.
+
+**Recommended path**: handle both emission AND target-update in the
+preset:
+- `overridePieceMoves` emits the ep capture when
+ `EnPassantTarget` is set.
+- `onAfterMove` detects when the preset's own pawn just did a
+ double-diagonal-push, and calls
+ `engine.session.insert(GAME_ENTITY, "EnPassantTarget", skippedSquare)`.
+- Add a tag / metadata to the LegalMove (e.g. `isEnPassant: true`,
+ already a field on LegalMove per existing FIDE en-passant handling)
+ so the engine's capture-resolution path removes the captured pawn.
+
+### Edge cases
+
+- **Berolina + FIDE pawn mix**: impossible in practice (the preset
+ replaces pawn rules for all pawns of the scoped color(s)). But
+ with `scope: "white"`, black pawns follow FIDE ep rules — the
+ engine's default ep handling covers black. Verify the hybrid case.
+- **Promotion + ep**: ep capture landing on the 1st/8th rank is
+ theoretically possible but extremely unusual on a fresh board. Keep
+ the ep capture non-promoting; document.
+- **`berolina-pawns-2` shares the same ep rule**: the sideways
+ captures don't change ep geometry.
+
+### Tests
+
+Add to `berolina-pawns.test.ts`:
+- ep capture available after enemy double-diagonal-push (4 positions).
+- ep capture NOT available after single-push (no ep target).
+- ep capture is lost if not taken on the next turn (standard ep
+ semantics).
+- ep captured pawn is correctly removed from the board.
+- Scope interaction: `scope: "white"` means white gets Berolina ep,
+ black still uses FIDE ep.
+
+Similar additions to `berolina-pawns-2.test.ts`.
+
+### Execution checklist
+
+- [ ] F3.1. Add ep-capture emission to `berolina-pawns.ts`
+ `overridePieceMoves`.
+- [ ] F3.2. Add `onAfterMove` handler that sets
+ `EnPassantTarget` on Berolina double-push.
+- [ ] F3.3. Copy/reuse ep logic into `berolina-pawns-2.ts`.
+- [ ] F3.4. Add 5+ ep-specific tests to each preset's test file.
+- [ ] F3.5. Update the preset docblock: remove the "deferred"
+ language, document the authoritative rule choice.
+- [ ] F3.6. Update `RULES.md` — remove the en-passant deferred note
+ from the Berolina gallery entries.
+- [ ] F3.7. **Commit**: `feat(presets): berolina-pawns en-passant
+ (both variants)`
+
+### Risk register
+
+- **Ambiguity of Berolina ep**: 2-3 published variants exist. We pick
+ the most common (Parton). Document authoritatively; other variants
+ are their own presets.
+- **EnPassantTarget collision with FIDE ep**: on a scope-flipped
+ activation, both sides might set the target — but only one side
+ can double-push per turn. No real collision.
+
+---
+
+## Feature 4 — Tier 3 Royalty-Transfer
+
+### Current state
+
+Tier 3 presets from the rule-variants design doc (explicitly deferred
+in commit `3cef8f5` closing the epic). The canonical example:
+**Abdication / Knight-Queen Transfer** — at any time, the king can
+"abdicate" to a chosen friendly piece, transferring royalty to that
+piece (and possibly vice versa).
+
+### Why it was deferred
+
+- Requires a **mid-game player action** beyond "one move at a time".
+ The existing engine only processes `LegalMove`; there's no
+ "action" channel for non-move actions.
+- Requires **UI for target selection** — click a piece → "transfer
+ royalty here".
+- Multiplayer semantics: a royalty transfer is a turn-consuming
+ action? Or free? How do both clients agree?
+
+### Design
+
+Introduce a new category: **player actions** that are not `LegalMove`
+but consume a turn (or not, configurable).
+
+#### Engine surface
+
+Add a new public method `engine.performAction(action: PlayerAction):
+ActionResult`. `PlayerAction` is a tagged union:
+
+```typescript
+type PlayerAction =
+ | { kind: "transferRoyalty"; fromPieceId: EntityId; toPieceId: EntityId };
+```
+
+Add a new preset hook `performAction(ctx: PlayerActionContext):
+ActionResult | undefined`. First non-undefined wins. Context includes
+`action`, `mover`, `engine`.
+
+**Engine dispatch**: similar to `applyMove` but for actions. Fires
+`onBeforeAction` → resolver → `onAfterAction` → turn advance
+decision → `onTurnStart` / `onCheckGameResult`.
+
+**Turn advance semantics**: same `shouldAdvanceTurn` hook. Presets
+can veto as they do for moves.
+
+#### Preset: `transferable-royalty`
+
+```typescript
+PRESET_REGISTRY.register({
+ id: "transferable-royalty",
+ name: "Transferable Royalty",
+ description: "Royal pieces can transfer their royalty to another friendly piece once per game.",
+ // ...
+ performAction({ engine, mover, action }) {
+ if (action.kind !== "transferRoyalty") return undefined;
+ // Validate: fromPiece is currently royal, toPiece is friendly + not already royal.
+ // Mark state: `transferredFrom[mover] = fromPieceId`; future `getRoyalPieces`
+ // returns `toPieceId` instead of `fromPieceId`.
+ return { consumed: true, turnAdvances: true };
+ },
+
+ getRoyalPieces({ engine, color }) {
+ // Read preset state — if a transfer has occurred for this color,
+ // return the new royal set (original royals minus transferred-from,
+ // plus transferred-to).
+ },
+});
+```
+
+#### UI
+
+- **Trigger**: right-click on a friendly piece → context menu with
+ "Transfer royalty to here" (requires a royal piece already selected
+ OR a 2-click flow).
+- **Indicator**: selected royal → hover other friendlies → candidate
+ targets highlighted in a distinct color (e.g. gold outline).
+- **Confirmation**: modal "Transfer royalty from King (e1) to Queen
+ (d1)?" — prevents accidental triggers.
+- **Turn consumed**: the action ticks the turn; show in the moveLog
+ as a "royalty-transfer" event (new `MoveRecord` tag).
+
+#### Protocol (multiplayer)
+
+Add a new WS message: `game.action` with payload `{ token,
+action: PlayerAction }`. Server validates and broadcasts the
+resulting state via the existing state-update path.
+
+### Prerequisites
+
+This is a **bigger lift** than the first three features. Before
+starting:
+1. Confirm stakeholder demand — is there a real user need for this?
+2. Decide turn-consumption semantics (a free action would be wildly
+ unbalanced).
+3. Nail the UI interaction (2-click vs context menu vs modal).
+
+### Execution checklist (draft — to be refined)
+
+- [ ] F4.1. Engine surface: `PlayerAction` type + `performAction` method.
+- [ ] F4.2. Preset hook: `performAction`.
+- [ ] F4.3. `MoveRecord` extended with `actionKind?` field for logging.
+- [ ] F4.4. `transferable-royalty` preset implementation + tests.
+- [ ] F4.5. Protocol: `game.action` WS message (server + client parity).
+- [ ] F4.6. UI: target selection + confirmation modal in GameView.
+- [ ] F4.7. E2E: solo scenario + multiplayer scenario.
+- [ ] F4.8. Docs: PRESET-API.md section on actions-not-moves;
+ RULES.md gallery entry.
+- [ ] F4.9. **Commit**: `feat(engine): player actions +
+ transferable-royalty preset`
+
+### Risk register
+
+- **Engine surface expansion**: significant. Every
+ `applyMove`-adjacent assumption (move-generates-next-turn, move-log,
+ checkGameResult timing) needs to be re-audited for the action path.
+ Mitigation: keep the `performAction` path structurally parallel
+ to `applyMove`; reuse the same hook dispatch order.
+- **UI interaction ambiguity**: "right-click is not discoverable".
+ Alternative: a dedicated "Actions" button in GameView that opens
+ a menu. Safer default for touch devices too.
+- **Multiplayer sync**: action takes effect server-side first; client
+ receives the state update. Same pattern as moves — no new
+ consistency issues.
+- **State lifecycle**: the "transfer once per game" constraint lives
+ in preset state. Ensure it's serialized in GameStatePayload (same
+ as custom modifiers — pattern from T3 Q4.1).
+
+---
+
+## Parallel Execution Map
+
+```
+Feature 1 (MP color choice) — 9 tasks — 1 session
+ F1.1-F1.8 serial (each builds on previous) → F1.9 commit
+
+Feature 2 (Extinction-chess UI) — 6 tasks — ½ session
+ F2.1-F2.5 serial → F2.6 commit
+
+Feature 3 (Berolina ep) — 7 tasks — ½ session
+ F3.1-F3.6 serial → F3.7 commit
+
+Feature 4 (Royalty-transfer) — 9 tasks — 1-2 sessions
+ Needs stakeholder alignment FIRST.
+```
+
+**Recommended sequencing**: 1 → 2 → 3 → (pause for 4 decision) → 4.
+
+Features 1-3 are INDEPENDENT and can be done in any order. Feature 4
+requires more thought; don't start until the first three ship and
+demand is validated.
+
+---
+
+## Verification Gate (applies to every feature)
+
+Pattern mirrors the rule-variants epic's Final Verification Wave:
+
+- F1 (oracle): plan compliance — every declared task checked.
+- F2 (unspecified-high): code quality — 0 anti-patterns, tests ≥ 8
+ per new preset, `incompatibleWith` graph symmetric.
+- F3 (manual QA + playwright): end-to-end smoke.
+- F4 (deep): scope fidelity — no drive-by refactors, no unrelated
+ protocol changes.
+
+Scale the wave down for Feature 2 (no new preset; just UI) and
+Feature 3 (no new preset; just preset extension): F2 + F3 reviewers
+only, F1 + F4 skipped.
+
+---
+
+## Glossary
+
+- **PlayerAction**: a turn-consuming event that is NOT a `LegalMove`.
+ Introduced in Feature 4 (deferred).
+- **Royalty transfer**: a PlayerAction that reassigns the "royal"
+ flag between friendly pieces. Core of Feature 4.
+- **En-passant target**: `GAME_ENTITY.EnPassantTarget` fact tracking
+ the square a pawn skipped on its most recent double-push; valid for
+ exactly one following half-move. Feature 3 extends this to
+ Berolina geometry.
+- **Target cycling**: the UI pattern Feature 2 introduces for
+ preset-state configuration without requiring a dedicated editor.
+
+---
+
+## Decision log (pre-implementation)
+
+Before starting any feature, confirm:
+
+| Feature | Decision needed | Default / Recommendation |
+|---|---|---|
+| 1 | Joiner-side color choice? | NO — defer. Creator picks, joiner takes what's left. |
+| 1 | Re-pick colors mid-session? | NO — defer. Fixed at room creation. |
+| 2 | Multiplayer target-sync in v1? | NO — solo-only. Room-creation-time target fixed for MP. |
+| 3 | Which Berolina ep variant? | Parton 1952 (most common). Document authoritative choice. |
+| 4 | Turn-consumption of royalty transfer? | Consumes a turn (balance). |
+| 4 | UI for target selection? | Dedicated "Actions" button > right-click (discoverability). |
+| 4 | Stakeholder validated? | **REQUIRED before starting**. Don't implement on spec. |
+
+---
+
+## Early-stop / scope-reduction triggers
+
+If tool budget or context pressure intrudes:
+
+- **After Feature 1**: ship MP color choice alone. Highest user
+ value. Features 2-4 become their own follow-ups.
+- **After Feature 1 + 2**: ship both polish items. Deferred gallery
+ note updates land later.
+- **After Feature 1 + 2 + 3**: feature-complete short of Tier 3.
+ Royalty-transfer becomes its own epic with its own plan.
+
+In every case: update this plan's checkboxes, append a
+`## Early Stop` section, and do NOT declare the full plan complete.
+
+---
+
+## Momus review instructions
+
+When this plan is saved, invoke Momus via:
+
+```
+task(subagent_type="momus", prompt=".sisyphus/plans/post-epic-deferrals.md")
+```
+
+Momus will evaluate against rigorous clarity / verifiability /
+completeness standards. Incorporate feedback before starting any
+feature.