feat(device): DeviceFeature requirement table + Supports() seam (closes #256) - #389
Conversation
#256) ADR 0001 deferred the version-gating layer until daqifi-core consumed a command introduced above the v3.5.0 floor. PR #347 did exactly that (SD file transfer over WiFi, firmware >= v3.7.0) with a bespoke inline version compare, so this generalizes it into the seam the ADR specified. - DeviceFeatureRequirements: static DeviceFeature -> FeatureRequirement table (min firmware version, board allow-list, hardware flags), seeded from ADR 0001's firmware audit and covering all four current features. SdOverWifiMinFirmware moves here from DaqifiStreamingDevice. - DaqifiDevice.Supports(DeviceFeature) / EnsureSupported(DeviceFeature), evaluated live against Metadata so board and firmware version can never be snapshotted apart. Version gating fails closed (absent/unparseable -> unsupported, preserving #347's rule); board and hardware requirements are evaluated only once DeviceType is known, because FromDeviceType(Unknown) yields all-false defaults meaning "not yet known", not "hardware absent". - Both hand-rolled gates now route through the seam: EnsureSdFileTransferSupportedOnTransport keeps only the transport predicate, and the SD-storage-query -113 backstop builds its typed exception from the table. No bespoke feature checks remain. - Table-driven tests across every axis (below/at/above min, absent, unparseable, Int32-overflow, pre-release, wrong board, missing hardware, unknown board) plus a table-exhaustiveness guard. - ADR 0001: implementation note recording that Supports() landed on the device rather than DeviceCapabilities (which never sees the firmware version) and the two axes' differing failure rules. The #327 CONFigure:CAPabilities:JSON? reader stays deferred. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deriving "one version below the minimum" by decrementing a component can produce an unparseable string (e.g. for a 3.0.0 minimum), which Supports() rejects via the fail-closed path rather than the comparison under test — the assertion would still pass, for the wrong reason. Spell out a real released version on each side of every boundary and assert the "below" datum still parses. Collapses three per-boundary theories into one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR Summary by QodoAdd DeviceFeature requirements table and Supports() feature-gating seam
AI Description
Diagram
High-Level Assessment
Files changed (5)
|
Code Review by Qodo
1.
|
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 59572ff |
…at failed Qodo review on #389. EnsureSupported built FeatureNotSupportedException with the table's MinVersion no matter which axis failed, but Supports() also fails on the board allow-list and on hardware flags. A Nyquist1 on firmware v3.7.0 with no SD card was therefore told "Requires firmware >= 3.7.0; the device reports '3.7.0'" — self-contradictory, and pointing at an upgrade that cannot fix a missing card. Route both Supports() and EnsureSupported() through one EvaluateSupport() that reports which axis failed, and report a required version only for a version failure. The -113 wire backstop keeps attributing the failure to the version deliberately: there the firmware itself says it does not know the command, so the table's minimum is the actionable answer. Also make the board allow-list ImmutableArray<DeviceType>. The table hands the same instance to every caller, and InternalsVisibleTo already exposes it to the test assembly, so a mutable array could silently re-gate a feature process-wide (same reasoning the team accepted in #318). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Response to Qodo reviewBoth findings accepted and fixed in 8666481. 1. Misleading firmware requirement (🐞 Bug, Correctness) — fixed. Genuine bug. 2. Mutable requirements arrays (🐞 Bug, Maintainability) — fixed. Not done: extending Verification
|
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 8666481 |
The implementation note covered how the two axes evaluate but not what the typed exception is allowed to claim. That is now a tested contract, so the design record should state it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes #256.
Why now
ADR 0001 deferred the version-gating layer until daqifi-core consumed a command introduced above the v3.5.0 floor. #347 did exactly that — SD file transfer over WiFi, gated at firmware >= v3.7.0 — implemented as a bespoke inline version compare in
DaqifiStreamingDevice. That was the trigger condition, so this generalizes it into the seam the ADR specified and routes the existing gates through it.What changed
DeviceFeatureRequirements(new, internal) — a staticDeviceFeature -> FeatureRequirementtable carrying a minimum firmware version, a board allow-list, and hardware flags. Seeded from ADR 0001's firmware audit and covering all four current features:AnalogOutput(NQ3-only, no version gate),SdStorageQuery(floor + SD hardware),CapabilityDocument(v3.5.0),SdFileTransferOverWifi(v3.7.0 + SD + WiFi).SdOverWifiMinFirmwaremoved here fromDaqifiStreamingDevice.DaqifiDevice.Supports(DeviceFeature)/EnsureSupported(DeviceFeature)— evaluated live againstMetadataon every call, never cached, since the board variant and firmware version arrive in separate status-message branches.Both hand-rolled gates now route through the seam.
EnsureSdFileTransferSupportedOnTransport()keeps only the transport predicate (which feature applies over WiFi vs. USB) and delegates the support question; the SD-storage-query-113backstop builds its typed exception from the table viaCreateFeatureNotSupportedException. No bespoke feature checks remain inDaqifiStreamingDevice.The one design call worth reviewing
The two axes fail differently, deliberately:
DeviceTypeisUnknown,FromDeviceTypehas not run andCapabilitiesholds all-falsedefaults that mean "not yet known", not "hardware absent". Reading them as violations would newly refuse SD-over-WiFi on a device that has merely not reported its part number yet — a behavior regression against feat(sd): allow SD file transfer over WiFi on firmware >= v3.7.0 #347. The wire-level-113remains the backstop for that window. The cost asymmetry justifies the split: a DAC command on an NQ1 returns a clean SCPI error, whereas an SD command over WiFi on old firmware stalls the bus.Supports()landed onDaqifiDevice, not onDeviceCapabilitiesas ADR 0001 sketched —DeviceCapabilitiesis board-derived and never sees the firmware version, so it structurally cannot answer a version gate. This also matches where its siblings already live (MinSupportedFirmware,IsFirmwareVersionSupported,Metadata), none of which are onIDevice. The ADR is updated with an implementation note recording both decisions.Testing
DeviceFeaturegrows.TreatWarningsAsErrors.--sd-list(20 files, exit 0),--sd-storage(exit 0). Both refactored call sites exercised on real hardware. The below-gate SD-over-WiFi branch only exists on pre-v3.7.0 firmware and is covered by unit tests, not the bench.Out of scope
Part 2 of #256 — the
CONFigure:CAPabilities:JSON?/:APIVersion?device self-description reader (firmware #327) — stays deferred, as the issue specifies.🤖 Generated with Claude Code