Repository navigation
test(connect): the 22000 Hz ceiling is the board's own — pin the hydrate that carries it (#257) - #269
Conversation
… actually justifies it The comment above the second HydrateDeviceMetadata call in Connect() cited a 22000 Hz figure that #257 read as underivable under pinned Core 1.7.0. Bench measurement says otherwise: the Nq1 (fw 3.7.2, USB) publishes a schema-2 capability document whose streaming.sample_rate_range_hz.max is 22000, and against the pinned package Core applies it after the only ChannelsPopulated raise of the connect. The call is load-bearing; the sentence was ambiguous about which side held 1000 and no document. Rewrites the comment to name the mechanism and the source of truth rather than a figure, and adds the tests that fail if the call is deleted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…vented StreamingFrequencyRangeTests uses 22000 as "a device that advertises more than the board table". The bench Nq1 (fw 3.7.2) states exactly that in its own capability document, so say so where the number is used. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
PR Summary by QodoPin post-initialize capability metadata hydration
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all |
|
Ready for review at Verified on hardware (Nq1 fw 3.7.2,
So #257's premise is false on the metal and the proposed Verified statically only (not exercised on the board): that Core's schema-version bounds (1..2) and the fw ≥ 3.5.0 gate are what admit this document — read from Also checked: the 2-of-3 failure claim reproduced by actually removing the hydrate call (2 failed with the stated assertions, control passed); One free body edit, no commit: the 20 vendored OxyPlot warnings are 13 Follow-ups filed: #270 for the rate sliders still capping at 1000 while the board advertises 22000 (with the unenforced Not merging. |
closes #257
Not merging — for review.
The ticket's premise does not survive the bench
#257 says the comment above the second
HydrateDeviceMetadatacall inConnect()cites a22000Hz ceiling that pinnedDaqifi.Core1.7.0 cannot produce, because "the same sentence saysthis board publishes no capability document".
The board publishes one. Read straight off the attached Nq1 (fw 3.7.2, USB,
/dev/cu.usbmodem1101) with the two queries Core itself sends:Core 1.7.0 accepts schema 1..2 (
DaqifiDevice.Minimum/MaximumCapabilityDocumentApiVersion) and thefeature gate is firmware ≥ 3.5.0 (
DeviceFeatureRequirements.CapabilityDocumentMinFirmware), sothis document is read and applied.
CapabilityDocument.MergeIntoassignscapabilities.MaxSamplingRate = Streaming.MaximumSampleRateHz— that is thesample_rate_range_hz.maxabove. 22000 is exactly derivable, and it is this device's ownnumber.
The sentence the ticket read as "the device has no capability document" is about the wrapper:
without the second hydrate, the wrapper is what has none. Ambiguous, and it cost a ticket — so it
is rewritten here.
What the call actually does, measured
A throwaway console app against the pinned package (
DaqifiStreamingDeviceoverSerialStreamTransport, snapshottingMetadataat the two pointsConnect()hydrates at):Two things follow, and together they are the real justification:
MaxSamplingRatedoes move 1000 → 22000 across that call, andCapabilityDocumentnull →present. So the comment's example was right; the field is evidence for the call after all.
ChannelsPopulatedis raised exactly once during the whole connect. Core re-raises it afterapplying a document only from
SyncAnalogOutputChannelsFromCapabilities, and only when thedocument changes the DAC set — this Nq1's document lists 16 analog inputs and 16 digital I/O and
no analog outputs, so nothing fires. The post-initialize hydrate is the wrapper's only chance
to see the document at all.
Nothing later rescues it either:
DeviceMetadata.UpdateFromProtobufre-applies the retainedoverlay on every subsequent status message, but the wrapper's copy is only refreshed by a hydrate,
and the only other one runs from that single
ChannelsPopulated.Why #255's bench said 1000
#255 reported
1001and30000clamping to 1000 on this same board. That measurement is ofSampleRateCap/the MCP path, not ofCapabilities.MaxSamplingRateafter a completedInitializeAsync— as the run above shows,1001is accepted once the document has been applied.Not chased further here: it is a different layer and a different PR, but it is why the ticket's
"this matches the bench run in #255" reads as corroboration when it is not.
The change
The comment, rewritten to argue from the mechanism instead of a figure: it names the two-stage
ordering, says the document is the only thing in Core that moves
Capabilitiesoff the board table,says why no second
ChannelsPopulatedrescues it, and ends with the ceiling is the device's tostate — read
Capabilities.MaxSamplingRate, so the next reader does not treat any number writtenhere as a constant. 13 lines replacing 8.
Tests — the part that makes it durable.
AbstractStreamingDevice.cshad no coverage of thiscall at all, which is exactly why a ticket could propose deleting it and nothing would have
objected. Three cases in a new file, driving the real
Connect()template with a Core double whoseInitializeAsyncreplays the measured ordering (status message →ChannelsPopulated→ capabilitydocument):
The_capability_document_reaches_the_wrapper_only_after_initialization— atChannelsPopulatedthe wrapper holds 1000 and a null document; when
Connect()returns it holds 22000 and thedocument.
The_ceiling_the_rate_guard_clamps_against_is_the_one_the_document_raised— 5000 is stored, notclamped to 1000. This is the user-visible consequence, via fix(streaming-rate): a profile with an out-of-range rate makes the device refuse to record (#248) #255's setter guard.
A_board_that_publishes_no_document_keeps_the_board_table_ceiling— the control, and the commoncase below firmware 3.5.0: nothing to overlay, ceiling stays at 1000.
Plus two lines in
StreamingFrequencyRangeTestsnoting that its22000is this board's measuredfigure rather than an invented one.
Nothing was deleted, and no number was swapped. The one-line "22000 → 1000" edit #257 proposed
would have been wrong: it would have replaced a correct figure with the board-table default and
left the call looking unjustified.
Verification
All three new tests pass against unmodified
main— the production code is correct, so theymust. The meaningful check is the other direction: with
HydrateDeviceMetadata(coreDevice.Metadata)commented out of
Connect(), 2 of the 3 fail —The_capability_document_reaches_the_wrapper_only_after_initializationonAssert.NotNull(device.Metadata.CapabilityDocument), andThe_ceiling_the_rate_guard_clamps_against_is_the_one_the_document_raisedwithExpected: 5000 / Actual: 1000. The control passes either way, which is what a control is for. Thecall was restored and the working tree verified clean before the gate.
Full suite 587/587.
Build gate — desktop and iOS
Release/iossimulator-arm64both green. Warnings measured on myown merge-base (
104d9ae, this branch is rebased onto it), both sides force-rebuilt with-t:Rebuild: 191 all-project total on each, reconciling as 171 first-party + 20 vendored OxyPlotwarnings on both (13
AVP1002, 4CS0108, 2CS0169, 1CS0618— the count is 20, not all ofthem are
AVP1002). Diffing the first-party warning sets gives zero added and zero removed; the oneline-number delta is the same pre-existing
CS8625in this file moving 1785 → 1790, which is thefive extra comment lines above it. No
packages.lock.jsonchanged. Android is not buildable on thisMac (workload absent); CI covers it.
Bench safety. Non-destructive throughout: two SCPI queries plus
SYSTem:ECHO 0on the raw port,then a normal connect/initialize/disconnect through Core. No firmware or WiFi flash, no SD command,
no reboot or power-cycle.
StreamingFrequencyis a client-side property on Core — setting it sendsnothing to the device — and it was restored before disconnect anyway. Board lock held for the
duration and released.
Observed, deliberately not fixed here
Minimum="1" Maximum="1000"in the views, while thisboard advertises 22000. The wrapper and Core would both take more; the UI cannot ask for it. A
real gap, but a UI change with its own argument, not a comment fix.
current_max_rate_hzis 7746 on this board, not 22000. The wrapper clamps against theabsolute ceiling only, so a user could pick 20000 and have the device refuse at start. fix(streaming-rate): a profile with an out-of-range rate makes the device refuse to record (#248) #255
considered and deferred this (Core has
EnforceStreamingFrequencyCapfor it); nothing herechanges that trade.