Summary
When an understudy or cover is playing a role for a given session, the existing mic auto-assignment should be re-run with the actual performers for that night. This adds a cast diff view to the Mics page, per-row apply/skip controls, a preview timeline, and a re-run modal with "preserve overrides" vs "wipe and regenerate" options.
Part 3 of 3 for issue #792. Depends on #1113 (casting matrix must exist).
Backend changes
Update MicrophoneAutoAssignmentController (controllers/api/show/microphones.py, POST /show/microphones/suggest):
- Accept optional
casting_overrides: {character_id: cast_id} in body — the algorithm uses these instead of Character.played_by for the affected characters' swap-cost calculations (same actor = zero swap cost)
- Accept
preserve_overrides: bool (default true) — when false, existing manual allocations are not treated as locked slots (wipe mode)
Update utils/show/mic_assignment.py (minimal change):
- Add optional
cast_overrides: dict[int, int] parameter to the assignment function
- Where cast identity is looked up via
character.played_by, check the overrides dict first
Frontend changes (client-v3/)
ConfigMics.vue — new re-run section:
New CastDiffTable.vue (components/show/config/mics/):
- Header: "Pending Changes: {N}/{total}" with Select All / Clear
- Columns: checkbox | Mic# | Act · Scene | Character | Was (struck-through) | Now (bold) | Apply/Skip toggle
- Per-slot diff derived from current
MicrophoneAllocation vs session casting
- Live preview via existing
MicTimeline component (updates as checkboxes change)
- Footer: "{N} manual override(s) on file · Re-run with {checked} changes…" button (disabled if 0 checked)
New ReRunMicModal.vue (components/show/config/mics/):
- Two radio options:
- Preserve manual overrides (default/recommended)
- Wipe and regenerate from scratch (destructive)
- Lists current manual overrides
- On confirm:
POST /api/v1/show/microphones/suggest with {casting_overrides, preserve_overrides} → PATCH /api/v1/show/microphones/allocations → success toast + badge clears
MicPlanStaleBanner.vue (full version, replacing stub from #1113):
- Shows exact slot count: "2 cast changes · 5 mic slots affected"
StartSessionConfirmModal.vue (from #1112/#1113) — full stale-mic blocking:
- If
micPlanIsStaleForSession → amber warning: "Mic plan is out of date"; Start button disabled; "Go to Mics" link
stores/show.ts additions:
- Getter:
micPlanIsStaleForSession(sessionId): boolean
- Action:
reRunMicAllocation({sessionId, castingOverrides, preserveOverrides})
E2E tests
Update client-v3/e2e/tests/09-show-config-mics.spec.ts:
- After cover casting change (from spec 12b), navigate to Mics
- "Pending Cast Changes" section visible with diff rows
- Uncheck one row → preview timeline updates
- "Re-run…" → select Preserve → confirm → toast + badge clears
Update client-v3/e2e/tests/13-live-show.spec.ts:
- Start session with cover cast → confirmation modal shows stale-mic warning + Start disabled
- Navigate to Mics, re-run → return → Start now enabled
Verification
pytest — mic assignment tests with casting_overrides; existing allocation tests unchanged
npm run typecheck + npm run lint
npm run test:e2e — full suite
Summary
When an understudy or cover is playing a role for a given session, the existing mic auto-assignment should be re-run with the actual performers for that night. This adds a cast diff view to the Mics page, per-row apply/skip controls, a preview timeline, and a re-run modal with "preserve overrides" vs "wipe and regenerate" options.
Part 3 of 3 for issue #792. Depends on #1113 (casting matrix must exist).
Backend changes
Update
MicrophoneAutoAssignmentController(controllers/api/show/microphones.py,POST /show/microphones/suggest):casting_overrides: {character_id: cast_id}in body — the algorithm uses these instead ofCharacter.played_byfor the affected characters' swap-cost calculations (same actor = zero swap cost)preserve_overrides: bool(defaulttrue) — whenfalse, existing manual allocations are not treated as locked slots (wipe mode)Update
utils/show/mic_assignment.py(minimal change):cast_overrides: dict[int, int]parameter to the assignment functioncharacter.played_by, check the overrides dict firstFrontend changes (
client-v3/)ConfigMics.vue— new re-run section:castDiffForSessiongetter (from Add per-character eligible cast pool and per-session casting matrix #1113) is non-empty: show amber "X cast changes pending — mic re-run recommended" badge in the page headerCastDiffTable.vuesection below existing tabs that auto-expands when there are pending changesNew
CastDiffTable.vue(components/show/config/mics/):MicrophoneAllocationvs session castingMicTimelinecomponent (updates as checkboxes change)New
ReRunMicModal.vue(components/show/config/mics/):POST /api/v1/show/microphones/suggestwith{casting_overrides, preserve_overrides}→PATCH /api/v1/show/microphones/allocations→ success toast + badge clearsMicPlanStaleBanner.vue(full version, replacing stub from #1113):StartSessionConfirmModal.vue(from #1112/#1113) — full stale-mic blocking:micPlanIsStaleForSession→ amber warning: "Mic plan is out of date"; Start button disabled; "Go to Mics" linkstores/show.tsadditions:micPlanIsStaleForSession(sessionId): booleanreRunMicAllocation({sessionId, castingOverrides, preserveOverrides})E2E tests
Update
client-v3/e2e/tests/09-show-config-mics.spec.ts:Update
client-v3/e2e/tests/13-live-show.spec.ts:Verification
pytest— mic assignment tests withcasting_overrides; existing allocation tests unchangednpm run typecheck+npm run lintnpm run test:e2e— full suite