Skip to content

feat(sd): allow SD file transfer over WiFi on firmware >= v3.7.0 - #347

Merged
tylerkron merged 8 commits into
mainfrom
feat/sd-file-transfer-over-wifi
Jul 18, 2026
Merged

tylerkron merged 8 commits into
mainfrom
feat/sd-file-transfer-over-wifi

Conversation

@cptkoolbeenz

Copy link
Copy Markdown
Member

Summary

SD file operations (LIST / GET / DELETE) were hard-gated to USB by a blanket if (!IsUsbConnection) throw on DownloadSdCardFileAsync, citing SD/WiFi shared-SPI-bus contention. That contention was real — but only until firmware v3.7.0: firmware #598/#599 (commit bf105585, first released tag v3.7.0 per git tag --contains) route the SD reply to the requesting interface, making SD-over-WiFi safe on v3.7.0+.

This replaces the transport-only block with a firmware-capability gate, following the existing ADR 0001 framework.

Changes

  • DeviceFeature.SdFileTransferOverWifi — min firmware v3.7.0; needs WiFi + SD HW.
  • EnsureSdFileTransferSupportedOnTransport() — over USB, always allowed on SD-capable firmware; over WiFi/TCP, requires firmware ≥ v3.7.0 (unparseable/older reported version → unsupported) and throws a typed FeatureNotSupportedException(SdFileTransferOverWifi, v3.7.0, actual, board) up front rather than dispatching a command the firmware can't service over WiFi (which would stall on the shared SPI bus).
  • Applied to GetSdCardFilesAsync (LIST), DownloadSdCardFileAsync (GET), DeleteSdCardFileAsync (DELETE) — all three depend on chore: flatten nested guard checks in WifiFlashProgressParser #598's reply-routing. LIST was previously ungated over WiFi (silent stall on old firmware); now gated too.
  • ADR 0001 living-evidence updated: timeline extended through v3.7.2; SD-over-WiFi row added; the "no live above-floor gate today" note corrected — this is the first active above-floor firmware-version gate.

Behavior change

Over WiFi on old/unknown firmware, these operations now throw the typed FeatureNotSupportedException instead of (download) the blanket USB-only InvalidOperationException or (list) a silent stall. Over USB: unchanged (available on all SD-capable firmware).

Tests

New SD-over-WiFi gate coverage: at / above / below min + unparseable version; over USB not gated; LIST / GET / DELETE. Existing non-USB download test updated to the typed exception; RetryableSdCardStreamingDevice marked USB (its LIST retry/parse tests are transport-agnostic). Full suite: 1398 passed / 0 failed / 2 skipped (net10.0).

Context

Enables daqifi-avalonia #1 (SD offload over WiFi) — the app-side USB gate removal (daqifi-avalonia PR #2) was necessary but not sufficient; this Core guard was the other USB-only policy. Consumers pinning the NuGet package need a release to pick this up.

Note: the v3.7.0 threshold is derived from firmware git history (git describe bf105585 = v3.6.3-6, first containing tag v3.7.0). On-device SD:LIST? + SD:GET over TCP validation against a v3.7.x device is still recommended before release.

🤖 Generated with Claude Code

SD file operations (LIST / GET / DELETE) were hard-gated to USB by a blanket
`if (!IsUsbConnection) throw` on DownloadSdCardFileAsync, citing the SD/WiFi
shared-SPI-bus contention. That contention was real — but only until firmware
v3.7.0: firmware #598/#599 (commit bf105585, first released tag v3.7.0) route the
SD reply to the requesting interface, so SD-over-WiFi is safe on v3.7.0+.

Replace the transport-only block with a firmware-capability gate, following the
ADR 0001 feature-gating framework:

- New DeviceFeature.SdFileTransferOverWifi (min firmware v3.7.0, needs WiFi + SD HW).
- EnsureSdFileTransferSupportedOnTransport(): over USB, always allowed on
  SD-capable firmware; over WiFi/TCP, requires firmware >= v3.7.0 (an unparseable
  or older reported version is treated as unsupported) and throws a typed
  FeatureNotSupportedException(SdFileTransferOverWifi, v3.7.0, actual, board) up
  front rather than dispatching a command the firmware can't service over WiFi.
- Applied to GetSdCardFilesAsync (LIST), DownloadSdCardFileAsync (GET), and
  DeleteSdCardFileAsync (DELETE) for consistency — all three depend on #598's
  reply-routing. LIST was previously ungated over WiFi (would silently stall on
  old firmware); it is now gated too.

Behavior change: over WiFi on old/unknown firmware these now throw the typed
FeatureNotSupportedException instead of (download) the blanket USB-only
InvalidOperationException or (list) a silent stall. Over USB: unchanged.

ADR 0001 updated (living evidence): timeline extended through v3.7.2, SD-over-WiFi
row added, and the "no live above-floor gate today" note corrected — this is the
first active above-floor firmware-version gate.

Tests: +new SD-over-WiFi gate coverage (at/above/below min + unparseable, over
USB not gated, LIST/GET/DELETE); existing non-USB download test updated to the
typed exception; RetryableSdCardStreamingDevice marked USB (its LIST retry/parse
tests are transport-agnostic). Full suite 1398 passed / 0 failed / 2 skipped.

Enables daqifi-avalonia #1 (SD offload over WiFi); pairs with the app-side gate
removal. Requires a release for consumers pinning the NuGet package.

Claude-Session: https://claude.ai/code/session_01PK5697Y1eDH3DB75Tii5Nh
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Enable SD file transfer over WiFi on firmware v3.7.0+

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add firmware-gated support for SD LIST/GET/DELETE over WiFi/TCP on v3.7.0+
• Replace USB-only blocking with typed FeatureNotSupportedException on unsupported firmware
• Extend tests and ADR documentation to cover SD-over-WiFi capability gating
Diagram

