Repository navigation
rig: pixi run qc — the five-minute release validation of a tier on a rig PC (LAB-222, LAB-338) - #285
Merged
7 commits merged intoOct 29, 2026
Merged
7 commits merged into
7 commits merged into
Conversation
…latform (#280, LAB-338) Manifest only; the re-lock with pixi 0.83 follows once the v0.3.0 channel is tagged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…`channel` feature (default/typed/viz), dev from git alone Channel read at the payload commit 5e20dee (the v0.3.0 tag sits one commit early; moving it is a one-liner for Michael). `dev` no longer includes `typed` and the rig_nodes pin is no longer a workspace dependency: one package name from a git source and the channel does not solve once the released version is ahead of rig-nodes main. Verified on the Mac with the sibling rig-nodes checkout renamed away: `pixi install --locked` (default + typed), check-typed lists the eight message types, `pixi run test` 59/59, `pixi run -e typed test` 62/62. Closes #280. LAB-338. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…a rig PC (LAB-338) One command checks the candidate on /next/ (or Production) end to end: the tier's build.json → a scratch worktree at that SHA with its own pixi environment, the candidate's graph with the rig's flags, FicTrac (or the simulator), the Studio emulator pre-check, the tier's Studio in a QC Chrome profile driven over DevTools (build SHA, protocol load, bridge connect, the one Web Serial click, Run experiment), export + runlog-check + closed-loop-report + bench-check, teardown, a pass/fail table and a ready-to-paste validation note. Per-rig rows and the checklist text live in rig/qc/profiles.toml (ww18, ww14, bergamo, mac); the procedure is rig/QC.md; tests/test-rig-qc.py pins the pure half. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Oct 29, 2026
…ostics moved to the private repo, LAB-227) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…on protocol needs FicTrac) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
4 tasks
…EAK (ww18 rc1 QC findings)
Two findings from the first `pixi run qc` on ww18 (candidate 2026-10-29 rc1, 2026-10-29):
1. bench-check's hold checks assume a still ball. A live fly turned it −1108° in 8 s, the
display followed FicTrac with 0.00 % idx mismatch, and the QC table said FAIL. New
qclib.hold_verdict(): when ONLY hold lines failed and closed-loop-report shows an epoch with
|net turn| >= 45°, the row is SKIP naming the turns; a hold miss on a still ball, or any other
bench-check failure, stays FAIL. ww18's run/reports look text says so. Tests in test-rig-qc.py.
2. Teardown on Windows left the graph's nodes and the router as orphans ("graph stopped", then
"ports free again" FAIL on 7447/8765/60000). qc.py stops rig.py with
rig_nodes.supervise.signal_group(SIGINT), which on Windows is CTRL_BREAK_EVENT on the process
group; Python delivers that as SIGBREAK, which rig.py did not handle, so it died before its
`finally` stopped the children. rig.py now registers SIGBREAK where it exists (bridge_node and
host_monitor already did). QC.md documents both.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mbreiser
added a commit
that referenced
this pull request
Oct 29, 2026
…f a tier on a rig PC (LAB-222, LAB-338)
mbreiser
added a commit
that referenced
this pull request
Oct 29, 2026
…a whole-epoch hold Michael on the rc1 QC: with FicTrac live the ball can always move, so a check that the display HOLDS frame 25 / 75 for a whole epoch is the wrong test on a rig. What the two epochs exist to verify is the v0.91 start frame: the first post-tare index equals the trialParams frame_index (closed-loop-start-frame.md). closed-loop-report now emits `first_idx_after_tare` + `opens_on_start_frame` per epoch (and marks "200 @25 ✓" in the table); qc.py records "epochs open on their start frame" from it; the rest of the epoch is the existing idx-mismatch check. The ww18 profile drops its `holds` (bench-check still judges completion, arena acks, channel, build; `holds` stays available for a bare or blocked ball, with hold_verdict's SKIP when the ball turned). Tests in test-closed-loop-report.py cover the open on 25 / 75 and a ≤ 3.3 bridge opening on 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mbreiser
added a commit
that referenced
this pull request
Oct 29, 2026
…f a tier on a rig PC (LAB-222, LAB-338)
mbreiser
added a commit
that referenced
this pull request
Oct 29, 2026
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.
Summary
One command on a rig PC validates a webDisplayTools tier end to end in about five minutes of wall time, with the person at the rig only confirming what they see. It replaces the hour of hand-driven QC of 2026-10-27/28 (
docs/development/handover-qc-windows-2026-10-26.md; results on #274).What it does (
rig/qc/qc.py): preflight (pixi ≥ 0.80, ports 7447/8765/60000 free, the rig's services up) → the tier'sbuild.json→ a scratch git worktree at exactly that SHA with its own pixi environment (pixi install --locked, plustyped/vizper rig; the rig's checkout and.pixiare never touched) → the candidate'spixi run rigwith the rig's flags (flags that build does not know are dropped with a warning) → FicTrac started or found running, frames counted at the gateway, simulator fallback →tests/studio_emulator.pypre-check (a broken candidate fails here, before any browser) → Chrome (a QC profile with no course token) on the tier'sarena_studio.html?mode=run&rig=…, driven over DevTools: build SHA + footer, protocol load, bridge connect, the one human click (Web Serial port picker), firmware + panels,Run experiment→pixi run export,runlog-check,closed-loop-report,bench-check→ teardown, pass/fail table,validation-note.mdand the exactpixi run tiers -- validate …line.rig/qc/profiles.toml— the per-rig rows (ww18, ww14, bergamo, mac: flags, protocol, FicTrac command, Foxglove layout, required ports, report holds) and the checklist text Michael reads at each step. A new rig is a row.rig/qc/qclib.py— the pure half, pinned bytests/test-rig-qc.py(77 checks, runs in the rootpixi run test).rig/QC.md— the procedure page: what to type on ww18 / ww14 / Bergamo, the two clicks, where artifacts land (C:\qc\<date>\), safety on a rig PC, adding a rig.rig/pixi.toml— theqctask. The branch includes rig: pin rig-nodes 0.3.0 + re-lock with pixi 0.83 (#280, LAB-338) #281 (rig-nodes 0.3.0 re-lock) so the rig workspace installs on pixi 0.83.Tested on this Mac against candidate 2026-10-28-rc2 (
06bad57) on/next/: PASS end to end in 26–35 s wall with a warm cache — graph (router, fictrac, host, bridge, stimulus, viz, foxglove), FicTrac-MacOS on the looped sample video (50 fps), emulator pre-check, Chrome shows build06bad57/ v0.100 with the protocol loaded and 51 frames/s at the bridge, Foxglove opened onws://localhost:8766, export + bag closed + closed-loop report 0 % mismatch.--tier prodon main8d5b952fails cleanly at the install step (main's rig lock predates pixi 0.80, #280 — fixed by today's release). The controller path (click → firmware/panels →Studio.runOnce(true)→ bench-check) could not run here (no controller); the first exercise is the ww18 dry run.Finding on the way: a never-used Chrome profile holds
ws://localhost:8765from the Pages origin on the Local Network Access permission prompt (Chrome 155: no error, "connecting…" forever). A DevToolsBrowser.grantPermissions localNetworkAccessdoes not unblock it;--disable-features=LocalNetworkAccessChecksdoes (the flag the Foxglove desktop app itself runs with), so the QC Chrome uses it;--lna-promptshows the prompt instead.Linear: LAB-222 (two-tier releases — the validation step), LAB-338 §7 item 8 (the cross-repo smoke on a rig PC). No HTML, no footer, no release-notes file touched; nothing Prettier-formatted.
Tier
next candidate (rig-side tooling + scripts only).
Release notes
none
Test plan
pixi run test(root): 73/73 files, incl. the newtests/test-rig-qc.pycd rig && pixi run qc -- --tier next --rig mac --autoend to end on this Mac (rc206bad57)--no-browser, interactive mode (piped Enter + a remark),--tier prodfailure pathpixi run qc -- --tier next --rig ww18) — controller path, real FicTrac viafictrac.batpixi run qc -- --tier next --rig ww14) — BIAS connector pre-check, four-color smokeFirst run on ww18 (candidate 2026-10-29 rc1, 2026-10-29)
pixi run qc -- --tier next --rig ww18 --autoran end to end in 104 s: candidate checked out and installed, graph + real FicTrac (96 fps), emulator pre-check PASS, Studio on /next/ with build f49f34f, controller connected on Michael's click, run completed, export, bag, runlog-check and closed-loop-report (0.00 % idx mismatch) all PASS. Two findings, fixed on this branch:first_idx_after_tare/opens_on_start_frameper epoch (the first post-tare index must be the trialParamsframe_index, 25 / 75), qc.py records it, and the rest of the epoch is the idx-mismatch check. The ww18 profile dropsholds;holdsstays for a bare or blocked ball, whereqclib.hold_verdict()turns a hold miss into SKIP when the ball turned.signal_group(SIGINT)is CTRL_BREAK_EVENT on Windows, delivered to Python as SIGBREAK, whichrig.pydid not handle: it died before itsfinally, leaving the nodes and the router running ("ports free again" FAIL).rig.pyregisters SIGBREAK now.Also: the bergamo profile runs the public
g6_2x10_smokeprotocol while the 2P diagnostics protocol lives in the private repo (LAB-227).🤖 Generated with Claude Code