diff --git a/CHANGELOG.md b/CHANGELOG.md index 23de8d33e0..36136396eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ checkpoint, and status-only commits are intentionally omitted. ### Changed +- Terminal live-proof recordings tune VP9 for realtime capture and accept the recorder once any payload is written, matching the encoder's bursty muxer output. - OpenClaw browser live proofs run against the repository's mock control-UI dev server. - Folder reconciliation now defers with a zero-mutation result when the open-state scan hits GitHub rate limiting, so throttled proof/apply/publish runs proceed to their close and publication work instead of failing before `Apply close proposals` can run. - Comment-only sync now defers its remaining batch as a runtime-budget-style yield when GitHub rate limits a live read, instead of failing the scheduled run; the next 15-minute cycle resumes the interrupted item, and close-mode apply keeps its loud failure. diff --git a/src/live-proof/drivers.ts b/src/live-proof/drivers.ts index 96e3e3963d..ee5e857ba1 100644 --- a/src/live-proof/drivers.ts +++ b/src/live-proof/drivers.ts @@ -244,6 +244,12 @@ export function terminalCommandPlan(options: { ":99.0", "-c:v", "libvpx-vp9", + // Realtime tuning: default VP9 encoding cannot hold 30fps on a + // two-core hosted runner and buffers output for seconds at a time. + "-deadline", + "realtime", + "-cpu-used", + "8", options.rawVideoPath, ], waitAfter: "recorder", @@ -347,20 +353,18 @@ function waitForRecorder( recorderSession: string, rawVideoPath: string, ): void { - let previousSize: number | undefined; for (let elapsed = 0; elapsed <= RECORDER_READY_TIMEOUT_SECONDS; elapsed += 1) { const size = recordingSize(runner, checkout, rawVideoPath); if (recorderExited(runner, checkout, recorderSession)) { - throw new Error("recorder session exited before the raw WebM began growing"); - } - if (size !== undefined) { - if (previousSize !== undefined && size > previousSize) return; - previousSize = size; + throw new Error("recorder session exited before the raw WebM was written"); } + // VP9 muxer output is bursty, so consecutive equal size samples are normal + // while recording; any written payload proves the recorder captures frames. + if (size !== undefined && size > 0) return; if (elapsed < RECORDER_READY_TIMEOUT_SECONDS) pollSleep(runner); } throw new Error( - `raw WebM did not begin growing within ${RECORDER_READY_TIMEOUT_SECONDS} seconds`, + `raw WebM was not written within ${RECORDER_READY_TIMEOUT_SECONDS} seconds`, ); } diff --git a/test/live-proof.test.ts b/test/live-proof.test.ts index 4e78c8e370..b5cecd3802 100644 --- a/test/live-proof.test.ts +++ b/test/live-proof.test.ts @@ -295,7 +295,7 @@ test("terminal driver accepts a recorder file that appears and grows late", () = const calls: string[] = []; const result = runTerminalFixture( terminalLifecycleRunner(calls, { - recorderSizes: [undefined, undefined, 7, 7, 11], + recorderSizes: [undefined, undefined, 0, 0, 11], }), ); assert.equal(result.status, "completed"); @@ -319,7 +319,7 @@ test("terminal driver reports a dead recorder with its pane diagnostics", () => () => runTerminalFixture(runner), (error: unknown) => { assert.ok(error instanceof Error); - assert.match(error.message, /recorder session exited before the raw WebM began growing/); + assert.match(error.message, /recorder session exited before the raw WebM was written/); assert.match(error.message, /\[xterm: .*\]\nxterm pane ready/); assert.match(error.message, /\[recorder: .*\]\nffmpeg: cannot open display :99/); return true; @@ -457,7 +457,7 @@ test("live-proof workflow keeps execute secretless and attach trusted", () => { assert.match(source, /uses: \.\/\.github\/actions\/create-target-write-token/); assert.match(source, /CLAWSWEEPER_LIVE_PROOF_S3_ENDPOINT: \$\{\{ secrets\./); assert.match(source, /setsid timeout --kill-after=30s 1500s/); - assert.match(source, /apt-get install --yes ffmpeg tmux x11-utils xvfb xterm/); + assert.match(source, /apt-get install --yes ffmpeg tmux x11-utils xfonts-base xvfb xterm/); assert.match(source, /--record \.\.\/live-proof-report\.md/); assert.doesNotMatch(source, /--plan \.\.\/live-proof/);