feat: Record a clip around one action with a single record-video command

Merged
HA
8d ago
+1845-40

Summary

  • uloop record-video --seconds N --trigger "<uloop command>" records a clip around one action in a single command: it focuses the Editor, starts recording, runs the trigger, marks the moment the trigger returns, waits N seconds, and stops. The burst sheet from the previous change is then centered on that moment.
  • --seconds alone records for N seconds; --trigger alone implies 3 seconds.

User Impact

  • Before: an agent had to run start, the action, a mark, a sleep, and stop as separate commands, and a failure in the middle could leave a recording running.
  • After: one command returns the stop response plus a OneShot report: the seconds waited, the trigger's own result, whether the recording accepted the mark, a note when focus-window failed, and warnings (for example when the mark was not accepted, or when an awaited pause point paused Play Mode).
  • A failing trigger stops the recording at once and fails with RECORD_VIDEO_TRIGGER_FAILED, the stopped recording attached. A cancelled one-shot fails with RECORD_VIDEO_CANCELLED after stopping. A stop that finds no recording (for example another caller stopped it) fails with RECORD_VIDEO_STOP_FAILED.
  • Triggers that cannot produce a useful recording are rejected before anything starts: record-video itself, enable-pause-point without --await, and enable-pause-point --await with a mode that pauses Play Mode (only --mode trace is accepted). Nested triggers work with outer single quotes and inner double quotes.
  • Without the new flags, record-video writes exactly what it wrote before, byte for byte, including its own argument errors.

Changes

  • The one-shot stops the recording on every exit after a successful start, using a context the caller's cancellation cannot cut short.
  • Unlike the pause-point wait, any Success:false from the trigger counts as a failure: a recording of an action that failed shows nothing worth reading.
  • --trigger parsing is split into the checks every trigger shares (quoting, an empty value, a leading uloop, --project-path) and each command's own rule. pause-point keeps its nested-wait rejection with the same message; tests pin it before the split. A leading-uloop correction is judged by the calling command's rules.
  • --seconds and --trigger are declared once and appear in --help, uloop list, unknown-option suggestions, and the skill-table generator's CLI-only allow list.
  • The tool lookup and param building of a plain tool call is extracted so the one-shot reports a start failure exactly as a plain start does.

Verification

  • go test for the new parse and flow tests (input-space table: passthrough for start/stop/status/mark and invalid values, invalid seconds, action and max-duration checks, trigger rules, call order, trigger failures, mark rejection, start failure byte comparison with a plain start, auto-stop, external stop, cancellation during trigger/mark/wait, focus note) all pass.
  • Mutations caught: stopping with the caller's context, ignoring the trigger's Success:false, and ignoring cancellation each fail the tests.
  • gofmt, go vet, and golangci-lint report 0 issues in all four modules. The module test suites pass except two tests that need to bind a socket or create a directory in the system temporary folder, which the local sandbox refuses; they are unrelated to this change.
  • check-release-triggers --base <integration branch> --head HEAD passes.
  • Live Editor checks in Play Mode (keyboard demo scene):
    • --seconds 2 --trigger "simulate-keyboard --action Press --key Space": MarkAccepted: true, BurstAnchor: mark, MarkSeconds 0.195. The Space highlight appears 2 captured frames (about 80 ms) before the mark frame, because the press finishes before the trigger returns.
    • --seconds 1 --trigger "enable-pause-point --file <path> --line <n> --mode trace --await --timeout-seconds 20" with the key pressed from another shell: hit returned, MarkAccepted: true, mark at 3.806 s; the effect appears 2 captured frames (about 66 ms) before the mark frame.
    • A nested trigger (enable-pause-point ... --mode trace --await --trigger "simulate-keyboard ...") hit and was marked.
    • record-video --help lists --seconds, --trigger, and mark among the --action values.