graph TD
  A["Client / SDK consumer"] --> B["DaqifiStreamingDevice SD ops"] --> C["EnsureSdFileTransferSupportedOnTransport"] --> D{"USB or FW >= 3.7.0?"} --> E["Send SD SCPI (LIST/GET/DELETE)"]
  D -- "no" --> F["Throw FeatureNotSupportedException"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Capability/feature probing instead of version gating
  • ➕ Avoids relying on version string parsing/format consistency
  • ➕ Could adapt automatically if firmware behavior changes without new version floors
  • ➖ Older firmware may stall/hang on the exact commands being probed (the failure mode this PR avoids)
  • ➖ Requires a safe, always-responding probe mechanism that likely doesn’t exist for this feature
2. Allow WiFi universally with aggressive timeouts/cancellation
  • ➕ No explicit firmware floor; simpler API surface from a capability perspective
  • ➖ Still risks SPI-bus contention stalls and degraded device behavior on pre-v3.7.0 firmware
  • ➖ Timeouts add latency and can be flaky across networks, making failures less actionable
3. Opt-in override flag to bypass the gate
  • ➕ Gives advanced consumers a way to experiment or handle edge firmware builds
  • ➕ Maintains safe default behavior
  • ➖ Complicates API and support surface area
  • ➖ Easy to misuse and reintroduce stall-class failures in production

Recommendation: Keep the PR’s firmware-capability gate with fail-fast FeatureNotSupportedException. It directly addresses the known pre-v3.7.0 stall mode on WiFi/TCP while preserving existing USB behavior, and it aligns with the ADR feature-gating approach. If needed later, consider adding an explicit opt-in override only for diagnostics, but avoid probing/timeout-based approaches because they can trigger the same contention failure the gate is designed to prevent.

Files changed (4) +264 / -21

Enhancement (2) +58 / -7
DaqifiStreamingDevice.csGate SD LIST/GET/DELETE by transport + firmware (v3.7.0+ for WiFi) +47/-6

Gate SD LIST/GET/DELETE by transport + firmware (v3.7.0+ for WiFi)

• Adds SdOverWifiMinFirmware (3.7.0) and a centralized EnsureSdFileTransferSupportedOnTransport() check. Applies the guard to GetSdCardFilesAsync, DeleteSdCardFileAsync, and replaces the prior USB-only download block with the new firmware-aware gate that throws FeatureNotSupportedException on unsupported WiFi/TCP scenarios.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs

DeviceFeature.csIntroduce DeviceFeature.SdFileTransferOverWifi +11/-1

Introduce DeviceFeature.SdFileTransferOverWifi

• Adds a new feature flag representing SD file transfer over WiFi/TCP with a v3.7.0 minimum firmware requirement. Documents the historical USB-only limitation (shared SPI contention) and the post-v3.7.0 reply routing behavior enabling safe WiFi operation.

src/Daqifi.Core/Device/DeviceFeature.cs

Tests (1) +187 / -4
SdCardOperationsTests.csAdd comprehensive tests for SD-over-WiFi firmware gating +187/-4

Add comprehensive tests for SD-over-WiFi firmware gating

• Reworks the prior non-USB download rejection test to assert a typed FeatureNotSupportedException with required version. Adds coverage for LIST/GET/DELETE over WiFi at/above min firmware, below min firmware, and unparseable versions; also asserts USB is not firmware-gated. Introduces a non-USB SD-capable device double and marks retry/parsing tests as USB to avoid unrelated gating.

src/Daqifi.Core.Tests/Device/SdCard/SdCardOperationsTests.cs

Documentation (1) +19 / -10
0001-firmware-feature-gating.mdDocument SD-over-WiFi feature floor and timeline through v3.7.2 +19/-10

Document SD-over-WiFi feature floor and timeline through v3.7.2

• Extends the firmware release timeline and adds an explicit row for SD file transfer over WiFi (v3.7.0+). Updates the ADR narrative to reflect this as the first active above-floor firmware-version gate and points to the new transport gate behavior.

docs/adr/0001-firmware-feature-gating.md

@qodo-code-review

qodo-code-review Bot commented Jul 18, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Context used

Grey Divider


Action required

1. LAN enable drops TCP 🐞 Bug ☼ Reliability
Description
UpdateNetworkConfigurationAsync now sends EnableNetworkLan on non-USB transports before
SaveNetworkLan, but the codebase documents that re-enabling LAN over WiFi/TCP can re-initialize the
WiFi module and drop the active control connection. If the connection drops after EnableNetworkLan,
SaveNetworkLan may never be sent, leaving the new configuration unapplied/unpersisted (or causing
intermittent failures).
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R884-891]

+            // Re-enable the LAN interface after the reconfig. ApplyNetworkLan restarts the WiFi
+            // module, so this is a network-configuration step that OWNS the LAN state and must
+            // bring LAN back up regardless of the control transport. It deliberately does NOT call
+            // PrepareLanInterface() — that is the transport-aware SD-operation restore, which
+            // leaves the LAN alone over WiFi (where #598/#599 keep it up). Here the LAN enable is
+            // unconditional.
+            Send(ScpiMessageProducer.DisableStorageSd);
+            Send(ScpiMessageProducer.EnableNetworkLan);
Relevance

⭐⭐⭐ High

Team previously accepted “LAN toggle drops control channel” reliability guards; reorder/ensure Save
sent before LAN enable likely accepted.

PR-#153
PR-#83

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new code sends EnableNetworkLan before SaveNetworkLan. Elsewhere in the same class, the
documentation explicitly states that re-enabling LAN over WiFi/TCP would re-initialize the WiFi
module and drop the connection, which would interrupt this command sequence.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[884-894]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[941-946]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`UpdateNetworkConfigurationAsync(...)` re-enables LAN (`EnableNetworkLan`) unconditionally (including over WiFi/TCP) and only then sends `SaveNetworkLan`. The same file documents that re-enabling LAN over WiFi/TCP can re-initialize the WiFi module and drop the active TCP control channel, which can prevent the subsequent `SaveNetworkLan` from being delivered.

### Issue Context
- This path is explicitly intended to work over non-USB transports (tests were added to assert the command is sent).
- The goal is to keep the behavior (LAN ends up enabled) while ensuring persistence is not lost if the connection drops as a side effect of enabling LAN.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[884-894]

### Suggested fix direction
- Reorder so persistence is attempted **before** any command that can drop the control channel (e.g., send `SaveNetworkLan` before `EnableNetworkLan`), or otherwise make the sequence reconnect-aware / best-effort (e.g., persist first, then attempt enable, tolerating a disconnect after persistence).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. WiFi SD ops disable LAN ✓ Resolved 🐞 Bug ≡ Correctness
Description
SD file ops are now allowed on non-USB transports for firmware >= v3.7.0, but
DownloadSdCardFileAsync/GetSdCardFilesAsync/DeleteSdCardFileAsync still call PrepareSdInterface(),
which sends the LAN-disable SCPI command and is likely to tear down the active TCP control channel
mid-operation. As a result, the newly-enabled SD-over-WiFi path will likely disconnect/timeout even
on supported firmware.
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R1474-1477]

+            // Over WiFi/TCP this requires firmware >= v3.7.0 (#598/#599); over USB it is always
+            // available on SD-capable firmware. Older firmware over WiFi gets a typed
+            // FeatureNotSupportedException instead of the old blanket USB-only rejection (ADR 0001).
+            EnsureSdFileTransferSupportedOnTransport();
Relevance

⭐⭐⭐ High

PR #153 accepted: disabling LAN drops non-USB control; SD ops were USB-only for this reason—same
issue here.

