Per-sample decoder verification at scale, plus the offset investigation.
The series-3 codec is now verified sample-by-sample against 14,338 preserved
Blastware ASCII exports — 1,249 waveform and 13,089 histogram, spanning 45 units
and files back to 2018. That is 11x the ground truth the production store
carried, and it found one real codec bug (below).
Fixed
Sub-minute histograms with a partial final block decoded to nothing
(histogram_codec.detect_multi_interval_stride). The stride search confirmed
itself on a third block header whenever the body was long enough to hold one —
but a body can exceed two strides and still contain only two real blocks, because
a partial final block leaves trailing padding. BE18193 T193L0XM.CI0H (51
intervals at 2 s = one full 30-interval block plus a 21-interval remainder, in a
2787-byte body) therefore had its correct stride of 612 discarded and produced an
empty decode. A missing third header now means end-of-stream rather than
disqualification; the block-counter check, which is what actually prevents the
false positives that once mis-dispatched 9,082 files, is unchanged.
Found by decoding the full DL2 archive against its preserved Blastware ASCII
exports. Across 63,535 unique histogram binaries the fix recovers 4 files — K440HJCN.3C0H and K557IF1U.8K0H (stride 252), T191HVNP.0S0H (92) and T193L0XM.CI0H (612) — with zero files regressed. Verification over all
14,340 archive pairs goes 14,337 → 14,338 exact, the only remainder being two
series-4 IDF files that belong to a different codec.
(The DL2 export keeps a byte-identical Sent/ mirror of its root, so a naive
walk double-counts every binary — 127,035 paths are 63,535 distinct files. The
ASCII exports are not mirrored, so the 14,340 pair count is already distinct.)
⚠ Prod stores hold .h5 files generated before this fix. Those 4 events stay
empty until backfill_sidecars.py is re-run — not worth a two-hour prod backfill
on its own; fold it into the next one.
Histogram/waveform twin matching is now interval-based (find_twins). A real
trigger is recorded twice — as a triggered waveform (stamped at the trigger instant)
and inside the scheduled histogram whose interval contains it (stamped at the 7am/7pm
interval start) — so the two twins can be hours apart. The old ±5-minute window
silently missed them, which broke review propagation (flagging one twin didn't flag its
twin). Twins are now matched by same serial + identical peak_vector_sum + opposite
record type + the waveform falling within the histogram's interval (bounded by the next
same-serial histogram). window_seconds is retained but ignored. Fixes terra-view #102
sub-task 2.
/health reported a hard-coded 0.1.0 instead of the real service version. sfm/server.py now derives its version from minimateplus.event_file_io.TOOL_VERSION,
making that constant the single source of truth for the service version and the
sidecar stamp alike — one place to bump at release.
CLAUDE.md had 793 NUL bytes appended after its last line, which made grep
treat the file as binary and silently skip it. Present since at least v0.21.0.
Stripped.
Added
docs/offset_investigation.md — a dated journal of the "offset" hardware
fault: base rate, detector design, per-unit case files, ruled-out hypotheses
(each kept with the evidence that killed it), and Instantel's own autozero
procedure with its 2027–2069 acceptance window.
scratch/verify_against_ascii.py — decodes a corpus of BW binaries and
diffs every sample against the paired _ASCII.TXT. Includes a saturation
carve-out: BW clamps clipped events to the range maximum and writes OORANGE,
while the decoder faithfully reports counts past nominal full scale.
scratch/offset_scan3.py — offset detector. Measures the resting floor in
the pre-trigger window (definitionally quiet) and requires it to hold across
pre / middle / end. Result: 5 of 45 units (11%), stable across a 2x
threshold range. Supersedes offset_scan.py and offset_scan2.py, both kept
as the reasoning trail.
Verified
19,244 healthy channel-events sit at a pre-trigger floor of exactly 0.000
(62.7%), 94.5% within ±1 quantisation unit, median +0.0000. No systematic
zero-point bias in the decoder — an independent confirmation of the
32000-count geo full scale, arrived at from a different direction than the
ASCII sample comparisons.
## v0.27.0 — 2026-08-28
**Per-sample decoder verification at scale, plus the offset investigation.**
The series-3 codec is now verified sample-by-sample against **14,338** preserved
Blastware ASCII exports — 1,249 waveform and 13,089 histogram, spanning 45 units
and files back to 2018. That is 11x the ground truth the production store
carried, and it found one real codec bug (below).
### Fixed
- **Sub-minute histograms with a partial final block decoded to nothing**
(`histogram_codec.detect_multi_interval_stride`). The stride search confirmed
itself on a third block header whenever the body was long enough to hold one —
but a body can exceed two strides and still contain only two real blocks, because
a *partial* final block leaves trailing padding. BE18193 `T193L0XM.CI0H` (51
intervals at 2 s = one full 30-interval block plus a 21-interval remainder, in a
2787-byte body) therefore had its correct stride of 612 discarded and produced an
empty decode. A missing third header now means end-of-stream rather than
disqualification; the block-counter check, which is what actually prevents the
false positives that once mis-dispatched 9,082 files, is unchanged.
Found by decoding the full DL2 archive against its preserved Blastware ASCII
exports. Across **63,535 unique** histogram binaries the fix recovers **4 files** —
`K440HJCN.3C0H` and `K557IF1U.8K0H` (stride 252), `T191HVNP.0S0H` (92) and
`T193L0XM.CI0H` (612) — with **zero** files regressed. Verification over all
14,340 archive pairs goes 14,337 → 14,338 exact, the only remainder being two
series-4 IDF files that belong to a different codec.
(The DL2 export keeps a byte-identical `Sent/` mirror of its root, so a naive
walk double-counts every binary — 127,035 paths are 63,535 distinct files. The
ASCII exports are *not* mirrored, so the 14,340 pair count is already distinct.)
⚠ Prod stores hold `.h5` files generated before this fix. Those 4 events stay
empty until `backfill_sidecars.py` is re-run — not worth a two-hour prod backfill
on its own; fold it into the next one.
- **Histogram/waveform twin matching is now interval-based** (`find_twins`). A real
trigger is recorded twice — as a triggered waveform (stamped at the trigger instant)
and inside the scheduled histogram whose interval contains it (stamped at the 7am/7pm
interval start) — so the two twins can be **hours apart**. The old ±5-minute window
silently missed them, which broke review propagation (flagging one twin didn't flag its
twin). Twins are now matched by same serial + identical `peak_vector_sum` + opposite
record type + the waveform falling within the histogram's interval (bounded by the next
same-serial histogram). `window_seconds` is retained but ignored. Fixes terra-view #102
sub-task 2.
- **`/health` reported a hard-coded `0.1.0`** instead of the real service version.
`sfm/server.py` now derives its version from `minimateplus.event_file_io.TOOL_VERSION`,
making that constant the single source of truth for the service version and the
sidecar stamp alike — one place to bump at release.
- **`CLAUDE.md` had 793 NUL bytes appended** after its last line, which made `grep`
treat the file as binary and silently skip it. Present since at least v0.21.0.
Stripped.
### Added
- **`docs/offset_investigation.md`** — a dated journal of the "offset" hardware
fault: base rate, detector design, per-unit case files, ruled-out hypotheses
(each kept with the evidence that killed it), and Instantel's own autozero
procedure with its 2027–2069 acceptance window.
- **`scratch/verify_against_ascii.py`** — decodes a corpus of BW binaries and
diffs every sample against the paired `_ASCII.TXT`. Includes a saturation
carve-out: BW clamps clipped events to the range maximum and writes `OORANGE`,
while the decoder faithfully reports counts past nominal full scale.
- **`scratch/offset_scan3.py`** — offset detector. Measures the resting floor in
the *pre-trigger* window (definitionally quiet) and requires it to hold across
pre / middle / end. Result: **5 of 45 units (11%)**, stable across a 2x
threshold range. Supersedes `offset_scan.py` and `offset_scan2.py`, both kept
as the reasoning trail.
### Verified
- **19,244 healthy channel-events sit at a pre-trigger floor of exactly 0.000
(62.7%), 94.5% within ±1 quantisation unit, median +0.0000.** No systematic
zero-point bias in the decoder — an independent confirmation of the
32000-count geo full scale, arrived at from a different direction than the
ASCII sample comparisons.
---
A real trigger is recorded twice — as a triggered waveform (stamped at the
trigger instant) and inside the scheduled histogram whose interval contains it
(stamped at the 7am/7pm interval start). The two twins routinely differ by
HOURS, so the old ±5-minute window in find_twins silently missed them — which
broke review propagation (flagging one twin left its twin unflagged).
Twins are now matched by: same serial + identical peak_vector_sum + OPPOSITE
record type + the waveform's timestamp falling within the histogram's interval
(bounded by the next same-serial histogram). Matching keys off record timestamps
(not call-in/received times, which drift with field connectivity). window_seconds
is retained but ignored.
Rewrote test_find_twins + test_twin_propagation for the new contract (incl. the
75-min-apart UM12947 case, cross-type exclusion, containing-interval selection,
open-ended latest interval). Full suite: 264 passed; the 16 failures are
pre-existing (missing gitignored fixtures + a v0.26.0 codec case), unchanged
from baseline.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDXjZCr4RqT2U3QvMDhgzf
detect_multi_interval_stride() confirmed a candidate stride on a third block
header whenever the body was long enough to contain one. But a body can exceed
two strides and still hold only two real blocks: a partial final block leaves
trailing padding. BE18193 T193L0XM.CI0H — 51 intervals at 2 s, i.e. one full
30-interval block plus a 21-interval remainder in a 2787-byte body — had every
decisive check pass at stride 612 (header at 0, header at 612, block counter
256 -> 257) and was then rejected for the absent third header at 1224. It
decoded to nothing.
A missing third header now means end-of-stream rather than disqualification.
The block-counter check is untouched — that is the test that prevents the
false positives which once handed 9,082 standard-block files to the
multi-interval walker.
Found by running the full DL2 archive against its preserved Blastware ASCII
exports (14,340 paired files, 11x the previous ground-truth corpus).
Measured over 127,035 archive histogram binaries:
recovered 8 files (strides 92, 252, 612; BE18193, BE18191, BE9557, BE9440)
regressed 0 files
Full-corpus verification: 14,337 -> 14,338 exact of 14,338 decodable pairs
(the 2 excluded are series-4 IDF, a different codec).
Also adds scratch/verify_against_ascii.py (per-sample decoder verification
against BW exports, with a saturation carve-out — BW clamps clipped events to
the range max while the decoder reports true counts) and scratch/offset_scan.py.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcog
The DL2 export keeps a byte-identical `Sent/` copy of its root, so walking it
counts every binary twice: 127,035 histogram paths are 63,535 distinct files,
and 13,077 waveform paths are 6,577. offset_scan.py now keeps the first
occurrence of each basename.
Corrects the previous commit's changelog claim of 8 recovered files — it is 4:
K440HJCN.3C0H and K557IF1U.8K0H (stride 252), T191HVNP.0S0H (92), T193L0XM.CI0H
(612). Still zero regressions. The per-unit breakdown reading exactly 2-2-2-2
should have given the doubling away.
The 14,338-exact verification result is unaffected: ASCII exports are not
mirrored (14,340 paths, 14,340 distinct names), and the harness enumerates
those rather than the binaries.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcog
Adds docs/offset_investigation.md — a dated journal of the "offset" hardware
fault, in the style of the codec status docs: findings with provenance, dead
ends kept with the reason they died, and per-unit case files.
Contents:
- base rate 5-6 of 45 units (11-13%) over the DL2 archive, 2018-2026,
confirming rather than overturning the earlier 2-of-21 estimate
- the detector, with the rationale for each term and its known blind spot
(event traces carry real motion, so only trace-dominating offsets show)
- the bimodality result: relaxing the amplitude floor 11x adds no new units
- Instantel's own procedure and thresholds from their FAQs 13-0-21 / 12-0-10:
A/D-mode ">5 counts", the autozero key sequence, and the 2027-2069 X1/X8
acceptance window that explains the ~10% field success rate of a re-zero
- four ruled-out hypotheses, each with the evidence that killed it:
condensation, clipping, the sensor check as a predictor (102 offset events,
zero failures — a grossly offset unit passes its own self-check), and the
calibration-timing correlation (confounded, one unit per time bucket)
- open questions, chiefly whether SUB 0x0E carries the autozero numbers
Cross-referenced from CLAUDE.md and Appendix E of the protocol reference
(whose CRLF line endings are preserved).
Separately: CLAUDE.md had 793 NUL bytes appended after its last line. They
predate this work (present at least as far back as e42956a / v0.21.0) and made
grep treat the file as binary, silently skipping it. Stripped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcog
terra-view's SFM Admin page (/admin/sfm) displays whatever /health returns for
`version`. That was hardcoded to "0.1.0" and never bumped, so the page showed
0.1.0 while the service was actually 0.26.0. Point both /health and the FastAPI
OpenAPI version at the release-bumped TOOL_VERSION (single source of truth), so
they can't drift again. Adds httpx-free regression tests (call health() directly).
Note: minimateplus.__version__ is separately stale at 0.1.0 — left as-is here
(nothing user-facing reads it; touching the package __init__ risks import order).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDXjZCr4RqT2U3QvMDhgzf
Brian challenged the v1 finding that offsets "come and go", against field
experience that a unit which develops one stays broken until the geophone is
replaced. He was right; v1 had two flaws, both of which manufactured false
recoveries:
1. It scored only the axis with the largest peak, so a real event on one axis
hid a persistent pedestal on another. BE12599 on 2026-08-21 read "clean"
because Long had a 1.065 in/s event, while Tran sat at +0.4732 in/s and was
never examined.
2. It used the mean, which a real transient perturbs. The median is the resting
baseline and a blast does not move it. Same event, Long channel:
mean +0.0783 vs median -0.0050.
offset_scan2.py flags a CHANNEL when |median| >= 0.025 in/s (5 A/D counts,
Instantel's own criterion) and treats >=3 consecutive flagged events as the
real signal. No m/p ratio guard is needed — that existed only to compensate for
the mean.
Corrected results:
units with any flagged event 6 -> 19 of 45
units with a sustained pedestal 8 of 45 (18%)
runs >=3 consecutive 29; 1-2 event runs (noise) 69
Also corrected: the affected channel is most often Vert, not Tran (v1 named
whichever axis had the largest peak, so it was frequently wrong). BE10895 and
BE18003 were invisible to v1. BE12599's fault began 2026-08-14, not 08-17.
The decode itself was never in question and is confirmed against Blastware's
own ASCII export: on K558LJN3.BK0W, BW shows Tran parked at +0.265..+0.375 in/s
for the entire record while Vert and Long sit at ~0.005 — Instantel's "parallel
lines above or below the zero line".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcog
Brian's method, and better than v2's whole-record median: the pre-trigger
window is definitionally quiet (the buffer captured before the trigger fired),
whereas a median is merely robust to the event. Requiring the floor to hold
across pre-trigger / middle / end rejects transients that a median cannot.
per channel: pre/mid/end medians, spread = max - min
offset when |pre| >= floor AND spread <= 0.02 in/s
real fault >= 3 consecutive flagged events on that channel
The empirical noise floor justifies the threshold and validates the decoder:
across 19,244 non-flagged channel-events the pre-trigger floor is 62.7% exactly
0.000, 94.5% within +/-1 quantisation unit, median +0.0000, mean -0.0008. There
is no systematic zero-point bias — an independent confirmation of the
32000-count geo scale.
The result is threshold-insensitive across a 2x range (0.020 to 0.040 in/s),
which is what separates a real signal from a tuned one:
FINAL: 5 of 45 units (11%) — BE9558, BE11529, BE12599, BE13117, BE18438
BE11007 and BE10895 drop out; the spread test identifies them as transients
rather than pedestals. v1's 11% headline was right by luck — it included
BE11007 and named the wrong channel on most units.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcog
Bumps pyproject, TOOL_VERSION, README and CLAUDE.md to 0.27.0. sfm/server.py
now derives its version from TOOL_VERSION (c8c4ec2), so that constant is the
single source of truth for the service version and the sidecar stamp alike.
What ships:
- histogram partial-final-block fix (4 files recovered, 0 regressed)
- interval-based find_twins matching (terra-view #102 sub-task 2)
- /health no longer reports a hard-coded 0.1.0
- 793 NUL bytes stripped from CLAUDE.md (made grep skip it as binary)
- docs/offset_investigation.md, and the offset detectors
- scratch/verify_against_ascii.py
Verification: the series-3 codec now decodes 14,338 / 14,338 archive pairs
exactly against their Blastware ASCII exports (1,249 waveform + 13,089
histogram, 45 units, back to 2018) — 11x the ground truth the prod store
carried, and it supersedes the old "per-sample on 11% of files" caveat.
Independent check on the scale: 19,244 healthy channel-events sit at a
pre-trigger floor of exactly 0.000 (62.7%), 94.5% within one quantisation
unit, median +0.0000. No zero-point bias in the decoder.
⚠ TOOL_VERSION moved, so the next prod backfill regenerates the whole store
(~2 hours on the NAS). That is intended — it is what publishes the 4 recovered
histograms — but it is not a no-op; schedule it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcog
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
v0.27.0 — 2026-08-28
Per-sample decoder verification at scale, plus the offset investigation.
The series-3 codec is now verified sample-by-sample against 14,338 preserved
Blastware ASCII exports — 1,249 waveform and 13,089 histogram, spanning 45 units
and files back to 2018. That is 11x the ground truth the production store
carried, and it found one real codec bug (below).
Fixed
Sub-minute histograms with a partial final block decoded to nothing
(
histogram_codec.detect_multi_interval_stride). The stride search confirmeditself on a third block header whenever the body was long enough to hold one —
but a body can exceed two strides and still contain only two real blocks, because
a partial final block leaves trailing padding. BE18193
T193L0XM.CI0H(51intervals at 2 s = one full 30-interval block plus a 21-interval remainder, in a
2787-byte body) therefore had its correct stride of 612 discarded and produced an
empty decode. A missing third header now means end-of-stream rather than
disqualification; the block-counter check, which is what actually prevents the
false positives that once mis-dispatched 9,082 files, is unchanged.
Found by decoding the full DL2 archive against its preserved Blastware ASCII
exports. Across 63,535 unique histogram binaries the fix recovers 4 files —
K440HJCN.3C0HandK557IF1U.8K0H(stride 252),T191HVNP.0S0H(92) andT193L0XM.CI0H(612) — with zero files regressed. Verification over all14,340 archive pairs goes 14,337 → 14,338 exact, the only remainder being two
series-4 IDF files that belong to a different codec.
(The DL2 export keeps a byte-identical
Sent/mirror of its root, so a naivewalk double-counts every binary — 127,035 paths are 63,535 distinct files. The
ASCII exports are not mirrored, so the 14,340 pair count is already distinct.)
⚠ Prod stores hold
.h5files generated before this fix. Those 4 events stayempty until
backfill_sidecars.pyis re-run — not worth a two-hour prod backfillon its own; fold it into the next one.
Histogram/waveform twin matching is now interval-based (
find_twins). A realtrigger is recorded twice — as a triggered waveform (stamped at the trigger instant)
and inside the scheduled histogram whose interval contains it (stamped at the 7am/7pm
interval start) — so the two twins can be hours apart. The old ±5-minute window
silently missed them, which broke review propagation (flagging one twin didn't flag its
twin). Twins are now matched by same serial + identical
peak_vector_sum+ oppositerecord type + the waveform falling within the histogram's interval (bounded by the next
same-serial histogram).
window_secondsis retained but ignored. Fixes terra-view #102sub-task 2.
/healthreported a hard-coded0.1.0instead of the real service version.sfm/server.pynow derives its version fromminimateplus.event_file_io.TOOL_VERSION,making that constant the single source of truth for the service version and the
sidecar stamp alike — one place to bump at release.
CLAUDE.mdhad 793 NUL bytes appended after its last line, which madegreptreat the file as binary and silently skip it. Present since at least v0.21.0.
Stripped.
Added
docs/offset_investigation.md— a dated journal of the "offset" hardwarefault: base rate, detector design, per-unit case files, ruled-out hypotheses
(each kept with the evidence that killed it), and Instantel's own autozero
procedure with its 2027–2069 acceptance window.
scratch/verify_against_ascii.py— decodes a corpus of BW binaries anddiffs every sample against the paired
_ASCII.TXT. Includes a saturationcarve-out: BW clamps clipped events to the range maximum and writes
OORANGE,while the decoder faithfully reports counts past nominal full scale.
scratch/offset_scan3.py— offset detector. Measures the resting floor inthe pre-trigger window (definitionally quiet) and requires it to hold across
pre / middle / end. Result: 5 of 45 units (11%), stable across a 2x
threshold range. Supersedes
offset_scan.pyandoffset_scan2.py, both keptas the reasoning trail.
Verified
(62.7%), 94.5% within ±1 quantisation unit, median +0.0000. No systematic
zero-point bias in the decoder — an independent confirmation of the
32000-count geo full scale, arrived at from a different direction than the
ASCII sample comparisons.
Sent/mirror; correct the recovered-file count 4839ddfa0eAdds docs/offset_investigation.md — a dated journal of the "offset" hardware fault, in the style of the codec status docs: findings with provenance, dead ends kept with the reason they died, and per-unit case files. Contents: - base rate 5-6 of 45 units (11-13%) over the DL2 archive, 2018-2026, confirming rather than overturning the earlier 2-of-21 estimate - the detector, with the rationale for each term and its known blind spot (event traces carry real motion, so only trace-dominating offsets show) - the bimodality result: relaxing the amplitude floor 11x adds no new units - Instantel's own procedure and thresholds from their FAQs 13-0-21 / 12-0-10: A/D-mode ">5 counts", the autozero key sequence, and the 2027-2069 X1/X8 acceptance window that explains the ~10% field success rate of a re-zero - four ruled-out hypotheses, each with the evidence that killed it: condensation, clipping, the sensor check as a predictor (102 offset events, zero failures — a grossly offset unit passes its own self-check), and the calibration-timing correlation (confounded, one unit per time bucket) - open questions, chiefly whether SUB 0x0E carries the autozero numbers Cross-referenced from CLAUDE.md and Appendix E of the protocol reference (whose CRLF line endings are preserved). Separately: CLAUDE.md had 793 NUL bytes appended after its last line. They predate this work (present at least as far back ase42956a/ v0.21.0) and made grep treat the file as binary, silently skipping it. Stripped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcogBrian's method, and better than v2's whole-record median: the pre-trigger window is definitionally quiet (the buffer captured before the trigger fired), whereas a median is merely robust to the event. Requiring the floor to hold across pre-trigger / middle / end rejects transients that a median cannot. per channel: pre/mid/end medians, spread = max - min offset when |pre| >= floor AND spread <= 0.02 in/s real fault >= 3 consecutive flagged events on that channel The empirical noise floor justifies the threshold and validates the decoder: across 19,244 non-flagged channel-events the pre-trigger floor is 62.7% exactly 0.000, 94.5% within +/-1 quantisation unit, median +0.0000, mean -0.0008. There is no systematic zero-point bias — an independent confirmation of the 32000-count geo scale. The result is threshold-insensitive across a 2x range (0.020 to 0.040 in/s), which is what separates a real signal from a tuned one: FINAL: 5 of 45 units (11%) — BE9558, BE11529, BE12599, BE13117, BE18438 BE11007 and BE10895 drop out; the spread test identifies them as transients rather than pedestals. v1's 11% headline was right by luck — it included BE11007 and named the wrong channel on most units. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcog