test(integration): protected bands round close-out (ticket 46) #45

Merged
ykp merged 1 commit from protected-bands-integration into master 2026-09-28 21:07:49 +02:00
Owner

Round 9 W4 — closes out the protected-bands round (43–45 shipped as PRs #42/#43/#44). No new features; one defect repair exposed by the battery.

Integration battery — tests/test_protected_bands_integration.py (new, 10 tests)

Full chain: AGWN → post chain (CFR → BandFilter → ProtectedNotch → Windowing) → SigV3.1 write → read-back:

  • The notch is visible in the read-back spectrum — depth bounded by the int16 quantization floor (~17 dB in-file: the notch empties the signal energy, leaving quantization noise; the full pure-tone depth −73 dB lives in the W2 tests), asserted ≥ 10 dB below the passband
  • Header metadata (Fe/FrequenceHF/BandeFI) and sample count unchanged by the stage
  • Byte-determinism with records (remez is deterministic, no seed)
  • Two-record cascade: both notches visible in one file
  • Out-of-span record ⇒ byte-identical to a record-free project (the no-op gate)
  • Count agreement: label count == designable count == 2 for the mixed fixture
  • L17/L18 block generation; design determinism; the tap-cap warning fires

Defect repair (exposed by the battery)

The pipeline never re-checked the records — an L18-overlapping project would generate (the UI gate would normally refuse, but the chain itself had no defense). Now the dispatch refuses like the band-isolation pattern: validation.protected_band_findings is public and the ProtectedNotch dispatch raises ValueError with the first finding messages.

Headless explorer — manual_tests/cola_protected_bands.py (new)

  • Per-record notch responses: centre / ±BW/4 / cut edges / passband + the one-sided MIRROR probe (RF 1010 survives at +0.01 dB while the 985–995 notch hits −73 dB)
  • Cascade composition in table order (the pipeline convention)
  • Settings sweep (--sweep-ripple): ripple × tap cap × measured depth — informative: ripple 0.1 dB @ 128 taps reaches −60 dB (larger allowed ripple buys equiripple depth at the same tap count)
  • --help works with no display (module import is matplotlib-free)

Spec consistency pass — doc/spec.md

§3.4's span rules split precisely (label = band overlap; stage skip = cut-span intersection); §5.3.3 warning wording aligned to the implementation (UserWarning + the remez-refusal fallback); §8.4 file dialogs gain the protected-bands JSON; new §8.5 Protected bands files (the format §4.2.5 referenced but that didn't exist); §9 notes gain the battery/explorer pointers.

Gate

  • pytest: 1288 passed / 64 skipped (headless tier), coverage 93.52 %
  • ruff + mypy: clean (77 files)

Round 9 (protected bands) closes here; the backlog README carries the wave summary + archive (local).

Round 9 W4 — closes out the protected-bands round (43–45 shipped as PRs #42/#43/#44). No new features; one defect repair exposed by the battery. ## Integration battery — `tests/test_protected_bands_integration.py` (new, 10 tests) Full chain: AGWN → post chain (CFR → BandFilter → **ProtectedNotch** → Windowing) → SigV3.1 write → read-back: - The notch is **visible in the read-back spectrum** — depth bounded by the **int16 quantization floor** (~17 dB in-file: the notch empties the signal energy, leaving quantization noise; the full pure-tone depth −73 dB lives in the W2 tests), asserted ≥ 10 dB below the passband - Header metadata (`Fe`/`FrequenceHF`/`BandeFI`) and sample count unchanged by the stage - Byte-determinism with records (remez is deterministic, no seed) - Two-record cascade: both notches visible in one file - Out-of-span record ⇒ **byte-identical** to a record-free project (the no-op gate) - Count agreement: label count == designable count == 2 for the mixed fixture - L17/L18 **block generation**; design determinism; the tap-cap warning fires ## Defect repair (exposed by the battery) The pipeline never re-checked the records — an **L18-overlapping project would generate** (the UI gate would normally refuse, but the chain itself had no defense). Now the dispatch refuses like the band-isolation pattern: `validation.protected_band_findings` is public and the ProtectedNotch dispatch raises `ValueError` with the first finding messages. ## Headless explorer — `manual_tests/cola_protected_bands.py` (new) - Per-record notch responses: centre / ±BW/4 / cut edges / passband + the **one-sided MIRROR probe** (RF 1010 survives at +0.01 dB while the 985–995 notch hits **−73 dB**) - Cascade composition in table order (the pipeline convention) - **Settings sweep** (`--sweep-ripple`): ripple × tap cap × measured depth — informative: ripple 0.1 dB @ 128 taps reaches −60 dB (larger allowed ripple buys equiripple depth at the same tap count) - `--help` works with no display (module import is matplotlib-free) ## Spec consistency pass — `doc/spec.md` §3.4's span rules split precisely (label = **band** overlap; stage skip = **cut-span** intersection); §5.3.3 warning wording aligned to the implementation (UserWarning + the remez-refusal fallback); §8.4 file dialogs gain the protected-bands JSON; **new §8.5 Protected bands files** (the format §4.2.5 referenced but that didn't exist); §9 notes gain the battery/explorer pointers. ## Gate - `pytest`: **1288 passed / 64 skipped** (headless tier), coverage **93.52 %** - `ruff` + `mypy`: clean (77 files) Round 9 (protected bands) closes here; the backlog README carries the wave summary + archive (local).
test(integration): protected bands round close-out (ticket 46)
All checks were successful
test / test (ubuntu-latest) (pull_request) Successful in 4m2s
fa4fee3d3c
Round 9 W4 — closes out the protected-bands round (43-45 shipped as
PRs #42/#43/#44):

- tests/test_protected_bands_integration.py (new, 10 tests): the
  full-chain battery — AGWN → post chain (CFR → band filter → notch →
  windowing) → SigV3.1 write → read-back; the notch visible in the
  read-back spectrum (depth bounded by the int16 quantization floor
  ~17 dB in-file — the pure-tone depth −73 dB lives in the W2 tests;
  the battery asserts ≥ 10 dB below the passband); header metadata
  unchanged by the stage; byte-determinism with records; two-record
  cascade (both notches visible); out-of-span record = byte-identical
  no-op; count-helper agreement (label == designable count); L17/L18
  block generation; design determinism; the cap warning fires.
- DEFECT REPAIR (exposed by the battery): the pipeline never re-checked
  the records — an L18-overlapping project would generate. Now the
  dispatch refuses (the band-isolation pattern): validation's
  protected-band findings are public (validation.protected_band_findings)
  and the ProtectedNotch dispatch raises ValueError with the first
  finding messages.
- manual_tests/cola_protected_bands.py (new): headless explorer —
  per-record notch responses (centre/±BW/4/cut edges/passband + the
  one-sided MIRROR probe: RF 1010 survives at +0.01 dB while the
  985-995 notch hits -73 dB), cascade composition in table order, and
  the settings sweep (ripple × tap cap × measured depth: ripple 0.1 dB
  @ 128 taps reaches -60 dB — larger allowed ripple buys depth).
- doc/spec.md consistency pass: §3.4 span rules split (label = band
  overlap; stage skip = cut-span intersection), §5.3.3 warning wording
  aligned to the implementation (UserWarning + remez-refusal fallback),
  §8.4 file dialogs gain the protected-bands JSON, new §8.5
  Protected bands files (the format §4.2.5 references), §9 note gains
  the battery/explorer pointers.

Gate: pytest 1288 passed / 64 skipped (headless tier), coverage
93.52 %; ruff + mypy clean (77 files).
ykp merged commit b0f360ec96 into master 2026-09-28 21:07:49 +02:00
ykp deleted branch protected-bands-integration 2026-09-28 21:07:49 +02:00
Sign in to join this conversation.
No description provided.