Skip to content

docs: video frames without jitter, a still camera, better shots - #657

Merged
a-ratz merged 4 commits into
ui-reworkfrom
docs/video-motion
Oct 13, 2026
Merged

a-ratz merged 4 commits into
ui-reworkfrom
docs/video-motion

Conversation

@a-ratz

@a-ratz a-ratz commented Oct 12, 2026

Copy link
Copy Markdown
Member

Docs and tools only (into ui-rework, merged to main with #623). Follows #654.

What was wrong with the v0.5.8 video

Watching it showed a slight, needless zoom in the Snake part, micro jerks in the dashboard, a Quick, Draw! guesser card growing out of the bottom of its window, and two shots that said little (Tournament: an empty lane area and a half card; Focus: a decider with nothing in its live view). The jerks came from the tools, not the dashboard:

  • Frame times: recordings were timed by when each frame arrived, which jitters by a few ms (7 to 13 % of the frames off the 16.7 ms paint grid by more than 4 ms), so a glide sampled at 30 fps now and then skipped or held a frame.
  • Dissolves: the stage dissolved a frame that came after a pause over 0.3 s; the next frame, 16 ms later, cut the dissolve short, a small jerk at the start of every movement.
  • Push-ins: the cameras zoomed slowly over every clip, rescaling every frame a little, so the UI shimmered.

What changed

  • tools/lib.mjs: a frame's time is the compositor's (when it was painted) on the monotonic clock; a wall-clock step shows as a difference between the paint and the arrival interval of two frames and is carried in the offset.
  • stage/stage.js: no dissolves; still cameras; shots reframed for the reel's about 1.2:1 windows (escalate, vote, shadow, tournament, coverage, lanes, focus); the Quick, Draw! board at full height.
  • tools/record-draw.mjs: Auto-arrange stays on, so the guesser grows in the middle of the Flow (Dashboard: Auto-arrange does not lay the Flow out again when a card grows #655, fix: Auto-arrange follows cards that grow (#655) #656).
  • tools/record-features.mjs: Tournament opens the bracket before the take and traces the winner and another finalist through it (hover); Focus double-clicks an LLM agent while it writes, so lineage and live output show.
  • README.md: the above, as advice for the next video.

Tested

The video was recorded again from ui-rework with these tools and rendered (111 s, 1920 x 1080): the median change between rendered frames went from 0.21 to 0.06, and check_motion.py rendered finds no jumps other than deliberate UI changes (the theme switch, the palette and the focus view opening, the old dashboard in the before shot).

🤖 Generated with Claude Code

…book)

What made the v0.5.8 video shimmer and jerk, and the shots that said
little:
- lib.mjs times frames by the compositor (paint times, 16.7 ms apart in a
  glide) on the monotonic clock instead of by arrival (a few ms of
  jitter), and carries wall-clock steps it sees between paint and arrival.
- The stage no longer dissolves between frames (a dissolve after a pause
  was cut short by the next frame) and its cameras keep still (no slow
  zooms: they rescaled every frame a little).
- record-draw keeps Auto-arrange on, so the guesser stays in the middle as
  it grows (#655).
- record-features: the tournament clip opens the bracket before the take
  and traces options through it; the focus clip opens an agent that is
  writing, so its live output shows.
- Shots reframed for the narrower reel window (escalate, vote, shadow,
  tournament, coverage, lanes, focus) and the draw board at full height.
- README: the above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@a-ratz

a-ratz commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

Local Codex review, round 1 (codex review of the branch against its base, at b470a1c).

Findings as reported

Target: branch diff against origin/ui-rework

Two recording regressions were reproduced with in-memory mocks: delivery delays corrupt timestamp alignment, and focus selection becomes stale before the take. Syntax checks passed; live browser recording was not rerun.

Full review comments:

  • [P2] Separate clock corrections from frame delivery delays — docs/internal/video/tools/lib.mjs:81-84
    When CDP delivery is delayed by more than 60 ms, drift treats that delay as a wall-clock adjustment. If latency then recovers in smaller increments, the incorrect offset persists: a simulated 120 ms stall leaves subsequent frames 120 ms ahead of mark() timestamps, even though the wall clock never changed. This desynchronizes the parallel app/board recordings and their cuts. Detect clock changes independently of delivery latency, preserving the monotonic-time invariant in AGENTS.md:228.

  • [P2] Wait for a running writer after the recording warm-up — docs/internal/video/tools/record-features.mjs:106-109
    With the default triage demo, replies stream for only 1.4–1.65 seconds. After this returns a running writer, clip() waits 1.2 seconds before recording and another 0.9 seconds before dblclick(), so the selected run has already finished when focus opens. The take therefore opens completed output rather than the live writing it waited for. Move the running-writer selection into the action phase, after those waits.

…ter in the take (playbook)

Review round 1:
- lib.mjs measures the wall clock's skew against the monotonic clock at
  each frame and moves the paint time by it: a late delivery no longer
  reads as a clock step.
- The focus clip looks for an agent that writes during the take and marks
  the cut at the double-click, so the focus view opens while the reply
  streams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@a-ratz

a-ratz commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Local Codex review, round 2 (codex review of the branch against its base, at 8dd1383).

Findings as reported

Target: branch diff against origin/ui-rework

The timestamp conversion can corrupt recordings across clock adjustments or missing CDP timestamps. JavaScript syntax checks passed, but in-memory tests reproduced both regressions; live recording was not tested.

Full review comments:

  • [P2] Bound corrected frame times by monotonic receipt time — docs/internal/video/tools/lib.mjs:75-77
    On WSL/NTP, a backward clock step between painting a frame and handling its CDP event leaves metadata.timestamp using the old offset while skew uses the new one. A two-second step therefore places that frame two seconds in the future; updating last then pins subsequent frames there, making frameAt() freeze and skip intervening motion. Reject or bound future corrected timestamps using monotonic receipt time before updating last, preserving the clock-step safety required by AGENTS.md:228.

  • [P2] Fall back when CDP omits the frame timestamp — docs/internal/video/tools/lib.mjs:76-77
    CDP declares ScreencastFrameMetadata.timestamp optional. If a frame omits it, this calculation produces NaN, which also poisons last, so every subsequent frame receives NaN even when its timestamp is present. Saving the index converts those values to null, breaking the stage's timestamp-based frame selection. Fall back to monotonic receipt time when the timestamp is absent or non-finite.

…none

A wall-clock step between a frame's paint and its delivery no longer
places the frame in the future (and pins the frames after it), and a
frame without a compositor timestamp takes its arrival time instead of
turning every later time into NaN.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@a-ratz

a-ratz commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Local Codex review, round 3 (codex review of the branch against its base, at 722f40e).

Findings as reported

Target: branch diff against origin/ui-rework

The new writer-polling deadline can select the wrong agent after a wall-clock correction. Syntax checks and in-memory timing checks passed otherwise; live recording and rendering were not exercised.

Review comment:

  • [P2] Use a monotonic deadline for writer polling — docs/internal/video/tools/record-features.mjs:107-107
    If VM time synchronization or another clock correction occurs during this wait, Date.now() can expire the deadline early or extend it beyond 30 seconds. Early expiration falls through to the default tech_support node, potentially recording an idle agent instead of the writer that is about to stream. Use performance.now() for both the deadline and comparison, following the monotonic-interval requirement in AGENTS.md:228.

The Focus take's wait for a streaming writer and the wait for a
decision's lane measured their deadlines with the wall clock, which
steps on WSL and could end the wait early.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@a-ratz

a-ratz commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Review loop closed after round 3.

  • Round 1 (delivery delay read as a clock step; Focus writer picked before the take): fixed in 8dd1383.
  • Round 2 (a step between paint and delivery placed a frame in the future; a missing CDP timestamp turned every later time into NaN): fixed in 722f40e. Frame times are now bounded by the frame's arrival on the monotonic clock, and a frame without a timestamp takes its arrival time.
  • Round 3 (the writer polling deadline used the wall clock): fixed in bc37edc, together with the same pattern in the lane wait (laneOf). Both deadlines now use performance.now().

The findings have narrowed to tooling edge cases with no effect on the rendered video, so I am stopping the loop here.

@a-ratz
a-ratz merged commit 44e34b6 into ui-rework Oct 13, 2026
4 checks passed
@a-ratz
a-ratz deleted the docs/video-motion branch October 13, 2026 00:10
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