You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
tools/post_cal_confirm.py (post-cal verdict tool, consumed by tests/regression/test_post_cal_confirm.py)
tools/cal_trace/ replay tool (consumed by tests/test_cal_trace.py)
All four are tied to the deletion-track SMART pipeline. The replacement is a NEW sphere-model corpus that exercises (profile, home_anchor, homeSecondary, rotation, target_xyz) → expected (panDmx16, tiltDmx16). This child issue specs the corpus shape; it does NOT write the corpus content.
Architectural ground rules
The acceptance-gate concern survives — every PR that touches the aim/cal pipeline must pass an offline corpus run before merge (per CLAUDE.md ## Cal-pipeline change checklist (#733)).
The DRIVER (tools/emulate_smart_pipeline.py) and the CORPUS (tests/fixtures/cal/corpus.json) both delete and are replaced.
The replacement targets aim/sphere.py::AimSphere directly. No SMART probe loop, no camera, no DMX wire.
Inventory
KEEP
None — every existing tool/corpus in this tier is on the deletion track.
REWRITE
None — the spec is a fresh write rather than a rewrite of existing content.
DELETE
File
LOC
Concern
Confirmed deletion target
PR gate
Replacement
tools/emulate_smart_pipeline.py
279
SMART pipeline emulator: coverage_math.coverage_polygon, working_area, sample_grid, solve_dmx_per_degree, angles_to_dmx against the cal corpus.
YES — entire SMART pipeline + coverage_math IK funcs delete.
PR-7
NEW tools/emulate_aim_sphere.py (or tools/sphere_corpus_runner.py) — see ADD.
If repurposed for post-Save-Home wizard verification, REWRITE the tool (re-target inputs to homePanDmx16 / homeTiltDmx16 / homeSecondary / aimSphere). If purely SMART: DELETE.
tools/cal_trace/ (replay tool)
—
NDJSON cal-trace replay (#686). Consumed by tests/test_cal_trace.py + tests/test_cal_trace_v2.py.
LIKELY YES
PR-7
If repurposed for wizard diagnostics: REWRITE. Otherwise DELETE.
# tools/sphere_corpus_runner.py"""Sphere-model corpus runner — #784 PR-7 acceptance gate.Replaces tools/emulate_smart_pipeline.py. Loads the case batteryfrom tests/fixtures/aim/sphere_corpus.json and assertsAimSphere outputs match the per-case expectations (withindocumented tolerances).Exit codes: 0 — every case passes its expectations. 1 — at least one case violates an expectation.Run: python tools/sphere_corpus_runner.py [--verbose]CI: tests/regression/test_sphere_corpus.py wraps this."""
The runner instantiates AimSphere(fixture_stub, profile_stub, step=128) per case and exercises aim_xyz / aim_direction against the case expectations. Pure offline — no Flask, no camera, no DMX wire.
DELETE rows + ADD rows all land in PR-7. The new corpus + runner replace the old corpus + emulator in the same PR; the regression-suite wrapper updates in the same PR.
CLAUDE.md ## Cal-pipeline change checklist updates with the new runner path.
Acceptance
python tools/sphere_corpus_runner.py --verbose exits 0 against tests/fixtures/aim/sphere_corpus.json.
The 15 case categories above are all populated in the corpus (with at least one case each).
CLAUDE.md updated to reference the new runner path.
tools/emulate_smart_pipeline.py and tests/fixtures/cal/corpus.json deleted.
Open questions for human review
Does tools/post_cal_confirm.py get repurposed for Save-Home wizard verification, or deletes? Need an operator decision; affects whether tests/regression/test_post_cal_confirm.py survives (Tier 4 cross-reference).
Same question for tools/cal_trace/ (replay tool).
Does the corpus runner need to exercise the HTTP /api/mover/<fid>/aim route, or only the in-process AimSphere class? Spec above defaults to in-process (cheaper, no Flask spin-up). HTTP variant could be a regression-tier ADD instead.
Is step=128 the right default for the corpus, or should the corpus declare per-case step? Default is fine for most cases; flag if a case needs step=32 for tight tolerance.
Scope
Tier 7 of the post-#784 test-suite overhaul. Covers offline tools and the synthetic corpus consumed by the cal-pipeline emulator:
tools/emulate_smart_pipeline.py(279 LOC, process(#720): emulator + /smart/preview degeneracy checks must be acceptance gates on every cal-pipeline PR — SMART shipped without updating the emulator and bugs landed in production #733 acceptance gate)tests/fixtures/cal/corpus.json(synthetic-fixture corpus)tools/post_cal_confirm.py(post-cal verdict tool, consumed bytests/regression/test_post_cal_confirm.py)tools/cal_trace/replay tool (consumed bytests/test_cal_trace.py)All four are tied to the deletion-track SMART pipeline. The replacement is a NEW sphere-model corpus that exercises
(profile, home_anchor, homeSecondary, rotation, target_xyz) → expected (panDmx16, tiltDmx16). This child issue specs the corpus shape; it does NOT write the corpus content.Architectural ground rules
## Cal-pipeline change checklist (#733)).tools/emulate_smart_pipeline.py) and the CORPUS (tests/fixtures/cal/corpus.json) both delete and are replaced.aim/sphere.py::AimSpheredirectly. No SMART probe loop, no camera, no DMX wire.Inventory
KEEP
None — every existing tool/corpus in this tier is on the deletion track.
REWRITE
None — the spec is a fresh write rather than a rewrite of existing content.
DELETE
tools/emulate_smart_pipeline.pycoverage_math.coverage_polygon,working_area,sample_grid,solve_dmx_per_degree,angles_to_dmxagainst the cal corpus.coverage_mathIK funcs delete.tools/emulate_aim_sphere.py(ortools/sphere_corpus_runner.py) — see ADD.tests/fixtures/cal/corpus.json{profile_id, fixture_xyz, fixture_rotation, home_anchor, secondary, target_xyz}→ expected SMART invariants.tests/fixtures/aim/sphere_corpus.json— see ADD.tools/post_cal_confirm.pyhomePanDmx16/homeTiltDmx16/homeSecondary/aimSphere). If purely SMART: DELETE.tools/cal_trace/(replay tool)tests/test_cal_trace.py+tests/test_cal_trace_v2.py.ADD — replacement architecture
tests/fixtures/cal/corpus.json. Drivesaim/sphere.py::AimSpheredirectly.tests/fixtures/aim/sphere_corpus.jsontools/emulate_smart_pipeline.py. Loads corpus, instantiatesAimSphereper case, asserts expectations.tools/sphere_corpus_runner.pytests/regression/test_smart_pipeline_emulator.pywith the equivalent thin shell around the new runner.tests/regression/test_sphere_corpus.py## Cal-pipeline change checklist (#733)mentionstools/emulate_smart_pipeline.py— update to point at the new runner.Corpus shape
{ "schemaVersion": 1, "description": "Sphere-model corpus — replaces tests/fixtures/cal/corpus.json post-#784 PR-7.", "cases": [ { "name": "fid17-back-right-marker", "fixture": { "id": 17, "x": 600, "y": 0, "z": 1760, "rotation": [0, 0, 0], "homePanDmx16": 44364, "homeTiltDmx16": 0, "homeSecondary": { "panMovedDirection": "right", "tiltMovedDirection": "down", "panOffsetDmx16": 10922, "tiltOffsetDmx16": 32768 } }, "profile": { "id": "movinghead-150w-12ch", "panRange": 540, "tiltRange": 180 }, "target_xyz": [500, 2280, 0], "expected": { "panDmx16_within": [44560, 44760], "tiltDmx16_within": [13550, 13860], "az_deg": -2.51, "el_deg": -37.64, "tolerance": { "pan_dmx16": 200, "tilt_dmx16": 250, "deg": 2.0 } } }, { "name": "upright-fixture-multi-valued-prefer-A", "...": "see notes below" } ] }Each case carries:
AimSphere(x/y/z/rotation/homePanDmx16/homeTiltDmx16/homeSecondary).id/panRange/tiltRangeplus optionaldmxToMechanical).target_xyz(drivesaim_xyz) orazDeg/elDeg(drivesaim_direction).panRange/tiltRangeand a 2° cell).Required cases (initial battery)
The corpus must contain AT LEAST these cases to lock down the bug surface:
aim_xyz(target_xyz)andaim_direction(stage_az, stage_el)byte-equivalent (fix(#784 PR-3): aim/sphere.py — 2°-cell precomputed lookup, bilinear bracketing-cell blend, clipped-target nearest-row fallback #785 Bug 5).aim_xyz(target_xyz)with identical inputs return identical DMX (fix(#784 PR-3): aim/sphere.py — 2°-cell precomputed lookup, bilinear bracketing-cell blend, clipped-target nearest-row fallback #785 Bug 3 —current_poseplumbing).aim_xyz(fixture_xyz)returns None (fix(#784 PR-3): aim/sphere.py — 2°-cell precomputed lookup, bilinear bracketing-cell blend, clipped-target nearest-row fallback #785 Bug 4).prefer="A"/prefer="B"— distinct branches, deterministic.current_pose— branch locks to current pose forprefer="closest".rotation = [0, 180, 0]— same world target → mirrored DMX (the post-feat: SMART cal + mover-aim — single canonical IK, per-probe contract, lamp-on contract (consolidates #746, #747, #749, #760, #779) #780 P1 invariant).homeSecondary.panMovedDirection = "right"+ offset > 0 — sign derivation:pan_sign = -1(per fix(#784 PR-3): aim/sphere.py — 2°-cell precomputed lookup, bilinear bracketing-cell blend, clipped-target nearest-row fallback #785 sphere-cloud finding).homeSecondary.tiltMovedDirection = "down"+ offset > 0 — sign derivation:tilt_sign = -1.homeSecondary—AimSphereconstruction raises"fixture <id> has no homeSecondary direction calls + offsets".[0, 65535]for both axes.Runner shape
The runner instantiates
AimSphere(fixture_stub, profile_stub, step=128)per case and exercisesaim_xyz/aim_directionagainst the case expectations. Pure offline — no Flask, no camera, no DMX wire.Existing-tools tier audit
tools/devgui/server.pytools/cal_trace/(if exists)tools/post_cal_confirm.pytools/docs/build.py(manual builder)PR sequencing
## Cal-pipeline change checklistupdates with the new runner path.Acceptance
python tools/sphere_corpus_runner.py --verboseexits 0 againsttests/fixtures/aim/sphere_corpus.json.python tests/regression/test_sphere_corpus.pyexits 0 (subprocess wrapper).tools/emulate_smart_pipeline.pyandtests/fixtures/cal/corpus.jsondeleted.Open questions for human review
tools/post_cal_confirm.pyget repurposed for Save-Home wizard verification, or deletes? Need an operator decision; affects whethertests/regression/test_post_cal_confirm.pysurvives (Tier 4 cross-reference).tools/cal_trace/(replay tool)./api/mover/<fid>/aimroute, or only the in-processAimSphereclass? Spec above defaults to in-process (cheaper, no Flask spin-up). HTTP variant could be a regression-tier ADD instead.step=128the right default for the corpus, or should the corpus declare per-case step? Default is fine for most cases; flag if a case needsstep=32for tight tolerance.Cross-references
## Cal-pipeline change checklist (#733)will need an update in the same PR.reference_smart_cal_emulator.md(will rotate to a new memory entry post-PR-7).