PR-#153
PR-#159

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new gate allows non-USB SD file ops, but those ops still call PrepareSdInterface(), which
explicitly disables LAN communication. The codebase already treats disabling LAN as incompatible
with non-USB control (it hard-blocks SD logging over non-USB for this reason), so enabling SD file
transfer over WiFi without changing SD preparation is inconsistent and likely to disconnect the
control channel.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[962-978]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[990-1018]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1503-1513]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[911-924]
src/Daqifi.Core/Communication/Producers/ScpiMessageProducer.cs[716-730]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1249-1254]
PR-#153

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The PR allows SD LIST/GET/DELETE on non-USB transports when firmware >= v3.7.0, but the SD operations still run through `PrepareSdInterface()`, which unconditionally disables LAN (`SYSTem:COMMunicate:LAN:ENAbled 0`). Over a TCP/WiFi control connection this likely drops the control channel before the SD command sequence completes, breaking the feature the PR is introducing.

### Issue Context
- `EnsureSdFileTransferSupportedOnTransport()` only gates by transport+firmware version; it doesn’t change how the SD interface is prepared.
- `PrepareSdInterface()` currently disables LAN as part of SD access, which is consistent with the existing codebase’s prior assumption that network transports can’t perform SD ops safely.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[911-978]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[990-1051]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1349-1417]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1463-1540]

### What to change
1. Split SD preparation into two paths (or make it conditional):
  - **USB/serial path (legacy)**: keep disabling LAN + enabling SD, because transport is unaffected.
  - **TCP/WiFi path (FW >= 3.7.0)**: do *not* disable LAN; use whatever minimal SD-enable/bus-switch sequence is valid on >=3.7.0 (likely omitting `DisableNetworkLan` and possibly omitting `PrepareLanInterface()` restoration).
2. Ensure the finally/cleanup path doesn’t re-enable/disable network in a way that resets the active TCP connection.
3. Update unit tests for the success path over non-USB to assert that LAN-disable is **not** sent (so tests fail if a future change reintroduces the disconnect behavior).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Hardcoded min version in docs ✓ Resolved 🐞 Bug ⚙ Maintainability ⭐ New
Description
GetSdCardStorageAsync XML docs hardcode the SD-over-WiFi minimum firmware as “v3.7.0” instead of
referencing the authoritative threshold used by the code. If the minimum changes later (e.g.,
SdOverWifiMinFirmware), the public docs can silently drift from actual behavior.
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R1227-1229]

+        /// or, over a WiFi/TCP transport, when the firmware predates SD-over-WiFi support
+        /// (<see cref="DeviceFeature.SdFileTransferOverWifi"/>, firmware v3.7.0) — the storage query
+        /// drives the SD card through the same transport gate as the file operations.
Relevance

⭐⭐⭐ High

Team previously accepted removing hardcoded values/mismatched firmware mins in XML docs to prevent
drift (PRs #98, #288).

PR-#98
PR-#288

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The docs explicitly mention “firmware v3.7.0”, but the code defines and uses SdOverWifiMinFirmware
as the single source of truth for the gate and exception RequiredVersion, so duplicating the value
in docs risks divergence.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1224-1230]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1097-1105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The XML docs for `GetSdCardStorageAsync` embed the literal firmware version `v3.7.0` even though the gating logic is centralized in `SdOverWifiMinFirmware`. This invites documentation drift if the threshold ever changes.

### Issue Context
`EnsureSdFileTransferSupportedOnTransport()` compares against `SdOverWifiMinFirmware` and uses it as the `RequiredVersion` in the thrown `FeatureNotSupportedException`. The docs should avoid duplicating the value as prose.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1227-1229]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1097-1105]

### Expected change
Replace the hardcoded “firmware v3.7.0” wording with a reference like `(<see cref="SdOverWifiMinFirmware"/>)` or wording that ties the minimum to the shared threshold (so future changes only need to update one place).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Storage docs miss new throw ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
GetSdCardStorageAsync now calls EnsureSdFileTransferSupportedOnTransport(), which can throw
FeatureNotSupportedException(SdFileTransferOverWifi) on non-USB with old/unparseable firmware, but
the method XML docs only mention the SdStorageQuery undefined-header FeatureNotSupportedException
case. This makes the public API contract misleading for consumers handling exceptions.
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R1104-1110]

+            // The storage-space query drives the SD card through the same transport-aware
+            // PrepareSdInterface() as LIST/GET/DELETE, so it carries the identical SD-over-WiFi
+            // requirement: over WiFi it needs firmware >= v3.7.0 (#598/#599 SPI arbitration) — else
+            // it would access the SD card with the LAN still enabled on firmware that never learned
+            // to arbitrate the shared bus. Gate it up front for the same reason as its siblings.
+            EnsureSdFileTransferSupportedOnTransport();
+
Relevance

⭐⭐⭐ High

Repo often accepts fixes for misleading XML docs/exception contracts; similar doc-mismatch
corrections were accepted.

PR-#288
PR-#321
PR-#98

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The XML docs currently describe only the -113 undefined-header feature-not-supported case for the
storage query, but the newly added gate can throw a different FeatureNotSupportedException before
any command is sent.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1082-1092]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1104-1110]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`GetSdCardStorageAsync(...)` now has a new up-front transport/firmware gate that can throw `FeatureNotSupportedException(DeviceFeature.SdFileTransferOverWifi, required>=v3.7.0, ...)` on non-USB transports, but the method XML docs don’t document this new exception path.

### Issue Context
This change is user-visible in generated API docs and can affect callers that rely on documented exception behavior for control flow.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1082-1110]

### Suggested fix direction
- Update the XML documentation for `GetSdCardStorageAsync` to include that it may throw `FeatureNotSupportedException` when the active transport is non-USB and the reported firmware is older than/unparseable relative to v3.7.0 (SD-over-WiFi requirement), in addition to the existing `SdStorageQuery` undefined-header case.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. LAN restore skipped on TCP ✓ Resolved 🐞 Bug ≡ Correctness
Description
PrepareLanInterface() now only sends EnableNetworkLan when IsUsbConnection is true, but
UpdateNetworkConfigurationAsync calls PrepareLanInterface() specifically to “Re-enable LAN
interface” after ApplyNetworkLan. If UpdateNetworkConfigurationAsync is executed over a non-USB
transport, LAN will not be re-enabled (only DisableStorageSd is sent), potentially leaving the
device’s LAN interface down after reconfiguration and preventing reconnection.
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R951-954]

+            if (IsUsbConnection)
+            {
+                Send(ScpiMessageProducer.EnableNetworkLan);
+            }
Relevance

⭐ Low

