feat: publish why a field has no reading: unavailable now, or undeclared for this radio - #3415
Conversation
…red for this radio `FieldStatusPublic.availability` gains `unavailable` (the profile declares the field and its `available_when` clauses do not hold in this state) and `undeclared` (the profile declares no acquisition entry for the path). `missing` keeps its old meaning: declared, admitted here, not yet observed. The projection takes the two new inputs as optional keyword arguments, so every caller that passes neither is unchanged. `WebServer._build_public_ state_from_snapshot` derives them from `self._get_profile(). state_acquisition` through `runtime_helpers.snapshot_field_status_inputs`, which is also what the tests call. An OBSERVED field keeps `available`/`stale` even where its clauses now read False: `StateFreshnessService._discard_declared_absent` removes it, and only then does the projection seed the absence. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…on doc enumerates the set Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… and re-pin what the change falsified `FieldStatusPublic.availability` now carries `unavailable` and `undeclared` alongside `missing`. Five frontend gates compared against `'missing'` alone and so flipped from hidden to shown under either new value: - `props/panel-props.ts: activeFieldShown` (rfGain/squelch/att/preamp); - `props/panel-props.ts: toModeProps.hasModInput`; - `adapters/mod-input-auto.svelte.ts: deriveAutoLanModInputProps`; - `adapters/mod-input-auto.svelte.ts: autoSetLanModInputForTx`; - `adapters/mod-input-tx-guard.svelte.ts: offendingSource`. All five mean "the backend has read it", so they now call one helper, `state/field-status.ts: isFieldRead` (`available` or `stale`). Each site has a case pinning hidden under both new values; mutating each site back to its old comparison reddens exactly those cases. `tests/test_web_server_coverage.py`: the two IC-7610 tests asserting `powerOn` is `missing` are re-pinned to `undeclared` and renamed — the profile names `global.tx_state.power_on` in neither `field_policies` nor `capabilities`. Prose corrected where it stated `missing` as the only absence, or named the test this branch renamed: `acquisition_scheduler.py: StateFreshnessService._discard_declared_absent`, `state_store.py: StateStore.discard`, three comments in `web/runtime_helpers.py`, `docs/architecture/field-path-promotion-criterion.md`, `docs/internals/radio-state-pipeline-validation.md` (the row's covering tests and the definition of `undeclared`), and `frontend/src/lib/state/field-status.ts`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Agent Review: BLOCKED aff969e Independent whole-candidate review: runtime changes and prior B1–B5 corrections pass the focused checks below. Two newly added/touched documentation claims remain false; these require wording corrections, not behavior changes (repository CLAUDE.md prose-accuracy rule).
Independent evidence:94 focused projection/schema/fixture Python tests plus2 B1 server-coverage cases PASS;142 committed frontend cases plus5 separate helper compatibility cases PASS. Reverted each of the five B2 sites individually in the reviewer worktree: M1–M5 each produced exactly2 expected assertion failures, then source bytes were restored. Eight real-profile empty-snapshot projections preserve all non-status raw/default fields and never manufacture observed evidence; IC705 declaration union and TX500 no-profile fallback confirmed. False/None gating, observed-value retention and freshness discard are covered by the passing projection cases. Both generators are clean. No full suite or hardware run; reviewer tree restored clean. CI as found: quick running, visual successful, Agent Review Gate awaiting a valid final verdict. After the prose-only correction, review only that delta and generator result; the source/mutation review need not be repeated. The coordinator-approved19-file exception is present. Meter/scan structural follow-up remains outside this PR. |
|
Agent Review: BLOCKED 2414855 Delta-only review: the two previous documentation classes and PR-body claim are corrected, with no runtime changes. One replacement sentence reverses the condition: Required: delete that clause, or describe the control as using |
|
Agent Review: PASS a78e2c9 Final delta-only review: the inverted panel-props comment is corrected. It now accurately describes isFieldRead as an availability compatibility gate, preserving the legacy no-entry fallback without claiming observation evidence. The prior optional-input schema/generated-doc correction also stands. Runtime behavior is unchanged from independently reviewed aff969e; no source or mutation tests were repeated for these wording-only deltas. Prior independent evidence:96 Python and147 frontend checks PASS; reverting each of the five compatibility gates separately caused exactly2 expected failures; eight-profile projection/raw-payload compatibility and both generators verified. The two review finding classes are resolved. Meter/scan consumers and hardware acceptance remain separate. REQUIRED BEFORE MERGE — PR body only: its current census still says554 additions/73 deletions=627. The final diff is555 additions/74 deletions=629 across19 files. Update that sentence; the approved19 code+0 regenerated baselines exception remains accurate. No source commit is needed. CI as found: fresh visual and consumer/type/grep gates running; quick was not yet listed. Exact-head required-check completion remains coordinator-owned. Head rechecked immediately before publication. |
|
Required before merge correction completed: the PR body now reports the exact final diff as 19 files, 555 additions, 74 deletions, 629 changed lines at a78e2c9. GitHub body readback confirms the old 554/73/627 count is absent. No source push was needed for this body-only correction. |
Coordinator-delegated file-count exception: 19 code + 0 regenerated baselines = 19 files.
Linear owner: MOR-2425. This PR delivers the T201 wire-availability slice recorded under that root; T201 does not have a separate Linear child issue.
Problem
The public
fieldStatuswire contract previously collapsed three unobserved states intoavailability: "missing": a field the radio does not declare, a declared field unavailable in the current state, and a declared field that has not been observed yet. Presentation consumers therefore could not make the T201 distinction between structural presence and a reading.Result
FieldStatusPublic.availabilityand its generated TypeScript union now includeunavailableandundeclared.available_whenresolving toFalseorNoneemitsunavailable; a projected path outside the declared set emitsundeclared; an unconditional declared path that has not been observed remainsmissing.availableorstaleresult until the freshness service discards them. Callers without profile acquisition metadata retain the previous all-missingfallback.WebServerpasses the profile-derived declaration and availability inputs into the projection.isFieldReadcentralizes the compatibility rule that resolvedavailableandstalevalues remain admissible whilemissing,unavailable, andundeclaredare rejected. It preservesgetFieldAvailability's legacy no-entry fallback and does not establish observation evidence. It updates five legacymissing-only gates: Mode panel active-field visibility and MOD-input presence, automatic MOD-input derivation and dispatch, and the TX MOD-input warning source. This prevents the two new absence values from making controls visible or dispatch-admissible.missing/unavailable/undeclaredstates, schema conformance, no-profile compatibility, freshness discard, server wiring, inherited frontend status, and all five legacy gates.The 19-file exception keeps one compatibility unit together: emitting the new wire values without converting the five legacy gates would change control visibility and dispatch authority. The PR remains below the 1000 changed-line ceiling at 555 additions and 74 deletions (629 total), measured with
git diff --numstat origin/main...HEADafter the review wording correction.Recovery and integration provenance
The existing public branch head was
feedc9a77127c2f8817fad9a1a5e1e59e0f854aa. A clean unpublished Mini commit was recovered locally as531d760537baa943873bbbeb5895c0196935aa6f, a direct descendant that supplies the five-gate compatibility correction and associated tests/docs. The integration branch preserves both commits and uses a normal merge of freshly fetchedorigin/main(b43d11443d052920072a5f4987e179c763b0b95f); no rebase or force push was used.Focused verification
uv run pytest tests/test_web_runtime_helpers.py tests/web/test_state_schema_conformance.py tests/test_field_status_frontend_fixture.py -q --tb=short --timeout=300 --timeout-method=thread— 94 passed.uv run pytest tests/test_web_server_coverage.py -k 'empty_state_store or partial_state_store' -q --tb=short --timeout=300 --timeout-method=thread— 2 passed, 153 deselected.uv run python scripts/gen_state_types.py --check— generated type block is current.uv run python scripts/gen_field_status_fixture.py --check— fixture is current and unchanged for the no-profile fallback.cd frontend && npx --no-install vitest run src/lib/state/__tests__/field-status.test.ts src/lib/runtime/props/__tests__/panel-props.test.ts src/lib/runtime/adapters/__tests__/mod-input-auto.isolated.test.ts src/lib/runtime/adapters/__tests__/mod-input-tx-guard.isolated.test.ts— 4 files, 142 tests passed.git diff --check origin/main...HEAD— clean.The behavior checks above ran on
aff969eb0b71e186b1484de0a97956f647d45c7d. Review then required a wording-only correction: document that either projection input can independently emit its corresponding absence and describeisFieldReadas a compatibility predicate rather than observation proof. The generated type check andgit diff --checkwere rerun after that correction; runtime code did not change.The full suite was intentionally not run in this recovery lease; required CI runs on the published exact head.
Follow-up boundary
This PR publishes wire truth and preserves the existing control gates. It does not implement the meter/scan structural consumer. That follow-up remains in draft PR #3414 and must be revised to consume availability without collapsing
unavailable,missing, andundeclared. Reconnect/store/DOM work, presentation expansion, and hardware/radio acceptance are also outside this PR and were not completed here.