Skip to content

Handle steps that run nothing, and stop the driver degrading a protocol - #8

Merged
bbfrederick merged 1 commit into
mainfrom
exar1-pause-steps
Aug 29, 2026
Merged

Handle steps that run nothing, and stop the driver degrading a protocol#8
bbfrederick merged 1 commit into
mainfrom
exar1-pause-steps

Conversation

@bbfrederick

Copy link
Copy Markdown
Owner

Three archives arrived from clinical studies rather than pure imaging
protocols, and the reader raised on all three.

A step in the running order need not run anything

EdfPauseStep is an instruction an operator put between scans -- "Count down
with RA to start of scan", "Pause for saliva collection", "Do NOT add Raw
Filter to 3D MPR" -- carrying an EdfMeasurementStepContent with injector
fields and no protocol child. Eleven of CHR-MDD's thirty-four steps are
pauses.

Nothing about the existing corpus could have surfaced this. Every archive
until now came from imaging protocols; these come from studies with
behavioural tasks, where the operator needs telling what to do between
acquisitions.

Pause steps are named, they are in the chain, and the PDF does not print them
as scans, so anything walking scans skips them. Step.is_pause reads the
instance kind and runs_a_protocol reads the content, and a test asserts the
two always agree, because either alone could be wrong. Reader, validator,
patcher and driver all handle them, and the validator's independent step count
now includes both kinds -- otherwise it disagrees with the chain it is
checking.

Two driver bugs, both caught by the same invariant

Driving an archive from its own PDF must write nothing. On these it wrote
one value and refused another, and both were real.

RMS became R. Unit stripping matched a unit anywhere in the string,
and MS is a unit, so a case-insensitive match ate the tail of RMS -- which
then failed to resolve as an Averaging choice. A unit only counts after
whitespace.

A printout carries fewer digits than the protocol. One scan prints
TE 1 = 54 ms for a stored 54.16, so writing the printed value back drops
0.16 ms. agrees_at_printed_precision treats a printed value as matching when
the stored one rounds to it at the precision actually printed. Without it the
driver quietly degraded every protocol it touched -- which is the failure mode
a PDF-driven generator has to be most careful about, since the loss is
invisible in the printout it came from.

Matching

Repeated scan names now pair positionally. A protocol may run four scans
called Localizer; when both sides agree on how many, running order settles
which is which, and when they disagree it stays a guess and is reported.
ZMK23 went from 18 matched and 5 unmatched to 23 and 0.

Two pairs in the corpus do not match each other

CHR-MDD                 printout 24 scans   archive 23 acquisitions
31P CSI 20230503 NOE    printout 13 scans   archive 24 acquisitions
ZMK23 with Physio       printout 23 scans   archive 23 acquisitions

All three printed counts are confirmed against their own contents pages, so
these are different versions of those protocols rather than parse errors. The
expectation table says so rather than quietly picking a number.

Verification

1843 passed, 2 skipped
black / isort / codespell     clean

Corpus is 54 snapshots and 1031 scans; the new protocols add no unaccounted
sequences.

Still open

No archive built end to end by exar has been through a loader. Every piece
is validated separately -- patched values on five archives, created scans on
one -- and the combination is not. Coverage is about a tenth of what a
protocol prints, dominated by console UI settings rather than acquisition
parameters, which is worth confirming before collecting more option scans.

🤖 Generated with Claude Code

Three archives arrived from clinical studies rather than pure imaging
protocols, and the reader raised on all of them. A step in the running
order need not run anything: EdfPauseStep is an instruction an operator
put between scans -- "Count down with RA to start of scan", "Pause for
saliva collection", "Do NOT add Raw Filter to 3D MPR" -- carrying an
EdfMeasurementStepContent with injector fields and no protocol child.
Eleven of CHR-MDD's thirty-four steps are pauses.

Nothing about the existing corpus could have surfaced that. It came from
protocols written for studies with behavioural tasks, where the operator
needs telling what to do between acquisitions.

They are named, they are in the chain, and the PDF does not print them as
scans, so anything walking scans skips them. Step.is_pause reads the
instance kind and runs_a_protocol reads the content; a test asserts the two
always agree, because either alone could be wrong. Reader, validator,
patcher and driver all handle them, and the validator's independent step
count now includes both kinds or it disagrees with the chain it checks.

Driving an archive from its own PDF must write nothing, and on these it
wrote one value and refused another. Both were real.

Unit stripping matched a unit anywhere, and "MS" is one, so a
case-insensitive match turned "RMS" into "R" -- which then failed to
resolve as an Averaging choice. A unit only counts after whitespace.

And a printout carries fewer digits than the protocol: one scan prints
"TE 1 = 54 ms" for a stored 54.16, so writing the printed value back drops
0.16 ms. agrees_at_printed_precision treats a printed value as matching
when the stored one rounds to it at the precision actually printed. Without
it the driver quietly degraded every protocol it touched.

Matching also pairs repeated names positionally. A protocol may run four
scans called Localizer; when both sides agree on how many, running order
settles which is which, and when they disagree it stays a guess and is
reported. ZMK23 went from 18 matched and 5 unmatched to 23 and 0.

Two of the three pairs do not match each other: CHR-MDD's archive holds 23
acquisitions against its printout's 24, and the 31P archive holds 24
against its printout's 13. All three printed counts are confirmed by their
own contents pages, so these are different versions of those protocols
rather than parse errors, and the expectation table says so. ZMK23's pair
agrees at 23 each.

Corpus is 54 snapshots and 1031 scans; the new protocols add no unaccounted
sequences.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bbfrederick
bbfrederick merged commit 5c448a3 into main Aug 29, 2026
8 checks passed
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.

1 participant