History supports skipping LAN re-enable on non-USB to avoid dropping TCP; transport-aware interface
prep accepted (PR #153).

PR-#153
PR-#159

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new conditional in PrepareLanInterface prevents EnableNetworkLan from being sent on non-USB
transports, while UpdateNetworkConfigurationAsync and INetworkConfigurable’s contract explicitly
describe a post-APPLY step to re-enable LAN. This creates a behavior mismatch for non-USB network
configuration flows.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[949-955]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[878-886]
src/Daqifi.Core/Device/Network/INetworkConfigurable.cs[22-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`PrepareLanInterface()` is now transport-aware (only re-enables LAN on USB). However, `UpdateNetworkConfigurationAsync()` uses `PrepareLanInterface()` as part of its network-configuration workflow to re-enable LAN after `ApplyNetworkLan`. On non-USB transports, this no longer sends `EnableNetworkLan`, changing behavior for network reconfiguration.

### Issue Context
The SD-over-WiFi change correctly avoids toggling LAN during SD file ops over TCP. But `PrepareLanInterface()` is also used outside SD workflows (network configuration), where re-enabling LAN may still be required.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[878-886]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[949-954]

### Suggested direction
Option A (most explicit): In `UpdateNetworkConfigurationAsync`, replace `PrepareLanInterface()` with the minimal sequence required for that workflow (e.g., `Send(DisableStorageSd)` and then `Send(EnableNetworkLan)`), so network reconfiguration always restores LAN regardless of transport.

Option B: Split responsibilities: keep `PrepareLanInterface()` as “restore-after-SD-op” (transport-aware), and add a dedicated helper like `RestoreLanAfterNetworkConfig()` that unconditionally enables LAN when appropriate.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit dd2ea46

Results up to commit 1bc9e78


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. WiFi SD ops disable LAN ✓ Resolved 🐞 Bug ≡ Correctness
Description
SD file ops are now allowed on non-USB transports for firmware >= v3.7.0, but
DownloadSdCardFileAsync/GetSdCardFilesAsync/DeleteSdCardFileAsync still call PrepareSdInterface(),
which sends the LAN-disable SCPI command and is likely to tear down the active TCP control channel
mid-operation. As a result, the newly-enabled SD-over-WiFi path will likely disconnect/timeout even
on supported firmware.
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R1474-1477]

+            // Over WiFi/TCP this requires firmware >= v3.7.0 (#598/#599); over USB it is always
+            // available on SD-capable firmware. Older firmware over WiFi gets a typed
+            // FeatureNotSupportedException instead of the old blanket USB-only rejection (ADR 0001).
+            EnsureSdFileTransferSupportedOnTransport();
Relevance

⭐⭐⭐ High

PR #153 accepted: disabling LAN drops non-USB control; SD ops were USB-only for this reason—same
issue here.

PR-#153
PR-#159

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new gate allows non-USB SD file ops, but those ops still call PrepareSdInterface(), which
explicitly disables LAN communication. The codebase already treats disabling LAN as incompatible
with non-USB control (it hard-blocks SD logging over non-USB for this reason), so enabling SD file
transfer over WiFi without changing SD preparation is inconsistent and likely to disconnect the
control channel.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[962-978]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[990-1018]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1503-1513]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[911-924]
src/Daqifi.Core/Communication/Producers/ScpiMessageProducer.cs[716-730]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1249-1254]
PR-#153

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The PR allows SD LIST/GET/DELETE on non-USB transports when firmware >= v3.7.0, but the SD operations still run through `PrepareSdInterface()`, which unconditionally disables LAN (`SYSTem:COMMunicate:LAN:ENAbled 0`). Over a TCP/WiFi control connection this likely drops the control channel before the SD command sequence completes, breaking the feature the PR is introducing.

### Issue Context
- `EnsureSdFileTransferSupportedOnTransport()` only gates by transport+firmware version; it doesn’t change how the SD interface is prepared.
- `PrepareSdInterface()` currently disables LAN as part of SD access, which is consistent with the existing codebase’s prior assumption that network transports can’t perform SD ops safely.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[911-978]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[990-1051]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1349-1417]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1463-1540]

### What to change
1. Split SD preparation into two paths (or make it conditional):
  - **USB/serial path (legacy)**: keep disabling LAN + enabling SD, because transport is unaffected.
  - **TCP/WiFi path (FW >= 3.7.0)**: do *not* disable LAN; use whatever minimal SD-enable/bus-switch sequence is valid on >=3.7.0 (likely omitting `DisableNetworkLan` and possibly omitting `PrepareLanInterface()` restoration).
2. Ensure the finally/cleanup path doesn’t re-enable/disable network in a way that resets the active TCP connection.
3. Update unit tests for the success path over non-USB to assert that LAN-disable is **not** sent (so tests fail if a future change reintroduces the disconnect behavior).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 252579b


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Informational
1. LAN restore skipped on TCP ✓ Resolved 🐞 Bug ≡ Correctness
Description
PrepareLanInterface() now only sends EnableNetworkLan when IsUsbConnection is true, but
UpdateNetworkConfigurationAsync calls PrepareLanInterface() specifically to “Re-enable LAN
interface” after ApplyNetworkLan. If UpdateNetworkConfigurationAsync is executed over a non-USB
transport, LAN will not be re-enabled (only DisableStorageSd is sent), potentially leaving the
device’s LAN interface down after reconfiguration and preventing reconnection.
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R951-954]

+            if (IsUsbConnection)
+            {
+                Send(ScpiMessageProducer.EnableNetworkLan);
+            }
Relevance

⭐ Low

