- Runs feature extraction once, then variance-aware ComBat, returns
both states as a single long-format DataFrame with columns
subject_id / site / feature / feature_value / harmonization_state.
- Reuses the same _MIN_VAR_THRESHOLD split as run_pipeline so
diagnostics reflect production exactly.
- 2 new tests: long-format shape + post-ComBat site-gap < pre-ComBat
(regression pin for the 5.0 → 0.0015 reduction story).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(mri): log run params, metrics, and parquet artifact to MLflow
EEG and MRI both append "See src.core.determinism." to the §4-rule-3
comment block. BBB silently dropped the pointer in 4dab60f. This
restores cross-pipeline consistency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor(mri): use core.determinism + core.storage helpers
- Fix broken Day-2 plan link (04-29→04-30, correct filename)
- Add Day-3 plan + mri_pipeline.py + test_mri_pipeline.py to "Where to Look"
- Extend Repository Layout tree with mri_pipeline.py (└──) entry
- Update fixtures/ description to list all three modality fixtures
- Broaden "All four BBB functions" prose to "All pipeline functions … Days 1–3"
- Bump timing claim from "under 2 seconds" to "under 4 seconds"
- Replace forward-ref string _StatFn with Callable[[np.ndarray], float] alias
- Add `from typing import Callable` to eeg_pipeline.py stdlib imports
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses I1, I2, M1 from code review:
- Docstring step-2 now calls out 6-connectivity, iterations=1, and the
thin-feature erosion caveat (suggesting 26-connectivity for production).
- New paragraph documents the all-False WARNING behaviour.
- logger.warning emitted when cleaned.any() is False, reporting volume
min/max and effective threshold so silent feature-zeroing is visible
in production logs.
- .astype(bool) moved onto the binary_opening line; return is just cleaned.
- New regression test: constant-valued volume produces empty mask + WARNING.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- README: float32→float64, "will produce"→"produces" (I1)
- README: add eeg_pipeline.py to repository layout tree (I2)
- README: add Day-2 plan and EEG test file to Where to Look (M1)
- bbb_pipeline + eeg_pipeline: import os/pyarrow at top, set OMP/OPENBLAS/MKL=1 and pa thread counts at module level after logger (I3a)
- AGENTS.md §4: document Determinism environment paragraph (I3b)
- AGENTS.md §1: mark mri_pipeline.py as (planned, Day 3) (I4)
- pytest.ini: add markers block with slow: marker (M2)
- Day-1 plan: no float32 references found — no-op (M3)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace pre-screen-before-filter logic with principled filter→ICA→epoch→validate
order so that FIR-spread NaN is caught at epoch validation time, not missed after
surviving epochs are extracted from contaminated filtered data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat(eeg): flatten 3D epochs into deterministic 2D feat_<ch>_<band|stat> table
Implements ICA-based EOG artifact removal using measure="correlation",
threshold=0.9 to reliably flag components on small (4-channel) fixtures
where the default z-score threshold is algebraically unreachable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(eeg): guard bandpass_filter against inverted l_freq/h_freq; document picks=all