Repository navigation
Outputs drill: record, stream and virtual camera as one command (#443) - #1024
Conversation
scripts/qa/outputs-drill.ps1 runs the release-gate workload against an installed app and writes verdict.json + summary.txt: per output (Program recording, each ISO, registered OS camera, loopback SRT stream) the identities expected/decoded/missing/repeated and PASS/FAIL/NOT JUDGED. Overall PASS only when every enabled output passes with zero missing. Transcribed from the 2026-10-22 qualification harness that passed a 30-minute soak; judging reuses the existing scripts/qa judges unchanged. iso-pixel-verdict.py gains additive missing/repeated counts (pass rule unchanged). Preferences are backed up, patched (RTMP forced off, key untouched, SRT to a loopback sink) and restored byte for byte. Tests: outputs-drill.test.ps1 (params, plan, prefs round trip, sink URL, readiness/invariants, runs test_outputs_drill.py: aggregation and offline orchestration on synthetic videos via the real judges). The live run was not executed end to end. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Blocker 1: StreamSrtPort was written as a JSON number; the app model is string?, so System.Text.Json rejected the whole file and the store recovered from .bak, silently dropping RTMP-off, the recording folder, the ISO list and the loopback SRT. Every patched value is now typed to the model and a patch that changes any value's JSON kind is refused. After the API is up the file is re-read and no output starts unless the patch survived. Readiness also fails hard on a recording outside the drill folder or any RTMP sender. The app's .bak sidecar is backed up and restored too. A test helper (outputs-drill-prefs-probe) runs the installed app's own serializer by reflection: it loads the patched profile and rejects the numeric port. Blocker 2: the stop sequence moved into finally (also runs on Ctrl-C). Outputs are stopped and finalized, the engine is turned off, and WM_CLOSE is sent only when the close guard would have nothing to ask. Preferences are restored only after the app exits; otherwise preferences-restore-pending.json and exit 3. Also: Collect-Identity throughout-assertions restored (hardware encoders, remotes 1080p, capture progress within 2 s, admissions, stream fps/warnings) into measurement.json, and any false fails the drill. The Program verdict is gated on program-source-admission-capture.mjs. Without an OS camera bound, Program and stream are UNBOUNDED, never PASS. The sink and receiver start at Normal priority and record it. The sink binds right after choosing the port. Backups live outside the evidence folder. A DPAPI SRT passphrase is refused. The drill also refuses if corevideo-native or the zoom engine is running. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Review fixes pushed in 19e47b1. Blocker 1 (patch silently discarded). I checked this against the installed app's own Blocker 2 (WM_CLOSE while live). The stop sequence now lives in
Medium 3. The reference's throughout-assertions are back, accumulated per sample into
Any false flag fails the drill. Sample rows keep Medium 4. The drill runs at Normal priority for the whole live phase. The sink and the receiver are checked and their Medium 5. A new status, UNBOUNDED: clean over the whole file but not camera-bounded. It never counts as PASS, so a Lows 6, 7 and 9.
Tests: Still unverified: the drill has still not been run end to end against the installed app. That leaves the applied-check against a running app, the stop/close sequence and its close-guard predicate against the live 🤖 Generated with Claude Code |
Closes the scripting half of #443: the release-gate outputs drill as one maintained command,
scripts/qa/outputs-drill.ps1, replacing the one-off harness inD:\CoreVideo-QA\beta-20261021-build\artifacts\qualification-1008-25(which passed the 2026-10-22 30-minute soak). Judging is not redesigned: it reuses the existingscripts/qajudges.What it does
production-output-preferences.json, points recording at<Out>\recordings, selects the ISO sources, forces RTMP off (the stored key and URL are never changed), and points SRT (caller) at a loopback ffmpeg listener on a free UDP port that the script starts itself. The file is restored byte for byte at the end, including on failure.StartCoreVideo.cmdat Normal priority withCOREVIDEO_QA_ISO_COUNTER/COREVIDEO_QA_PROGRAM_COUNTERand the delivery trace inside the evidence folder. It drives everything through/snapshot,/stateandPOST /invoke. No SendKeys, no elevation, no PresentMon.-Seconds. Invariant breaks abort the run as FAIL. It stops in Collect-Identity order, stops the sink, leaves the meeting, closes the app with WM_CLOSE (the harness's exit path), restores preferences, and judges offline.verdict.jsonandsummary.txthave one line per output (Program recording, each ISO, OS camera, stream): expected, decoded, missing and repeated identities, plus PASS, FAIL or NOT JUDGED with a reason. Overall PASS requires every enabled output to pass with zero missing; NOT JUDGED never counts as PASS. Program GPU deadlines are reported as supplementary evidence only.-RemoteIds, the drill uses local sources only: webcam and screen in Program, and the webcam as the only ISO. The verdict says so. Screen capture has no QA ISO identity stamp, so it is not ISO-recorded. Media is not included yet.iso-pixel-verdict.pygains additivemissing/repeatedcounts. Its pass rule is unchanged.docs/qa/outputs-drill.md. CLAUDE.md is unchanged because its table coversdocs/referenceonly.docs/BACKLOG.mdis unchanged.Exercised by tests (run locally, Windows PowerShell 5.1 + Python 3.12)
scripts/qa/outputs-drill.test.ps1: 24/24 pass. It covers parameter validation (seconds range, D: evidence folder, remote ids, meeting URL, webcam id), id-list parsing, the workload plan in local and remote modes, webcam discovery from the profile, and the preferences backup/patch/restore round trip on a temp file with fake secrets. The round trip checks that RTMP is forced off, the RTMP key, URL and SRT passphrase are untouched, the ISO list stays a JSON array, non-ASCII text survives, older profiles get the missing keys, the restore is byte-identical and a corrupted backup is detected. It also covers the loopback sink URL, the free UDP port, readiness and collection invariants on sample snapshots, and the compact sample row, then runs the Python tests.scripts/qa/test_outputs_drill.py: 15/15 pass. It covers verdict aggregation from sample judge outputs shaped like the 2026-10-22 soak (PASS, FAIL on missing or repeated, judge error = NOT JUDGED, FAIL outranks NOT JUDGED, a collection failure is FAIL, unproven camera serving, camera interval coverage, summary lines). It also runs the offline orchestration end to end on synthetic 60-frame 1080p60 videos with QA identity bands and a synthetic trace, decoded by the real recording and ISO judges through ffmpeg: a clean run passes on Program, ISO and stream, and one dropped frame fails all three with missing=1.scripts/qa/test_iso_pixels.py(5/5, one new case) andscripts/tests/iso-pixel-evidence.test.mjspass. The script's parameter refusals were run directly; they exit before touching anything.Not verified
vcam-serve.logserving proof, stop order, app close, preferences restore against the real file) is a transcription of the reference harness and has not run from this script. I didn't run it because the owner may be using the machine and the test meeting has no remote feeds.-CameraReceiveror buildcorevideo-shared-camera-receiver.🤖 Generated with Claude Code