History supports skipping LAN re-enable on non-USB to avoid dropping TCP; transport-aware interface
prep accepted (PR #153).

PR-#153
PR-#159

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new conditional in PrepareLanInterface prevents EnableNetworkLan from being sent on non-USB
transports, while UpdateNetworkConfigurationAsync and INetworkConfigurable’s contract explicitly
describe a post-APPLY step to re-enable LAN. This creates a behavior mismatch for non-USB network
configuration flows.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[949-955]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[878-886]
src/Daqifi.Core/Device/Network/INetworkConfigurable.cs[22-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`PrepareLanInterface()` is now transport-aware (only re-enables LAN on USB). However, `UpdateNetworkConfigurationAsync()` uses `PrepareLanInterface()` as part of its network-configuration workflow to re-enable LAN after `ApplyNetworkLan`. On non-USB transports, this no longer sends `EnableNetworkLan`, changing behavior for network reconfiguration.

### Issue Context
The SD-over-WiFi change correctly avoids toggling LAN during SD file ops over TCP. But `PrepareLanInterface()` is also used outside SD workflows (network configuration), where re-enabling LAN may still be required.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[878-886]
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[949-954]

### Suggested direction
Option A (most explicit): In `UpdateNetworkConfigurationAsync`, replace `PrepareLanInterface()` with the minimal sequence required for that workflow (e.g., `Send(DisableStorageSd)` and then `Send(EnableNetworkLan)`), so network reconfiguration always restores LAN regardless of transport.

Option B: Split responsibilities: keep `PrepareLanInterface()` as “restore-after-SD-op” (transport-aware), and add a dedicated helper like `RestoreLanAfterNetworkConfig()` that unconditionally enables LAN when appropriate.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 30caedb


🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. LAN enable drops TCP 🐞 Bug ☼ Reliability
Description
UpdateNetworkConfigurationAsync now sends EnableNetworkLan on non-USB transports before
SaveNetworkLan, but the codebase documents that re-enabling LAN over WiFi/TCP can re-initialize the
WiFi module and drop the active control connection. If the connection drops after EnableNetworkLan,
SaveNetworkLan may never be sent, leaving the new configuration unapplied/unpersisted (or causing
intermittent failures).
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R884-891]

+            // Re-enable the LAN interface after the reconfig. ApplyNetworkLan restarts the WiFi
+            // module, so this is a network-configuration step that OWNS the LAN state and must
+            // bring LAN back up regardless of the control transport. It deliberately does NOT call
+            // PrepareLanInterface() — that is the transport-aware SD-operation restore, which
+            // leaves the LAN alone over WiFi (where #598/#599 keep it up). Here the LAN enable is
+            // unconditional.
+            Send(ScpiMessageProducer.DisableStorageSd);
+            Send(ScpiMessageProducer.EnableNetworkLan);
Relevance

⭐⭐⭐ High

Team previously accepted “LAN toggle drops control channel” reliability guards; reorder/ensure Save
sent before LAN enable likely accepted.

PR-#153
PR-#83

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new code sends EnableNetworkLan before SaveNetworkLan. Elsewhere in the same class, the
documentation explicitly states that re-enabling LAN over WiFi/TCP would re-initialize the WiFi
module and drop the connection, which would interrupt this command sequence.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[884-894]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[941-946]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`UpdateNetworkConfigurationAsync(...)` re-enables LAN (`EnableNetworkLan`) unconditionally (including over WiFi/TCP) and only then sends `SaveNetworkLan`. The same file documents that re-enabling LAN over WiFi/TCP can re-initialize the WiFi module and drop the active TCP control channel, which can prevent the subsequent `SaveNetworkLan` from being delivered.

### Issue Context
- This path is explicitly intended to work over non-USB transports (tests were added to assert the command is sent).
- The goal is to keep the behavior (LAN ends up enabled) while ensuring persistence is not lost if the connection drops as a side effect of enabling LAN.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[884-894]

### Suggested fix direction
- Reorder so persistence is attempted **before** any command that can drop the control channel (e.g., send `SaveNetworkLan` before `EnableNetworkLan`), or otherwise make the sequence reconnect-aware / best-effort (e.g., persist first, then attempt enable, tolerating a disconnect after persistence).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational
2. Storage docs miss new throw ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
GetSdCardStorageAsync now calls EnsureSdFileTransferSupportedOnTransport(), which can throw
FeatureNotSupportedException(SdFileTransferOverWifi) on non-USB with old/unparseable firmware, but
the method XML docs only mention the SdStorageQuery undefined-header FeatureNotSupportedException
case. This makes the public API contract misleading for consumers handling exceptions.
Code

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[R1104-1110]

+            // The storage-space query drives the SD card through the same transport-aware
+            // PrepareSdInterface() as LIST/GET/DELETE, so it carries the identical SD-over-WiFi
+            // requirement: over WiFi it needs firmware >= v3.7.0 (#598/#599 SPI arbitration) — else
+            // it would access the SD card with the LAN still enabled on firmware that never learned
+            // to arbitrate the shared bus. Gate it up front for the same reason as its siblings.
+            EnsureSdFileTransferSupportedOnTransport();
+
Relevance

⭐⭐⭐ High

Repo often accepts fixes for misleading XML docs/exception contracts; similar doc-mismatch
corrections were accepted.

PR-#288
PR-#321
PR-#98

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The XML docs currently describe only the -113 undefined-header feature-not-supported case for the
storage query, but the newly added gate can throw a different FeatureNotSupportedException before
any command is sent.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1082-1092]
src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1104-1110]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`GetSdCardStorageAsync(...)` now has a new up-front transport/firmware gate that can throw `FeatureNotSupportedException(DeviceFeature.SdFileTransferOverWifi, required>=v3.7.0, ...)` on non-USB transports, but the method XML docs don’t document this new exception path.

### Issue Context
This change is user-visible in generated API docs and can affect callers that rely on documented exception behavior for control flow.

### Fix Focus Areas
- src/Daqifi.Core/Device/DaqifiStreamingDevice.cs[1082-1110]

### Suggested fix direction
- Update the XML documentation for `GetSdCardStorageAsync` to include that it may throw `FeatureNotSupportedException` when the active transport is non-USB and the reported firmware is older than/unparseable relative to v3.7.0 (SD-over-WiFi requirement), in addition to the existing `SdStorageQuery` undefined-header case.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread src/Daqifi.Core/Device/DaqifiStreamingDevice.cs
Two real defects the pre-merge review caught, both blocking the feature:

