Repository navigation
feat(MOR-4274): one scope session — demand, view negotiation and a single CI-V writer - #4522
Conversation
|
Agent Review: PASS faa83b4 |
faa83b4 to
c7388e1
Compare
|
Agent Review: PASS c7388e1 |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inputs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace SDK and web lifecycle writers with one ScopeSession. Negotiate fixed edges from profile data, pause output without releasing views, and restore the exact pre-session state after the final owner leaves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lth regression Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Full rebased fast run exposed restore replay and builder AST parity regressions; retained as RED evidence before their correction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… display Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gression Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ution Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tartup Capture dual state only for requested SUB streaming and retain MAIN-only lifecycle commands. Keep recoverable startup reconciliation failures available for the existing retry driver while the command executor starts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Autospec ScopeSession instead of maintaining a hand-shaped scope writer. Assert every granted field through the real decoder while retaining the bounded queue, latest-wins delivery and clean teardown checks for both profiles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep cached frames and selected geometry when an update is refused; the server preserves the previous subscription. Startup errors still clear provisional grants, and provider/session retirement still clears retained frames. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c7388e1 to
b286f0d
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use geometry retirement for sample cleanup and the existing toolbar status slot for negotiation errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Agent Review: PASS 1271cad |
Size exception: owner 2026-10-21 (limit 4000 lines); file count waived by the coordinator (owner delegation 2026-09-03)
Change class: feature
Boundary: open-core candidate
49 code + 0 regenerated baselines = 49 files.
Linear: MOR-4274
Scope subscriptions, SDK controls, readiness/reconnect, TX deferral and shutdown now share one acknowledged
ScopeSession. Paused clients retain their subscription and view while output turns off; the first active client resumes output once. Last release restores the original panel/output and the view settings and fixed slots that the session changed.RadioScopeSourcebinds directly to the session. Absolute views use profile-backed FIXED edges within the tuned receiver’s band where supported; other views use the smallest covering CENTER span, with uncovered coverage reported by the existing hub contract. Health uses typed frame arrival and acknowledged output facts: zero/empty/out-of-range arrivals do not imply a hidden scope.This is one delivery contract: switching the writer requires migrating SDK, Core RX/lifecycle, hub, reconnect and teardown together and deleting the old paths. There is no compatibility adapter or second session API.
Acceptance and failing-first evidence
The original acceptance RED identifiers below refer to the pre-rebase commits on which those failures were observed; the final branch retains their rebased test changes. Round-2 identifiers are mapped explicitly in the Pre-review section.
Tests were reconciled and committed before the production GREEN commit. The independent baseline archive is
37eef242d8911a990cd9938899439ecb2eb5b250; production edits were never copied into that archive. Final test paths below replace the initial root-level test paths.tests/runtime/test_scope_session_wire.py::test_real_scope_writer_emits_one_enable_per_transport95599673a: both transports fail on duplicate enable on baselinetest_last_release_restores_wire_snapshot_once,tests/runtime/test_scope_session.py::test_one_enable_exact_restore_oncea65b052df: six baseline failures across three original states and two transportstests/test_rig_loader.py::TestScopeFixedEdgeRanges,tests/test_scope.py::TestScopeCommandBuilders,tests/scope/test_radio_scope_source.py95599673a: profile/command RED; IC-7610 plus IC-7300/IC-705 coveragetest_deferred_enable_and_tx_have_no_writes,test_enabled_requires_acknowledgement,test_known_tx_defers_sdk_scope_wire_writesa65b052dftest_oor_is_arrival_never_hidden,test_missing_frames_after_oor_use_the_arrival_deadline,tests/web/test_scope_hub_channel.py::test_typed_oor_and_uncovered_arrivals_never_publish_hidden40f4babaatest_pause_only_output_resume_once_and_release_restores,test_server_simulator_pause_resume_release_counts_scope_writes,tests/test_web_server_coverage.py::test_health_snapshot_cannot_delay_fresh_scope_resume95599673a; readiness-resume RED at1feceb9deBaseline selected run: 30 FAIL / 43 PASS. Independent restore/TX baseline run: 7 FAIL. New-session collection RED is structural evidence, not claimed as behavioral baseline evidence. Extra inherited-implementation REDs pin initial-paused demand, pending-view release, shared fixed slots, unnecessary restoration, lost ACK/cancellation, health and explicit-receiver snapshot behavior. All behavioral assertions from both earlier session test sets are retained in one module; duplicates were removed.
The first local review reported five P1 and three behavior-changing P2 findings. Corrections add headless SDK TX-OFF/reconnect driving through existing Core events; preserve selected receiver/cache during addressed readbacks; feed session health from Core frame publication even with paused web demand; consume readbacks/stream observations and bypass uncertain no-op decisions; restore the command-binding test assertions; require fresh VERIFY activity; restrict FIXED to the tuned band; and retain the health/reconcile loop after recoverable command failures. Shared fixed-slot readback invalidation is covered as a sibling case.
Review corrections have real SDK + local WebSocket regressions in
tests/runtime/test_scope_session_review.py. Corrected tests replayed against an isolated3695fcfafarchive fail on receiver selection, false hidden health, real disconnect/connect demand recovery and control-WebSocket reapplication after a front-panel readback. Initial fixture-authentication failures and a soft-reconnect no-op were not counted as behavioral RED. Additional exact REDs cover monitor survival and cross-receiver fixed-slot readback. Test commits precede the correction GREEN commit.Deleted paths and search proof
Deleted from this PR:
_record_scope_demand;_scope_demand_is_stale;_set_radio_scope_output;_execute_scope_command;_scope_reenable_recorded_for;_cancel_scope_reenable_task;_scope_menu_guard_active;_enable_hardware_scope; reconnect inline enable;_schedule_scope_enable_when_ready/_wait_and_enable_scope; SDR-stale fallback enable; poller EnableScope requeue; poller_enable_scope_sessionandrestore_scope_session; serialdisable_scopeoverride;latest_scope_demand_generation,_scope_demand_generation,_scope_reenable_generation;WebRadioScopeWriter; hardcoded_SCOPE_FIXED_EDGE_RANGE_STARTS_HZ/_resolve_scope_fixed_edge_range._scope_disable_gracewas already absent at the R4 base; the new session accepts an injected grace parameter.The public SDK
restore_scope_session_statedelegates into the same actor. ScopeHub's pre-existing callback generation remains for frame callback currency; it does not decide scope demand/readiness.Search before write:
scope enable/disable/sessionandscope demand/restore/ready: nearest existing implementations were the separate runtime SDK and web poller/session helpers.ScopeSessionreplaces those owners; the generic hub remains responsible for subscriptions and delivery.fixed edge/rangeandspan/profile/band: nearest mapping was the removed command-level IC-7610 constant. Typed profile data extends the existing loader and command-builder inputs; no model branch is introduced.scope arrival/hiddenandscope health/silence: existing native frame callback and health monitor are reused; only the visibility verdict moves into the session's acknowledged state.lock/gateandscope restore/enable:core/priority_exchange.py: PriorityExchangeGateorders individual CI-V exchanges; it has no subscriber/view/original-state contract. The session lock serializes those multi-command state changes while retaining the existing exchange mechanism.subscriber count/activeandpaused/demand:ScopeHub._notify_demandsupplies counts;ScopeDemandtypes that boundary rather than adding another demand owner.ScopeSessionProviderdeclares the existing radio-to-web binding, andScopeSessionHealthtypes the replacement visibility verdict.scope setting frame/buildanddeferred configure/validate:_write_scope_settingalready used the profile-bound command builders;_scope_setting_framereuses that construction for validation before accepting desired values and for the sole writer. No second validation table is introduced.simulator scope setter/ACKandrecorded scope dispatch/readback:ScopeControlSimulatorextends the existing LAN simulator through Core parsers; the default recorded fixture is unchanged.Profile source and compatibility
Only IC-7610 declares FIXED ranges: official IC-7610 CI-V reference,
IC-7610_ENG_CI-V_4.pdf, printed page 15 (PDF page 16), command27 1E. Its 12 ranges and 1 kHz edge precision are cited inrigs/ic7610.toml. Other profiles remain CENTER-only without a cited range table. FIXED programming and touched slot restoration are ACK-based; this is simulator acceptance, not physical-radio acceptance.API behavior changes are documented in
docs/api/scope.md: SDKFASTnow waits for command ACKs;VERIFYadditionally waits for observed scope activity.disable_scopereleases SDK ownership and restores only when the final owner leaves. Unbound fixed-edge builders require profilerangesor an explicitrange_index; bound runtime setters supply profile data. Custom hardware providers used by the web server must supplyScopeSessionProvider. New profile fields are keyword-only, preserving existing positional constructor arguments.Pre-review
Two local pre-review rounds were completed before this fix brief. Round 1 found five P1 and three behavior-changing P2 findings;
80bcefaadfixes their Core lifecycle/RX/health/observation, binding-test, VERIFY, band-selection and monitor-survival causes. Round 2 found four P1 and three behavior-changing P2 findings. All seven are addressed below. The owner explicitly requested no third pre-review; the coordinator owns the PR verdict. No CLEAN or Agent Review PASS is claimed for this head.RED failures were observed on the Mac mini before each GREEN change. The observed RED SHA is pre-rebase; the corresponding rebased test commit is also given. The final integrated tree includes
origin/mainat8e54424ca(#4518 and #4516).test_sub_fixed_slot_capture_preserves_main_selected_edge88e997c2f→d32a627b32ff4ae461test_control_ws_reapplies_span_after_real_front_panel_readbackc76e7699b→b2571cc44d183c2183test_standalone_control_ws_setter_retires_after_ack0aeb0c3dc→d2d7e9ec2eae27ea56test_typed_oor_and_uncovered_arrivals_never_publish_hidden(3 cases)b24bd0968→864ee6f37744b0a08atest_retained_sdk_scope_resumes_after_late_serial_identity(2 cases)69684ce36→c61c1a383c8294870ftest_scope_selection_return_refreshes_stream_display_facts(2 cases)43e6dc2c2→d97b4e51f72bc2cb4dtest_invalid_deferred_setting_cannot_poison_later_work(6 cases)8638de0af→8d746d6ecc0a7545ebThe RED outcomes were, respectively: MAIN edge 2 instead of 1; reapply timeout with hardware span still 4; monitor replay to span 2 instead of retaining panel span 4; missing session arrival; scope output never resumed; stale mode/span after returning to MAIN; and invalid requests returning without ValueError. Fixture-address/identity setup failures encountered while constructing the serial test were corrected before recording its behavioral RED.
Gate round 1
Final candidate:
faa83b41da59f30e8bd45c84ed0821a3a9ca6efc; tested tree:2be31933ddc1f257d1d1db565cffe9a36ec0983e(identical before/after the final commit). 3 P1 + 1 P2 fixed; 3 P2 deferred to MOR-4322.732f16b8ee35672dbae56d44dda(dual-receiver fixture correction after6aff80319)877d0d5bb,faa83b41dae23d341f130f4a39784966b33dfaa83b41dRED evidence is in
test_panel_output_readbacks_allow_sdk_reenable,test_sub_view_real_dispatch_preserves_main_sdk_selection,test_successive_fixed_sessions_capture_fresh_panel_slot, andtest_startup_scope_failure_keeps_command_executor_alive(built-in timeout, Core timeout and CommandError). Initial full verification exposed nine serial lifecycle failures from an eager dual-state read;faa83b41dcaptures that state only when SUB demand needs it, preserving MAIN-only command sequences.Final Mac mini validation (Darwin host, outside containers):
mini-test -m "not slow" -n auto --maxprocesses 4 --tb=short --timeout=300 --timeout-method=thread --basetemp=/var/tmp/mor-4274-r5-round1-final-pytest: 27,897 passed / 274 skipped / 21 xfailed, zero failures, 8 warnings, 647.19 s.mypy --strict src/rigplane/web src/rigplane/runtime/scope_session.py src/rigplane/runtime/radio_scope_source.py: 32 files pass._civ_rx.pyand_scope_runtime.pyto strict mypy reports 24 diagnostics in two files under the existingfollow_imports=skippolicy. Shadow-file comparison with starting head56fdb0342gives exactly the same diagnostics after ignoring line shifts; this expanded command is not a passing gate. No typing configuration or suppressions changed.--checkpass. Native dry-run goldens: IC-7610 81, X6200 64, FTX-1 72 matching checks, zero regressions.Search-before-write:
read_dualsearchedget_scope_dual/scope_single_dual; nearest_read_scope_receiveronly captures receiver viewport settings, so the existing dual getter is supplied as a lazy global-state capture callback. Existing historical simulator measurements below were not recollected for these control/lifecycle fixes.The remaining P2 findings — selection deduplication without intervening frames, restoration within the shutdown budget, and CENTER half-span units — remain MOR-4322 and are unchanged. No subagents,
tmp/pre-review.sh, workflow dispatch/rerun, real-radio access or merge. The coordinator owns gate round 2 (last).Validation
Scope stress follow-up after #4523 rebase
Candidate:
c7388e18c95e89d134df531997b8beaad9190d2b. Onlytests/test_scope_stress.pychanges in this follow-up. RED reproduced at7d916c3376bffed68188525e53e76ee4c421e319: bothtest_scope_stress_slow_consumer_queue_boundedprofiles fail. The hand-written writer lacked thereleasekeyword andset_fixed; the IC-7610 response was an error message, not a malformed grant. The grant codec is unchanged.The replacement uses
create_autospec(ScopeSession, instance=True, spec_set=True), derives method signatures from the production session, and compares the realdecode_scope_grantedresult with all fields of a typedGrantedSubscription. The 400-frame queue/memory bound, latest-wins delivery, single blocked sender, output enable/release counts and clean teardown assertions remain.Mac mini verification (all commands run with
TMPDIR=/var/tmp/mor-4274; pytest uses--basetemp=/var/tmp/mor-4274/pytest):mini-test -m slow tests/test_scope_stress.py tests/test_audio_stress.py tests/test_mock_icom_session.py tests/test_session_lifecycle_e2e.py: 5 passed, 29 deselected, 17.35 s.mini-test -m "not slow" -n auto --maxprocesses 4 --tb=short --timeout=300 --timeout-method=thread -q: 27,913 passed / 274 skipped / 21 xfailed, zero failures, 8 deprecation warnings, 656.85 s. Both pytest commands usedmini-test --run bash -cto set the remote TMPDIR explicitly.mini-run uv run ruff check .: 4 TID251 errors, two internal rigctld imports in each of.github/scripts/private_core_b14_installed_smoke.pyand.github/scripts/private_core_installed_smoke.py.mini-run uv run ruff format --check .: 17 tooling files would be reformatted; 1,250 already formatted. The changed stress test passes both focused checks.mini-run uv run mypy src: 136 errors in 43 files, 365 files checked. These broad checks are not passing gates. Their diagnosed files, source/configuration inputs and formatting candidates are unchanged from the starting rebased head; no unrelated fixes or suppressions were added.Current aggregate: 2,988 additions + 1,043 deletions = 4,031 changed lines across 50 files. Earlier size and verification entries below describe their named historical candidates. No production behavior changed in this follow-up, so no new performance measurement was collected. The coordinator owns the delta review and size gate; no local pre-review, workflow dispatch/rerun, real-radio access or merge was performed.
Earlier Mac mini validation, rebased candidate:
56fdb0342cff5254255b46ff2b845623b4a4a00e; its tree7b59ca33b419b6103812d60165475d07eb852805exactly matches the tree tested before the final commit.mini-test --run bash -c 'PYTHONPATH=.github/scripts RIGPLANE_WORKER_DIAG_DIR="$HOME/validation/mor-4274-r5-review-fixes/worker-diags-final" uv run pytest tests/ -m "not slow" -p pytest_worker_diagnostics -n auto --maxprocesses 4 --tb=short --timeout=300 --timeout-method=thread -q': 27,892 passed / 274 skipped / 21 xfailed, zero failures, 635.39 s. Darwin host, outside containers.First rebased full run: 27,890 passed / two failures. It exposed redundant panel/output SETs after explicit restore and an AST test still looking inside the pre-extraction writer.
56fdb0342retains current confirmed restoration observations and pins both writer-to-builder and builder-to-command links with fail-closed mutation checks. The two failures plus session/wire/control-WS regressions then passed in a 104-test focused run, followed by the clean full run above. The first full run is retained as RED at tree-identical commitba0c0e91e(formatting-only freeze).Ruff check and format check: all 47 changed Python files pass.
mypy --strict src/rigplane/web src/rigplane/runtime/scope_session.py src/rigplane/runtime/radio_scope_source.py src/rigplane/scope/hub.py src/rigplane/scope/models.py: 34 files pass. Simulator extension: strict check with--follow-imports=silentpasses. Including the extension under the repository defaultfollow_imports=skipproduces seven Any-import diagnostics; that command configuration is not reported as a clean check.lint-imports: six contracts kept.Regenerated wire goldens with
scripts/gen_state_wire_goldens.py; byte-identical. Wire goldens, field-status fixture and state types--checkall pass. No fixture or frontend changes, so local Vitest is not selected.Size: 2969 additions + 1024 deletions = 3993 changed lines across 49 files, no generated exclusions. One cohesive session/writer migration; owner size exception above applies.
No new pre-review run, workflow dispatch/rerun, real-radio connection or merge.
Natural required PR CI and
Agent Review Gateare reported separately at the exact head. No workflow dispatch or rerun was requested; no merge is authorized.Simulator measurements
Baseline
37eef242d8911a990cd9938899439ecb2eb5b250; AFTER56fdb0342cff5254255b46ff2b845623b4a4a00e. Mac mini, simulator at 30 fps, receiver 0, requested width 1024, identical recorded waveform geometry and initial panel-on/output-off state. Both runs isolate simulator, Core and v2 client in separate processes.AFTER latency range: 93.849–162.787 ms. Paused phase delivered zero pixel frames. CPU is lower in this sample; one run does not establish causality. AFTER also includes the two rebased main commits. Delivery requested width 1024 and was granted native width 689. The final full test run ended before the benchmark began. Pause includes one output-off and two span writes. Static recorded geometry can disagree with programmed span, limiting viewport-write/CPU comparison. Delivery remains approximately 30 fps. Disconnect includes restoration of the original span plus output-off; the original panel was already on.
Commands:
mini-run uv run python <private-r5-harness> <output-directory>(10 connect trials plustop -l 31 -s 2 -pid <core_pid> -stats pid,command,cpu,idlew, first sample excluded); the companion transition harness captures actual parsed SET frames;mini-run uv run python qa/bench/dogfood/scope_fps.py --radio simulator --transport sim --duration 30 --max-fps 30 --width 1024measures delivery.Exact final benchmark command (the frozen harness internally uses the top command above):
mini-run bash -c 'set -e; out="$HOME/validation/mor-4274-r5-after/56fdb0342"; uv run python "$HOME/validation/mor-4274-r5-after/r5-bench-after.py" "$out"; uv run python "$HOME/validation/mor-4274-r5-after/r5-bench-transitions.py" "$HOME/validation/mor-4274-r5-after/r5-bench-after.py" "$out"; uv run python qa/bench/dogfood/scope_fps.py --radio simulator --transport sim --duration 30 --max-fps 30 --width 1024 > "$out/delivery.json"; uv run python "$HOME/validation/mor-4274-r5-after/analyze-after.py" "$out"; cat "$out/delivery.json"'Baseline testkit tree:
b68843d1d3414406ce2f7607bd78abf11721496d; AFTER testkit tree:b19cc4af0703e370d559cc115d426ff51aede050. The original recorded simulator blob is unchanged (5df023b7827b2c621bc8fed8933ce0ecc287e7c8); AFTER adds the typed scope-control extension (3c77641cda526e3edb12d58c3a8c9ee8bc1eed01). Full captures and top samples remain on the mini; the lane result records their locations.Baseline already sent output-off on pause; its extra panel writes and duplicate enable paths are the measured redundancy. Baseline viewport setters were NAKed by the recorded fixture, so its viewport counts are attempted writes. AFTER uses the ACK-capable simulator extension with unchanged recorded waveform geometry and the same initial panel-on state; successful physical view actuation is not established by this comparison.
CPU is the Core process only (simulator/client excluded), not browser/GPU CPU. No real radio, installed-build or hardware validation was attempted. IC-705 and IC-7300 were untouched. No frontend or golden fixtures changed, so standalone Vitest is not selected locally; natural PR CI remains authoritative for its selected paths.
🤖 Generated with Claude Code
Journey regression
Rebased onto R3/main
273dcf42ae265fc894435ee6a826c6e25641c4a6. Main: 16 journeys passed. Rebased R5: 15 passed, 1 failed, reproducing the MAIN pixel loss without retries.Cause/fix: R5 fixed-edge negotiation receives a CI-V NAK from the simulator's limited controls.
ScopeHandler._handle_controlpreserves the previous subscription on rejection (docs/api/scope.md), but frontendScopeStream.handleMessagecleared all cached frames/geometry, unmounting MAIN during SUB traffic. It now preserves active pixels/geometry and reports the error; startup, null-frame and session/identity retirement remain tested. R3 rendering and journey expectations are unchanged.bca4ec3ef81f0f03799d0c480ae766f5d452f604,scope-subscription-v2.svelte.isolated.test.ts→keeps MAIN pixels and geometry after a receiver %i update is refused during SUB traffic: both receiver cases failed with null MAIN geometry (producer golden fixtures + real decoder).b286f0de37928e03c32c427aed90ef87ccede3f5. Journeys 16 passed; full Vitest 15,223 passed + 1 expected failure (599 files). Lint, i18n, type checks, build, component-package verification passed. Fixture captures 65/65 valid; placeholder 16, chrome-inset 85, FTX-1 RIT 1, Chromium/WebKit renderer 10 each passed. Missing WebKit provisioning was resolved. Three obsolete audio-fixture error-retirement assertions were corrected; null-frame/disconnect assertions remain.Aggregate 3,023 + 1,046 = 4,069 changed lines / 53 files; delta 35 additions + 3 deletions / 3 files. Existing size exception retained. Coordinator owns delta/size review; no pre-review script, workflow dispatch, real radio or merge. No hot-path/performance mechanism changed; prior metric evidence retained.
P1 correction after the final Codex round
Final head:
1271cadacfa71f47e8b9ba34b463ba03b8e34596(tree02fd973f0345145a5d188c43aa8a8da6c2f02ee1).SpectrumPanelno longer treats every negotiation error as sample retirement. Its existing geometry/scopeTraceLivepath still retires the sample and renderer; a refused update with retained geometry preserves both. The panel regression verifies changing MAIN payloads reach the same mounted spectrum after SUB frames, then verifies actual retirement still unmounts it.StatusIndicator, with English/Russian/Japanese catalog text. The same status snippet is reachable when the toolbar is hidden or the panel is audio FFT. A later accepted grant clears the notice.Searched:
status/error/scopeStatusIndicatorandnotification/toast/notice; nearest existing:SpectrumToolbar.scopeStatusIndicatorandlib/Button/StatusIndicator.svelte; used that slot/component and the existing stream error subscription, without a second retirement mechanism. Deletion grep:rg -n 'scopeStream.error.subscribe.*clearSample' frontend/src/components/spectrum/SpectrumPanel.sveltehas no matches.RED:
0af70a6ba5b1917907d4ffcb3fde8fb90d5977ae, tests against unchanged product code fromb286f0de37928e03c32c427aed90ef87ccede3f5. Bothscope negotiation refusal (MOR-4274)panel tests failed: the mounted renderer received no subsequent MAIN paint, and the refusal status was absent. The real-serverMOR-3513 mixed scope preserves MAIN while PCM and controls advancejourney received changing MAIN pixels but recorded 0 spectrum advances and noScope view refusedstatus. Its opt-in simulator now alternates amplitude payloads through the normal CI-V sender, so a frozen image cannot satisfy the advancement assertion. The final journey awaits rendered advancement before its snapshot and also requires the refusal to be visible.Final-head Mac mini verification (all with remote
TMPDIR=/var/tmp/mor-4274; artifacts under~/validation/mor-4274-r5-p1/):npx playwright test -c playwright.journey.config.ts: 16 passed. Mixed-scope witness: 18 MAIN wire changes, 4 spectrum advances, 24 SUB-only checks, 0 pixel losses, 0 mount/geometry losses.npx vitest run --maxWorkers=4: 15,225 passed + 1 expected failure, 599 files passed.npm run lint,i18n:check,check,build, andverify:component-kit-api: passed; svelte-check: 0 errors / 0 warnings.mini-test --run bash -c 'export TMPDIR=/var/tmp/mor-4274; uv run pytest tests/ -m "not slow" -n auto --maxprocesses 4 --basetemp=/var/tmp/mor-4274/pytest-p1-final --tb=short --timeout=300 --timeout-method=thread -q': 27,913 passed / 274 skipped / 21 xfailed / 8 deprecation warnings, 655.19 s. The journey fixture is the only Python change; its Ruff lint/format checks pass.Correction delta from
b286f0de3: 92 additions + 6 deletions = 98 changed lines / 7 files. Aggregate: 3,115 additions + 1,052 deletions = 4,167 changed lines / 60 files; the coordinator retains the size/delta gate. No new performance measurement was collected for this refusal-handling correction. No pre-review script, subagents, workflow dispatch, real-radio access or merge was performed.