fix(openworlds): cold-open waiting state is clearly alive, not frozen (Addresses #385) - #387
Conversation
|
Caution Review failedPull request was closed or merged during review No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR introduces cold-open (first-beat) UI that distinguishes initial narration from subsequent beats by computing ChangesCold-open first-beat UI and accessibility
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
Addresses #385 (P0/CRITICAL). Do NOT close on merge — verify on next build's newbie playtest.
Root cause
The cold-open (first-beat) waiting state renders through
DmNarratingBeat(viewer/openworlds/screen-table.jsx). It did have animation — pulsing dots, a shimmer label, and a live elapsed counter — but every one of those aliveness cues was either decorative-and-aria-hidden(dots, shimmer) oraria-hidden(the elapsed counter), and the visible headline never changed.The §8.2 newbie harness "sees" the screen via Playwright
ariaSnapshot/ariaText(screen-reader semantics) plus a single static PNG screenshot (qa/playwright/palette_server.js). To that surface — and to a real screen-reader user, and to anyone glancing at a still frame — the entire affordance collapsed to two unchanging strings: "The Dungeon Master is narrating" + "Setting the opening scene — the first beat of a session can take a few minutes." Nothing in the accessibility tree ever changed, so it read as a frozen/crashed app. The newbie waited ~11s, saw nothing move, and gave up (satisfaction 4/10,gave_up:true).So #336's animation and #348's adaptive recovery window were wired to the cold-open (
pending.firstBeatthreads through fromarmPendinginapp.jsx), but the proof-of-life was invisible on the surfaces that actually decide "is this alive?".What changed (
screen-table.jsxonly — additive, first-beat-scoped)aria-hiddenfor the cold-open ("composing · 0:42") — it's in the accessibility tree and visibly ticks. A live readout that changes every second is the strongest "still alive" cue a screen reader / snapshot can perceive.aria-liveregion. A separate visually-hiddenrole="status"announces a stable reassurance once on mount (a polite region re-announces its whole text on every change, so the ticking clock stays out of it — the exact noise [playtest][P1] "DM is narrating…" state has no progress animation — 35–60s waits feel like a freeze #336 avoided by hiding elapsed).firstBeatearly-return leaves the original [playtest][P1] "DM is narrating…" state has no progress animation — 35–60s waits feel like a freeze #336/[playtest][P1] Nav buttons time out / unclickable during active DM narration (can't leave Table, can't return to it) #341/[playtest][P1] DM first-response ~60s NO narration → recovery fired; recovery-timeout may pre-empt the legit multi-minute DM opening #348 path untouched (only the inline label string became alabelvar that evaluates to the same string for later beats).Tests
New
viewer/tests/test_cold_open_progress.py(6 tests) transpiles the realscreen-table.jsxwith the shipped Babel and rendersDmNarratingBeatunder acreateElement-capturing React stub (same architecture astest_recovery_timing.py). It asserts:Full viewer suite: 162 passed, 1 skipped, 17 subtests (single-process).
test_recovery_timing.py(#348) still green.license_checkclean. JSX transpiles under the bundled Babel.Scope notes
viewer/openworlds/screen-table.jsx(+ the new test). Noapp.jsxchange needed —pending.firstBeatwas already threaded through; the bug was purely in the render, not the hook/state plumbing.styles.cssNOT touched (the existingdm-narrate-stylekeyframes already cover the dots/shimmer; reduced-motion still stills them; the elapsed/flavor are information, not decoration).Follow-up (separate, tracked in #385)
The first-beat latency itself ("a few minutes", no streaming) is a distinct quality concern; this PR only makes the wait obviously-alive so a first-timer waits instead of bouncing.
Summary by CodeRabbit
New Features
Improvements