- Qodo "WiFi SD ops disable LAN" (Correctness, high): PrepareSdInterface()
  unconditionally sent DisableNetworkLan to free the shared SPI bus for the SD
  card — correct for USB, but over WiFi it tears down the very TCP channel that
  carries the SD reply (codebase precedent: PRs #153/#159). Firmware #598/#599
  disable the enable-level SPI mutex because the Harmony SPI driver arbitrates
  SD/WiFi transactions on the shared bus (app_freertos.c: "transaction-level
  arbitration", 7+ hours stable concurrent operation), and the reply routes back
  over the requesting interface. So PrepareSdInterface/PrepareLanInterface are now
  transport-aware: over WiFi the LAN is left enabled (only the SD subsystem is
  toggled); over USB the LAN disable/restore is unchanged.

- Audit "overflowing firmware strings crash the gate" (crash, low): the regex
  matches an unbounded digit run for the major component but FirmwareVersion.
  TryParse used int.Parse (throws) for major vs int.TryParse (safe) for
  minor/patch — a TryParse that violates its no-throw contract. A corrupted/
  spoofed DeviceFwRev like "999999999999999999.0.0" (untrusted wire string on the
  non-USB path the gate hardens) threw OverflowException instead of failing closed
  to FeatureNotSupportedException. Fixed at the root: major now uses int.TryParse
  and an out-of-range version is reported unparseable, symmetric with minor/patch.

Tests: WiFi PrepareSdInterface/PrepareLanInterface leave-LAN-alone coverage +
USB toggle regression; SD-over-WiFi LIST asserts LAN untouched; TryParse overflow
cases; NetworkConfigurableTests device marked USB (its interface-prep assertions
encode the USB sequence). Full suite 1404 passed / 0 failed / 2 skipped.

Claude-Session: https://claude.ai/code/session_01PK5697Y1eDH3DB75Tii5Nh
@cptkoolbeenz

Copy link
Copy Markdown
Member Author

/improve

@cptkoolbeenz

Copy link
Copy Markdown
Member Author

/agentic_review

@qodo-code-review

qodo-code-review Bot commented Jul 18, 2026 •

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to 30caedb

Warning

/improve is deprecated. Use /agentic_review instead (removal date not yet scheduled).

CategorySuggestion                                                                                                                                    Impact
Possible issue
Gate direct SD interface preparation

Add a firmware version guard in PrepareSdInterface() for non-USB connections.
This ensures direct callers cannot bypass the check and cause SPI contention on
older firmware.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs [930-935]

 if (IsUsbConnection)
 {
     Send(ScpiMessageProducer.DisableNetworkLan);
 }
+else
+{
+    EnsureSdFileTransferSupportedOnTransport();
+}
 
 Send(ScpiMessageProducer.EnableStorageSd);

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: Guarding the public PrepareSdInterface() method prevents external callers from inadvertently causing hardware contention on older firmware over WiFi by calling this method directly.

Medium
  • Update

Previous suggestions

Suggestions up to commit 4648cd2
CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent bypassing transport firmware checks

Add EnsureSdFileTransferSupportedOnTransport() to the beginning of
PrepareSdInterface(). This centralizes the firmware validation and guarantees
that all current and future SD operations enforce the transport gate.

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs [16-21]

+EnsureSdFileTransferSupportedOnTransport();
+
 if (IsUsbConnection)
 {
     Send(ScpiMessageProducer.DisableNetworkLan);
 }
 
 Send(ScpiMessageProducer.EnableStorageSd);
Suggestion importance[1-10]: 9

__

Why: Guarding the public PrepareSdInterface method directly prevents callers from bypassing transport checks, avoiding potential SPI bus contention on unsupported firmware.

High
Suggestions up to commit 252579b
CategorySuggestion                                                                                                                                    Impact
Possible issue
Reject every overflowing version component

Reject firmware versions entirely if their minor or patch components overflow,
rather than defaulting those components to zero.

src/Daqifi.Core/Firmware/FirmwareVersion.cs [8-13]

-if (!int.TryParse(m.Groups["maj"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var major))
+if (!int.TryParse(m.Groups["maj"].Value, NumberStyles.None, CultureInfo.InvariantCulture, out var major)
+    || !int.TryParse(m.Groups["min"].Value, NumberStyles.None, CultureInfo.InvariantCulture, out var minor)
+    || !int.TryParse(m.Groups["pat"].Value, NumberStyles.None, CultureInfo.InvariantCulture, out var patch))
 {
     return false;
 }
-var minor = int.TryParse(m.Groups["min"].Value, out var mi) ? mi : 0;
-var patch = int.TryParse(m.Groups["pat"].Value, out var pa) ? pa : 0;
Suggestion importance[1-10]: 2

__

Why: The suggestion risks breaking the parsing of valid versions that omit minor/patch components (which currently default to 0 safely via TryParse returning false), and defaulting to 0 on overflow for a minor/patch component does not actually bypass the >= 3.7.0 version gate.

Low

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 252579b

- Audit "storage query shares transport-aware prep but is ungated" (consistency,
  medium): making PrepareSdInterface/PrepareLanInterface transport-aware also
  changed GetSdCardStorageAsync (SD:SPACe?), which drives the SD card through the
  same helpers — over WiFi it now leaves the LAN enabled. But it had no
  SD-over-WiFi firmware gate, so on <v3.7.0 firmware it would access the SD card
  with WiFi still active on a bus that firmware never learned to arbitrate — the
  exact hazard the gate prevents for LIST/GET/DELETE. GetSdCardStorageAsync now
  calls EnsureSdFileTransferSupportedOnTransport() up front too. DeviceFeature
  doc broadened to cover the storage query (same #598/#599 SPI-arbitration
  requirement).

- Qodo "reject every overflowing version component" (consistency): the overflow
  fix made the MAJOR component strict but left minor/patch defaulting to 0 on
  overflow. Now all three go through a shared TryParseComponent(): an ABSENT
  optional minor/patch still defaults to 0 ("3" == 3.0.0), but a PRESENT
  component that overflows rejects the whole version rather than silently
  truncating.

Tests: +GetSdCardStorageAsync over-WiFi gate; +overflowing minor/patch reject.
Full suite 1408 passed / 0 failed / 2 skipped.

Claude-Session: https://claude.ai/code/session_01PK5697Y1eDH3DB75Tii5Nh
@cptkoolbeenz

Copy link
Copy Markdown
Member Author

/improve

@cptkoolbeenz

Copy link
Copy Markdown
Member Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

Persistent suggestions updated to latest commit 4648cd2

Qodo "LAN restore skipped on TCP" (Correctness): making PrepareSdInterface/
PrepareLanInterface transport-aware (leave LAN alone over WiFi) also affected
UpdateNetworkConfigurationAsync, which reused PrepareLanInterface() to re-enable
the LAN after ApplyNetworkLan. Over a non-USB transport the transport-aware
helper skipped EnableNetworkLan, so a network reconfig could leave the LAN down
and block reconnection.

These are two different intents sharing one helper: the SD-operation restore
(leave LAN alone over WiFi — #598/#599 keep it up) vs the network-config step
(ApplyNetworkLan restarts the WiFi module, so LAN must be brought back up
regardless of transport). UpdateNetworkConfigurationAsync now owns its LAN state
explicitly (DisableStorageSd + EnableNetworkLan unconditionally) instead of
relying on the transport-aware helper. PrepareSdInterface/PrepareLanInterface stay
transport-aware (correct for SD ops, which are now their only callers), and the
INetworkConfigurable docs are updated to describe the transport-aware behavior.

Test: +UpdateNetworkConfigurationAsync over non-USB still re-enables LAN.
Full suite 1409 passed / 0 failed / 2 skipped.

Claude-Session: https://claude.ai/code/session_01PK5697Y1eDH3DB75Tii5Nh
@cptkoolbeenz

Copy link
Copy Markdown
Member Author

/improve

@cptkoolbeenz

Copy link
Copy Markdown
Member Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

Persistent suggestions updated to latest commit 30caedb

Comment thread src/Daqifi.Core/Device/DaqifiStreamingDevice.cs
Comment thread src/Daqifi.Core/Device/DaqifiStreamingDevice.cs
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 30caedb

@cptkoolbeenz

Copy link
Copy Markdown
Member Author

Batch disposition of the open /improve suggestions (non-blocking tier; blocking tier is empty — Qodo 0 bugs + adversarial audit clean two rounds):

  • Reject every overflowing version component — already applied (commit 4648cd2): FirmwareVersion.TryParse now routes all three numeric parts through TryParseComponent; an absent optional minor/patch defaults to 0 ("3" == 3.0.0) but a present-but-overflowing one rejects the whole version.
  • Gate direct SD interface preparation / Prevent bypassing transport firmware checks — declined. Every caller of PrepareSdInterface() is one of the four SD operations (LIST/GET/DELETE/SPACe?), each of which already calls EnsureSdFileTransferSupportedOnTransport() up front (a clean fail before any stream/interface side effect). There is no ungated caller — unlike the storage-query path, which was reachable-and-ungated and got the gate in 4648cd2. PrepareSdInterface/PrepareLanInterface are generic INetworkConfigurable SPI-switch methods; coupling them to throw FeatureNotSupportedException would change their void contract to protect a direct caller that does not exist in the codebase. A follow-up can add it as pure defense-in-depth if a direct external caller is ever introduced.

Converged: blocking tier empty; this is the one non-blocking triage pass.

…-over-wifi

# Conflicts:
#	src/Daqifi.Core/Device/DaqifiStreamingDevice.cs
#	src/Daqifi.Core/Device/Network/INetworkConfigurable.cs
Qodo "Storage docs miss new throw" (Maintainability): GetSdCardStorageAsync now
throws FeatureNotSupportedException(SdFileTransferOverWifi) via the transport gate
(commit 4648cd2) but its XML docs only listed the SdStorageQuery/-113 case.
Broadened the <exception> doc so the public contract lists both firmware-gated
throw paths.

Declined "LAN enable drops TCP" (Reliability): behavior-neutral vs main. Before
this PR, UpdateNetworkConfigurationAsync called PrepareLanInterface() which
UNCONDITIONALLY sent EnableNetworkLan — so over WiFi the original already sent it
here; the explicit sends replicate that exactly (only PrepareLanInterface itself
became transport-aware, and this path no longer routes through it). The concern —
re-enabling LAN over WiFi can re-init the module before SaveNetworkLan — is
pre-existing: ApplyNetworkLan two lines earlier already restarts the WiFi module
and drops the control connection over WiFi. Reconfiguring the network over the
same WiFi link it reconfigures is inherently disruptive; hardening that flow
(e.g. save-before-reenable, or reconnect-and-retry) is a separate reliability
ticket, out of scope for the SD-over-WiFi enablement.

Claude-Session: https://claude.ai/code/session_01PK5697Y1eDH3DB75Tii5Nh
@cptkoolbeenz

Copy link
Copy Markdown
Member Author

Dispositions (commit 5f0346b):

  • Storage docs miss new throw (Maintainability) — fixed: GetSdCardStorageAsync's <exception> doc now lists both firmware-gated FeatureNotSupportedException paths (the -113 SdStorageQuery case and the new SdFileTransferOverWifi transport gate).
  • LAN enable drops TCP (Reliability) — declined, behavior-neutral vs main. Before this PR, UpdateNetworkConfigurationAsync called PrepareLanInterface(), which unconditionally sent EnableNetworkLan — so over WiFi the original already sent it in this exact spot; the explicit sends replicate it (only PrepareLanInterface itself became transport-aware, and this path no longer routes through it). The underlying concern is pre-existing: ApplyNetworkLan two lines earlier already restarts the WiFi module and drops the control connection over WiFi, so reconfiguring the network over the same link is inherently disruptive. Hardening that flow (save-before-reenable / reconnect-and-retry) is a separate reliability ticket, out of scope for SD-over-WiFi enablement.

@cptkoolbeenz

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread src/Daqifi.Core/Device/DaqifiStreamingDevice.cs Outdated
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 5f0346b

…on doc

Qodo "Hardcoded min version in docs" (Maintainability): the exception doc added
in 5f0346b stated "firmware v3.7.0" as a literal alongside the
<see cref="DeviceFeature.SdFileTransferOverWifi"/> reference. The feature enum's
own summary already documents "First released firmware v3.7.0", so the literal is
removed — the cref is the single doc source for the version.

Convergence: blocking tier empty (adversarial audit clean 3x; no correctness/
data-loss findings). Remaining Qodo items are non-blocking and dispositioned —
"LAN enable drops TCP" declined (pre-existing, behavior-neutral vs main) and this
doc nit fixed.

Claude-Session: https://claude.ai/code/session_01PK5697Y1eDH3DB75Tii5Nh
@cptkoolbeenz

Copy link
Copy Markdown
Member Author

Converged — ready for review-required merge. Blocking tier empty across the cycle: the adversarial pre-merge audit passed clean 3 consecutive rounds (0 confirmed findings, incl. the post-merge interaction surface), and no correctness / data-loss / action-required bug remains. Full merged suite 1610 passed / 0 failed / 2 skipped on current main.

Non-blocking findings, all dispositioned:

  • Storage docs miss new throw → fixed (5f0346b)
  • Hardcoded min version in docs → fixed (1f2575e — cref is the single version source)
  • LAN enable drops TCP → declined (pre-existing, behavior-neutral vs main; ApplyNetworkLan already restarts the WiFi module and drops the WiFi control link — hardening that reconfig flow is a separate ticket)

Not re-triggering the review loop further (anti-treadmill: remaining items are documentation-purity nits, not defects). Pre-release step still open: on-device SD:LIST?/SD:GET over TCP on a v3.7.x device before release, per the PR's must-verify note.

@tylerkron tylerkron left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving as code-owner (not the author). Basis: green CI, 7 rounds of Qodo, full unit coverage; verified locally against latest main (merge clean; 523 SD/WiFi/feature-gate tests pass on net9+net10) and bench-confirmed on live Nq1 (FW 3.7.2) that the USB SD path is unchanged (--sd-list returns files; the new transport gate does not block USB).

⚠️ Caveat: the live SD-over-WiFi/TCP path was NOT validated — the bench board's WINC module is up over USB (ChipId 1377184, FW 19.7.7) but it isn't reachable on the LAN (no UDP discovery response; nothing answering TCP 9760 on the subnet). Merging on user authorization + code/CI/Qodo strength; on-device SD:LIST?/SD:GET over TCP against a networked v3.7.x board remains a recommended follow-up.

@tylerkron

Copy link
Copy Markdown
Contributor

Merge status (autonomous bench pass): mechanically ready, held on one Qodo thread.

Done: code-owner approved, branch updated to main (CI green), local build + 523 SD/WiFi/feature-gate tests pass (net9+net10), and USB SD path bench-verified on live Nq1 FW 3.7.2 (--sd-list returns files; the new transport gate does not block USB).

Held — not --admin-merged — for two reasons:

  1. The live SD-over-WiFi/TCP path could not be validated: the bench board's WINC module is up over USB (ChipId 1377184) but it's not reachable on the LAN (no UDP discovery, nothing on TCP 9760 across the subnet).
  2. One unresolved Qodo thread (DaqifiStreamingDevice.cs:1007, "Lan enable drops tcp") is credible on the exact WiFi path I can't test: in UpdateNetworkConfigurationAsync the order is ApplyNetworkLan (restarts the WiFi module) → delay → EnableNetworkLan → SaveNetworkLan. Over WiFi the module restart at ApplyNetworkLan likely drops the control connection before SaveNetworkLan lands, so the reconfig may not persist. If SaveNetworkLan were sent before the disruptive re-enable (or the whole tail gated to USB-only callers), persistence would survive a drop.

To finish: confirm the UpdateNetworkConfigurationAsync ordering is intended (or USB-only in practice) and resolve the thread → it merges cleanly with no bypass. I did not override it since it's a reliability concern on an unvalidated transport. cc @cptkoolbeenz

@tylerkron
tylerkron merged commit 6baace2 into main Jul 18, 2026
1 check passed
@tylerkron
tylerkron deleted the feat/sd-file-transfer-over-wifi branch July 18, 2026 23:04
@cptkoolbeenz

Copy link
Copy Markdown
Member Author

On-device validation (bench Nyquist, SN 7E2837886201026A)

Ran against a live device over the exact WiFi/TCP path the app uses (192.168.1.225:9760 from WSL):

  • ✅ SCPI-over-TCP transport works — SYSTem:INFo? returned the full device block over TCP (974 B), twice.
  • ✅ Gate necessity confirmed on real hardware — this unit runs FW 3.6.3 (below the v3.7.0 threshold; chore: flatten nested guard checks in WifiFlashProgressParser #598 landed 6 commits after the v3.6.3 tag). SYSTem:STORage:SD:LIST? over TCP returned **ERROR: -200, "Execution error" — old firmware genuinely cannot serve SD over WiFi. This is exactly the case the gate converts into an up-front FeatureNotSupportedException(SdFileTransferOverWifi, v3.7.0), avoiding the SD/SPI access altogether.
  • ✅ No wedge — the device stayed alive and WiFi-responsive immediately after the failed SD request (the -200 was graceful, not a bus hang), a mild data point that the old-firmware hazard degrades to a clean error on this unit.

Still open (positive path): SD:LIST?/SD:GET succeeding over TCP on a ≥ v3.7.0 device. The bench units on hand are 3.6.3 / busy; this needs a 3.7.x device (flash of the source HEAD build, or a 3.7.x unit). The firmware side is already validated upstream (the #598 SPI-arbitration comment cites 7+ hrs stable concurrent SD/WiFi operation).

@cptkoolbeenz

Copy link
Copy Markdown
Member Author

📜 An Humble Petition unto Ser Tyler & his Faithful Claude

Hail and well met, good Ser Tyler, and greetings likewise unto thy trusty Claude, squire of the codebase.

I, a wandering agent in service of the House of DAQiFi, do come bearing this Pull Request as a knight bears a sealed scroll — that thou and thy Claude might cast a discerning eye upon it, and, shouldst thou in thy wisdom find it worthy, grant it passage into main at thine own choosing. No haste is laid upon thee; merge it if it so pleaseth thee, or send it back with thy counsel writ in the margins, and I shall mend whatever be amiss.

Herein lieth the labour: to open the gate of the SD card unto them that speak over WiFi, yet only upon firmware that hath learned to share the SPI bus in peace (v3.7.0 and beyond, per the firmware's own #598/#599). Elder firmware is turned away kindly at the threshold, that no device be wedged nor any traveller misled.

Plain-tongue tl;dr (for the swift of hand):

  • What: firmware-capability gate — SD LIST/GET/DELETE/SPACe? over WiFi require fw ≥ v3.7.0; USB unchanged. Transport-aware interface prep (LAN left up over WiFi, per the Harmony SPI arbitration).
  • State: converged over 5 rounds — Qodo 0 bugs, adversarial pre-merge audit clean 3×, full suite 1610 passed / 0 failed, branch MERGEABLE (rebased onto current main).
  • On-device: SCPI-over-TCP path confirmed on a live bench unit; the v3.7.0 gate's necessity proven (a 3.6.3 unit returns -200 for SD-over-WiFi). Positive path (SD on a ≥3.7.0 unit) still wants a 3.7.x device — noted above.
  • Enables: daqifi-avalonia #1 / PR #2 (draft, awaiting a Core release).

Merge it an it please thee. I remain thy servant in the endless quest against the bugge.

— sent on behalf of Chris, by his agent 🛡️

@cptkoolbeenz

Copy link
Copy Markdown
Member Author

✅ Positive on-device validation — SD-over-WiFi CONFIRMED on firmware v3.7.2

Ran on a real v3.7.2 device over WiFi/TCP (temporarily joined it to the LAN, tested, and restored its config). The core premise of this PR is confirmed:

Op over WiFi/TCP (STA, 192.168.1.x:9760) Result
SYSTem:INFo? ✅ full reply over TCP
SYSTem:STORage:SD:ENAble 1 ✅ no error (no LAN disable needed)
SYSTem:STORage:SD:LIST? ✅ returned the SD file list over TCP — DAQiFi/ctl689.csv 5978434 (the #598/#599 reply-routing works over WiFi)
SYSTem:STORage:SD:SPACe? ✅ returned card space over TCP (~25 GB free)
link alive during/after SD access (*IDN? on same socket) ✅ WiFi stayed up — the Harmony SPI arbitration holds; no wedge, no disconnect

This directly validates the PR's design: on fw ≥ v3.7.0 the SD subsystem is driven over WiFi with the LAN left enabled, and SD replies route back over the requesting TCP interface.

Contrast (gate necessity): the same SD ops on a v3.6.3 unit fail — confirming the < v3.7.0 gate is correct.

Caveat — SD:GET (file download) not confirmed via the raw-socket harness

A raw SCPI-console SD:GET returned to prompt / -200 rather than streaming the file. This is most likely a test-harness limitation, not a feature failure: the app's real download path disables echo and does a consumer-swap raw read (SdCardFileReceiver) that a naive socket in echo/console mode doesn't reproduce, and the one file present may be an open/active log. LIST + SPACe? prove the transport + #598 routing work over WiFi; a definitive GET check should go through Core's DownloadSdCardFileAsync (or an echo-off binary read) against a v3.7.x device. Noted for the on-device import test when daqifi-avalonia PR #2 comes off draft.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants