Make the rewind scrubber render a subject-neutral timeline document - #741
Merged
Merged
Conversation
The scrubber read a Run-shaped payload directly (history.events, history.observations, five hardcoded Run event-type strings), so every future lens over a different kind of subject would have meant new frontend branching. It now renders a generic document -- a time domain plus marker/series lanes, each marker optionally carrying the state it puts its subject into -- so adding a lens later is a server-side change with no frontend work. A small bridge adapter in page.html converts today's unchanged run_history wire payload into that document shape; it is expected to move server-side and disappear once the producer emits documents directly. Also fixes three defects found while reading the code for this: the whole --cs-* color palette was defined on a class (.cora-scrubber) that nothing ever applied to the mount root, so the scrubber has been rendering unstyled; backToLive() never called the mount's own cleanup, leaking a running requestAnimationFrame loop against detached DOM; and scaffold() interpolated a run's name/status into innerHTML unescaped, the one injection hole on an otherwise textContent-only page. Verified against a real browser (Playwright) driving a fake relay: palette resolves, XSS payload renders as escaped text, and the folded readout correctly walks active -> paused -> active -> ended across a Started/Held/Resumed/Completed sequence with per-lane readings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This was referenced Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First step of a larger plan (two modes sharing one viewer: a flowing live window, and rewind reaching any run in the record). This step is frontend-only, no transport change, independently shippable.
The scrubber read a Run-shaped payload directly (
history.events,history.observations, five hardcodedRun*event-type strings), so every future lens over a different kind of subject would have meant new frontend branching. It now renders a generic timeline document — a time domain plus marker/series lanes, each marker optionally carrying thestateit puts its subject into — so adding a lens later is a server-side change with no frontend work. A small bridge adapter inpage.html(runHistoryToTimelineDocument) converts today's unchangedrun_historywire payload into that document shape; it's expected to move server-side and disappear once the producer emits documents directly (a later step).Also fixes, found while reading the code for this
--cs-*color palette was defined on a class (.cora-scrubber) that nothing ever applied to the mount root — the scrubber has been rendering unstyled.backToLive()never called the mount's own cleanup, leaking a runningrequestAnimationFrameloop against detached DOM.scaffold()interpolated a run's name/status intoinnerHTMLunescaped — the one injection hole on an otherwisetextContent-only page.Verification
Driven in a real browser (Playwright) against a fake relay server serving fixture
run_historydata:--cs-linecomputed correctly,.cora-scrubberclass present).active → paused → active → endedacross aRunStarted/RunHeld/RunResumed/RunCompletedsequence, with per-lane numeric and categorical readings at the cursor.backToLivemid-playback correctly invokes cleanup.No Python files touched;
infra/status-relay/sits outside the repo's Python-scoped pre-commit hooks by design (host-layer artifact, stdlib+websockets only). Full CI still runs since pre-push hooks aren't path-scoped.🤖 Generated with Claude Code