Skip to content

fix(openworlds): cold-open waiting state is clearly alive, not frozen (Addresses #385) - #387

Merged
100yenadmin merged 1 commit into
mainfrom
fix/385-coldopen-progress
May 30, 2026
Merged

fix(openworlds): cold-open waiting state is clearly alive, not frozen (Addresses #385)#387
100yenadmin merged 1 commit into
mainfrom
fix/385-coldopen-progress

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented May 30, 2026

Copy link
Copy Markdown
Member

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) or aria-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.firstBeat threads through from armPending in app.jsx), but the proof-of-life was invisible on the surfaces that actually decide "is this alive?".

What changed (screen-table.jsx only — additive, first-beat-scoped)

Tests

New viewer/tests/test_cold_open_progress.py (6 tests) transpiles the real screen-table.jsx with the shipped Babel and renders DmNarratingBeat under a createElement-capturing React stub (same architecture as test_recovery_timing.py). It asserts:

  • the cold-open accessible text changes at t=2s / 30s / 120s (the exact frozen-app failure);
  • the headline rotates through several flavor lines;
  • the announced live-region text is stable second-to-second (no per-tick spam, no m:ss in it);
  • the old static "Setting the opening scene — the first beat" copy is gone from the cold-open;
  • later-beat treatment is unchanged (regression guard).

Full viewer suite: 162 passed, 1 skipped, 17 subtests (single-process). test_recovery_timing.py (#348) still green. license_check clean. JSX transpiles under the bundled Babel.

Scope notes

  • Touched only viewer/openworlds/screen-table.jsx (+ the new test). No app.jsx change neededpending.firstBeat was already threaded through; the bug was purely in the render, not the hook/state plumbing.
  • styles.css NOT touched (the existing dm-narrate-style keyframes already cover the dots/shimmer; reduced-motion still stills them; the elapsed/flavor are information, not decoration).
  • Honest "composing…" + elapsed timer — no faked progress.

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

    • Opening scene composition now displays distinct UI with "Composing…" text instead of generic narration copy.
    • Added rotating flavor labels and improved timer display for the first beat.
  • Improvements

    • Enhanced accessibility for cold-open progress with better live-region announcements and stable status updates.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f673a78-4530-4a86-b838-8b780e609918

📥 Commits

Reviewing files that changed from the base of the PR and between 71616cd and 2f82f72.

📒 Files selected for processing (2)
  • viewer/openworlds/screen-table.jsx
  • viewer/tests/test_cold_open_progress.py

📝 Walkthrough

Walkthrough

The PR introduces cold-open (first-beat) UI that distinguishes initial narration from subsequent beats by computing pendingFirstBeat state, rendering rotating flavor labels via DM_COLD_OPEN_FLAVOR, and adjusting action-bar copy and DmNarratingBeat accessibility behavior accordingly. Comprehensive tests validate that the cold-open is observably alive to accessibility consumers.

Changes

Cold-open first-beat UI and accessibility

Layer / File(s) Summary
Cold-open state and flavor pool definition
viewer/openworlds/screen-table.jsx
Derives pendingFirstBeat to detect when the pending action is the first beat, introduces DM_COLD_OPEN_FLAVOR array of rotating cold-open status strings, and exposes the flavor pool on window for test and devtools access.
DmNarratingBeat cold-open rendering with accessibility
viewer/openworlds/screen-table.jsx
Refactors DmNarratingBeat to detect firstBeat and render specialized cold-open UI: rotates through the flavor pool based on elapsed time, applies first-beat-specific wait guidance, and implements accessibility by adding a role="status" live region announced once on mount while keeping the visible label and elapsed timer updated outside the live region. Later beats continue using the steady narrating path.
Action-bar UI wiring for cold-open
viewer/openworlds/screen-table.jsx
Updates action-bar input placeholder and Declare button label to use cold-open "composing your opening scene" wording when pendingFirstBeat is true, otherwise preserving existing narrating/stuck/read-only text behavior.
Test infrastructure and comprehensive behavior validation
viewer/tests/test_cold_open_progress.py
Adds embedded Node+Babel harness to transpile and render screen-table.jsx, extracting visible text, aria-hidden-filtered accessible text, and role="status" live regions. Validates that the cold-open is observable to accessibility consumers: flavor pool rotates across time, accessible text changes between elapsed times and includes clock tokens, visible text uses composing language, live-region statusText is stable (no ticking clock), and later beats maintain previous aria-hidden elapsed behavior while displaying ticking clocks in full text.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

  • electricsheephq/WorldOS#385: The PR directly implements the cold-open first-beat UI and accessibility behavior described in the playtest issue, including rotating flavor labels and live-region accessibility semantics to make the cold-open "obviously alive" to screen readers.

Possibly related PRs

  • electricsheephq/WorldOS#328: Introduces the DmNarratingBeat narrating affordance that this PR extends with cold-open-specific flavor rotation and accessibility behavior.
  • electricsheephq/WorldOS#343: Restructures narration rendering around pending/in-flight states; this PR extends DmNarratingBeat rendering within that same pending-UI context for the cold-open first beat.
  • electricsheephq/WorldOS#351: Adds firstBeat-aware wait-hint and recovery timing contract; this PR adds the firstBeat-driven cold-open flavor/UI and accessibility behavior that pairs with that contract.

Poem

🐰 The first beat now shimmers with rotating flair,
Composing scenes while the clock ticks fair,
Accessibility whispers without a care,
No more silent static—the cold-open's there! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides comprehensive context (root cause, changes, tests, scope) but lacks the required CLA and validation checklist sections from the template. Add the missing 'Licensing / CLA' and 'Validation' sections with appropriate checkboxes and checks-run list to match the repository template.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing the cold-open waiting state to be clearly alive, not frozen, and references issue #385.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

@100yenadmin
100yenadmin merged commit bb1870d into main May 30, 2026
6 of 7 checks passed
@100yenadmin
100yenadmin deleted the fix/385-coldopen-progress branch May 30, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant