feat: #835 Live Composition Increment 2 — enable-able follow-ups (still dark) - #1047
Conversation
…ble + correct (still DARK)
Adversarial-review follow-ups on Increment 1 (merged). The feature stays DARK
(default WORLDOS_STREAM_BEATS=0); these fixes prepare it without enabling it.
FIX A (frontend OFF-path inertness): expose a server-visible `streamBeats` flag
on /openworlds/campaigns.json (mirrors WORLDOS_STREAM_BEATS via the new
_stream_beats_enabled(), default false). The OpenWorlds catalog poll threads it
into `state.streamBeats`, and useLiveSession now gates the /beat-stream poll
effect (the effect + fetch + interval + visibilitychange listener) on it. With
the feature off the viewer fires ZERO /beat-stream requests instead of polling
~500ms during every pending turn for a sidecar that never exists.
FIX B (orphaned tailer on signal, ON path): (1) persist the tailer PID to
$STATE_DIR/stream/tailer.pid in worldos_stream_tailer_start (the dm_turn $(...)
subshell can't export a global to the parent's trap), and reap it from the new
worldos_stream_tailer_kill_pidfile in play.sh/play_party.sh's EXIT/INT/TERM
cleanup traps — so a SIGINT/SIGTERM mid-beat no longer orphans the tailer.
(2) self-bounding defaults in stream_tailer.tail_stream: a hard wall-clock
lifetime cap (WORLDOS_STREAM_TAILER_MAX_S, default 1800s) and an idle/no-growth
cap (default 180s), so a missed stop signal can never leave it running forever.
FIX C (cross-beat cursor race): reset beatStreamCursor (+ composingText) to 0 in
armPending, not only on run change — the tailer truncates current.jsonl per beat,
so a continuing beat inheriting the prior beat's high cursor dropped the new
beat's opening preview chunks.
FIX D (text aliases): the engine's log_event normalizes message/content/note as
aliases for `text` (text or message or content or note). The Increment-1 scanner
only captured the literal `text`, so an alias beat streamed nothing. The scanner
now captures whichever of {text,message,content,note} (PROSE_TEXT_KEYS) appears
as the depth-1 prose value, with `text`-wins precedence; nested/non-alias keys
(e.g. meta) are never captured.
Tests (single-process, -p no:xdist):
qa/test_stream_tailer.py — +8 FIX-D (each alias, alias-before-kind buffer,
alias+non-prose-kind, text-wins, meta-negative, nested-alias-negative) +3
FIX-B (idle cap, lifetime cap, env-bound parsing).
qa/test_stream_tailer_lifecycle.sh — new shell test for the pidfile lifecycle
(OFF no-op, ON persist, stop clears, signal-trap reaper kills by pidfile).
viewer/tests/test_live_narration_stream.py — +3 (composing row ON, OFF-path
ZERO fetches, FIX-C cursor reset proven discriminating).
viewer/tests/test_openworlds_static.py — +1 (streamBeats reflects env) plus an
assertion that it defaults false.
bash -n clean on the touched scripts.
Feature still defaults OFF; the viewer no longer polls /beat-stream when off.
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThe PR delivers two enhancements to the live-composition pipeline. ChangesLive Composition: prose aliases, self-bounding tailer, pidfile orphan cleanup, streamBeats gate
Sequence Diagram(s)sequenceDiagram
participant Server as viewer/server.py
participant Viewer as viewer/openworlds/app.jsx
participant BeatAPI as /beat-stream
participant Tailer as scripts/stream_tailer.py
Server->>Viewer: campaigns.json includes streamBeats
Viewer->>BeatAPI: poll when streamBeats is true
BeatAPI->>Tailer: read sidecar from since cursor
Tailer-->>BeatAPI: chunks and next cursor
BeatAPI-->>Viewer: composing chunks
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 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 |
The #1043 review's follow-ups, still default OFF (
WORLDOS_STREAM_BEATS=0). A: frontend now truly inert when off — server exposesstreamBeats, the React /beat-stream poll is gated on it (proven: 0 requests when off). B: tailer killed on SIGINT/SIGTERM via pidfile+trap reaper, plus a self-bounding max-lifetime/idle cap so it can't orphan. C: beat-stream cursor resets onarmPending(no dropped first chunks per beat). D: tailer capturesmessage/content/notealiases (text-wins), matching the engine's log_event normalization. 29 tailer + 759 viewer + 13 lifecycle tests pass; OFF-default verified inert.Summary by CodeRabbit
New Features
message,content, andnotefields as alternatives totext.Bug Fixes
Tests