Fixes surfaced by the "How Salt Changed History" run:
- Decisions rail showed a stale choice. Contract now requires a superseding
decision_log entry when a logged choice changes mid-run; the board collapses
by category+subject and renders the latest as current, tagged "revised"
(AGENT_GUIDE Decision Communication Contract; board.js renderDecisions).
- Assets gate jumped by a draft render. The review surface is the filmstrip:
render per-scene stills to snapshots/<scene_id>.png (scripts/atelier_snapshots.py)
and STOP; the full/draft render is the compose stage, post-approval
(checkpoint-protocol, bespoke-composition).
- Broken thumbnails / missing scenes. A .tsx "animation" asset is no longer a
renderable visual — resolve snapshots/<scene_id>.* or a shot-spec placeholder;
add <img onerror> fallback and a BESPOKE placeholder (state.py, board.js).
- Clicking a card video did nothing. .thumb video had no sizing, so the frame
and click box drifted; add object-fit:cover. Click-to-play on the render-hero
player; narration is clickable -> modal with the full text (board.css/js).
- Type too small everywhere. Add one --fs-scale (1.16) knob; every font-size is
calc(px * var(--fs-scale)) so text scales proportionally and reversibly,
verified for zero overflow at desktop + tablet widths.
- F-01: cost bar crit (red) state past 90% of budget
- F-02: normalize() hardens fetched board state against sparse payloads
- F-03: /thumb 404s for videos with no extractable poster frame instead
of serving raw video bytes
- F-04: checkpoint artifact path refs only resolve inside the project dir
- F-05 (board half): stall detection — in_progress stage with no disk
activity >10min renders red 'stalled?' + header badge flips to STALLED?;
verified against the real wedged why-cities-glow project
- eval harness from dogfood session committed (visual regression +
interaction smoke, capture watcher, server/gate test suites) +
regression tests for each finding; 46 backlot tests green, visual eval
green (restage-before-capture note logged)
- README 'Watch It Happen' section with four staged screenshots (fictional
demo productions, generated placeholder art — no private project content)
+ governance section notes enforced approval gates
- scripts/backlot_screenshot_stage.py: stages 4 demo projects in 4 pipeline
states against OPENMONTAGE_PROJECTS_DIR and captures the shots with
Playwright — reproducible README imagery
- lib/paths.py: OPENMONTAGE_PROJECTS_DIR env override (checkpoints, events,
and the board all follow one root); backlot/state.py now imports it
(was still defining its own copy — the exact drift the review warned of)
- undeclared-stage checkpoints (e.g. a legacy 'idea' run under a cinematic
manifest) now slot into their canonical rail position instead of dangling
after publish, drawn dashed + italic with an 'unlisted' note
- ChangeHub subscriptions filtered per project: unrelated-project bursts
can no longer flood a board's queue and starve its own change signal
- thumbnail temp files unique per request (concurrent-miss race on the
shared .tmp path corrupted the cache)
- watcher change-mapping is pure string work (no per-path resolve() in
thousand-file change batches); 'C:'-style project ids rejected
- replay: tz-naive timestamps treated as UTC; final render/script no
longer leak at t0 on storyboard-less projects; single tick chain on
rapid pause/play; drag-safe scrubber (label tracks input, board renders
on release); render-video playback survives SSE re-renders
- state: scene id 0 joins correctly, nested depth>0 events don't corrupt
generating state, out-of-project asset paths honestly unserveable,
negative durations clamped, tolerant manifest stage parse
- UI: decisions alt filter precedence fixed, activity counts parallel
same-tool runs, el() html sink removed, NaN-safe formatters
- replay mode: scrub a completed run from checkpoint history + event
timestamps — stage rail rewinds, script/storyboard/renders appear at
their real moments, generating shimmer replays; ~20s full-run playback
- stage history_entries exposed in BoardState (powers replay + versions)
- entrance choreography plays on first paint only (not on SSE refreshes)
- live-run fixes found by driving a simulated production against the
board: null rendered by native append, activity ticker showing closed
starts as running, scene-id label handling for 'scene-N' ids
- scripts/backlot_simulate_run.py: drives a fake production through the
REAL contract (init_project, in_progress heartbeats, gated
awaiting_human -> approved, per-scene events, growing manifest) —
live-board verification + demo driver
- backlot/README.md