Repository navigation
Practice CoD: stop after a confirmed VICTORY or DEFEAT end screen (QORCAP_AUTOCLOSE, practice lane) - #151
Merged
Conversation
Off unless QORCAP_AUTOCLOSE=1, QORCAP_LANE=practice and the game set is Call of Duty (the practice LIVE launcher sets both variables). Before this change QORCAP_AUTOCLOSE only closed the launcher window 10 s after a clean exit; qorcap.exe and the sidecar did not read it. CoD already stopped on its own only when the full final posted (end word plus a readable Qortrola30 row and owner-row consensus). New sidecar/qorcap_cod_endstop.py watches the end-word and owner-row reads the CoD frame loop already makes. Guard: - only a calibrated, confirmed victory/defeat read counts; any other frame (killcam, gameplay, lobby, Tab board without the word, the other word) ends the run, so a one-frame flicker cannot confirm; - strictly later capture timestamps, gap <= 3 s; - 8 reads over >= 3 s when Qortrola30's end row was read on one frame of the run, otherwise 16 reads over >= 6 s; - the run must start >= 120 s after the first frame seen (arming). After confirmation capture runs a tail (QORCAP_AUTOCLOSE_TAIL_S, default 12, clamped 10..15), then the existing stop path runs: the same /shutdown, seal, countersign, bundle and receipt as Enter. The stop reason is an existing value (final if a final posted, otherwise post_final_tail). Enter/Q and capture errors still stop at once; the tail only adds time to the older final stop. Info only: one "stop note:" line in qorcap.log (the /shutdown body may carry a printable-ASCII note, at most 240 bytes, that is logged and not stored) and cod_end_stop.json beside the bundle. Neither is bundle.json, the receipt, or a verification input. Tests: state machine with fake reads and an injected clock (killcam, Tab, flicker, word switch, gap, pre-arm negatives; VICTORY and DEFEAT positives; tail timing), the stop wiring, the runner hook, committed 720p fixtures, and a box-only frame test kept out of both CI suites. Sidecar suite floors re-collected: 905 linux / 887 windows. Untouched: receipt.v0, contracts/, schema/, the capture encode and the recording path. REAL MATCH stays on v0.3.38.
ConWan30
marked this pull request as ready for review
November 11, 2026 17:37
ConWan30
added a commit
that referenced
this pull request
Nov 11, 2026
…#152) The linux job failed on main and on #150/#151 after about 63 min in `sudo apt-get update && sudo apt-get install -y ffmpeg`. The logs show no prompt and no dpkg lock: on the ubuntu-26.04 image the runner could not resolve azure.archive.ubuntu.com, archive.ubuntu.com or security.ubuntu.com ("Temporary failure resolving"). apt waited 20 s per package per mirror for ~90 packages, then exited 100. packages.microsoft.com and github.com resolved. The ffmpeg step now downloads a pinned BtbN/FFmpeg-Builds static build (autobuild-2026-10-31-12-59, n8.1.3 linux64 gpl, month-end builds are kept long-term), checks its sha256, and puts ffmpeg/ffprobe on PATH via GITHUB_PATH. If the download or checksum fails, apt runs with DEBIAN_FRONTEND=noninteractive, Acquire retries/timeouts, a dpkg lock timeout and an outer `timeout`. The step has timeout-minutes 15. Job timeouts: linux 40, contracts 20, windows 45 min. Only .github/workflows/ci.yml changes. release.yml and product code are untouched.
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.
Draft. Not for merge without Con's word. Practice lanes only.
What it does. With
QORCAP_AUTOCLOSE=1andQORCAP_LANE=practice(the practice LIVE launcher sets both) on a Call of Duty match, QorCap stops on its own after a confirmed VICTORY or DEFEAT end screen. It waits a tail first (default 12 s) and then stops through the same path as Enter.What
QORCAP_AUTOCLOSEdid before. It was read only by the launcher.ps1, which closes the console window 10 s after a clean exit. Neither qorcap.exe nor the sidecar read it, and it was not specific to Madden or CoD. Before this PR, CoD stopped by itself only when the full CoD final posted: the end word plus a readable Qortrola30 row with K/D and owner-row consensus. If the row didn't read, you had to press Enter.False-trigger guard (
sidecar/qorcap_cod_endstop.py). It watches reads the CoD frame loop already makes. It doesn't read pixels itself and posts nothing to/events.CodEndScreenReaderreturns a calibrated,confirmedvictoryordefeat.QORCAP_AUTOCLOSE_FRAMES). If the row was not read, the bar is twice that: 16 reads over at least 6 s.QORCAP_AUTOCLOSE_ARM_S). A leftover end screen in the lobby cannot stop a new match.Stop. After confirmation, capture runs
QORCAP_AUTOCLOSE_TAIL_S(default 12, clamped 10..15). Then the main loop stops the same way as Enter:/shutdown, seal, countersign, bundle, receipt.finalif a CoD final posted, otherwisepost_final_tail. No new value goes into the receipt.finalstop that would fire earlier waits for it.Info only.
stop note:line goes into qorcap.log./shutdownaccepts an optional printable-ASCIInoteof at most 240 bytes; it is logged and not stored. The line contains none of the needlesstop_reason_from_loglooks for.cod_end_stop.jsonis written beside the bundle: result, reader, run count and span, frame seq, capture t_ms, config. It follows the same rule aspost_final_tail.json: not bundle.json, not the receipt, not read by verification.cod_session.json, becauseattach_sessioncopies its sync tags onto bundle.json.Files.
sidecar/qorcap_cod_endstop.py(new),sidecar/qorcap_cod.py(+12: optional read hook; an exception in the hook is swallowed),sidecar/qorcap_match.py(wiring),src/api.rs(/shutdownnote logged), tests,tools/assert_pytest_skips.py(new file in both suites; box-only file excluded; floors re-collected to 905 linux / 887 windows against qortime 0204e6d).Untouched: receipt.v0,
contracts/,schema/, the capture encode, the recording path. REAL MATCH stays on v0.3.38. The keyframe fix (Fix 2) stays on hold.Tests (local Linux).
test_qorcap_cod_endstop.py, 25 tests:test_qorcap_cod_endstop_box.pyruns only on the build box (excluded from CI) over 370 earlier sampled frames. No mid-match sample read as an end word; each of the 18 end-word samples confirmed its own word when held.test_qorcap_submit.pybecause fastapi isn't installed on the box; CI installs it. Skip gate passes.cargo test: lib 206, main 1, contracts 2, haptic_replay 1, redteam 2, all pass. No new clippy warnings.Not measured. I have no committed killcam or "Final Killcam" frames, so the killcam negative is modeled ("no confirmed word"), not measured on a real killcam. Real-match recall at the sidecar's sample rate (about 2–4 Hz) is unmeasured; a slow PC could take longer to reach 8 or 16 reads. In the sampled frames, "BEST PLAY" follows the end header, and a 12 s tail may end during it.
CI artifact. The CI on main uploads
qorcap-<sha7>-windows-x64for this PR, which contains #150 plus this change.