Scenario: Debugging a Cross-Flow Anomaly¶
Status: draft Date: 2026-04-08 Project: pixecog Starting state:
- Milestone
spindle-detection-validatediscomplete - Milestone
swr-detection-validatediscomplete - Pipeline
coupling_spindle_ripplehas been run for all 5 subjects - The cross-correlogram output is unexpectedly flat — no coupling peak
- All upstream milestones passed their individual validation
- The researcher expected a clear spindle-ripple coupling peak based on Siapas 1998, Sirota 2003, and Maingret 2016
What the researcher wants: Figure out why the coupling analysis shows no spindle-ripple interaction when all the literature — and this project's own validated detections — say it should be there.
Phase 1: Symptom — "Something is wrong"¶
Human: The cross-correlogram between spindles and ripples is completely
flat. No coupling peak at all. Sirota 2003 and Siapas 1998 both
show clear spindle-ripple coupling. Something is wrong.
Agent: That's unexpected — both detection milestones passed with good
metrics. Let me investigate systematically.
The trap
The instinct is to suspect the coupling code itself. But in cross-flow pipelines, the symptom is almost never where the cause is. The coupling analysis can be perfectly correct and still produce flat output if its inputs are wrong. The investigation must trace the full dependency chain.
Phase 2: Reproduce and scope — "Is it real, and how widespread?"¶
Behind the scenes
→ pipeio_target_paths("coupling_spindle_ripple", group="correlogram")
Returns: output paths for all 5 subjects
→ [reads cross-correlogram output for sub-01]
Confirms: flat correlogram, no significant peak at any lag
→ [reads cross-correlogram outputs for sub-02 through sub-05]
All flat — no coupling peak in any subject
→ note_create(note_type="idea",
tags=["observation", "investigate"],
series="coupling_spindle_ripple",
title="Investigate: flat cross-correlogram across all 5 subjects")
Agent: **Confirmed:** the cross-correlogram is flat for ALL 5 subjects.
No significant coupling peak at any lag from -500ms to +500ms.
**Scoping:** this is systemic (all subjects), not subject-specific.
That rules out electrode issues or subject-level artifacts — this
points to a pipeline or configuration problem.
Starting systematic trace through the dependency chain:
```
spectrogram_burst → spindle events ─┐
├→ coupling_spindle_ripple → correlogram
sharpwaveripple → ripple events ─────┘
```
Let me check the coupling code first, then trace upstream.
Phase 3: Check coupling code — "Is the analysis itself correct?"¶
Behind the scenes
→ pipeio_mod_context("coupling_spindle_ripple", "cross_correlogram")
Returns: script reads spindle event times from spectrogram_burst output,
reads ripple event times from sharpwaveripple output,
computes cross-correlogram with ±500ms window, 10ms bins,
shuffled control (100 shuffles)
→ codio_get("cogpy")
Returns: cogpy.coupling.cross_correlogram — well-tested function,
used in other projects
Agent: **Coupling code inspection:**
- Script reads spindle events from `spectrogram_burst` output ✓
- Script reads ripple events from `sharpwaveripple` output ✓
- Cross-correlogram: ±500ms window, 10ms bins, 100 shuffles ✓
- cogpy.coupling.cross_correlogram is well-tested ✓
The code looks correct. It's computing the right thing. If the
inputs are correct, the output should show coupling.
**The issue must be upstream.** Let me check the inputs.
Investigation pattern
The agent checked the most obvious suspect first (the coupling code) and ruled it out quickly. This is important — don't skip steps just because you have a hypothesis. Rule out the simple explanation before pursuing the complex one.
Phase 4: Trace upstream — ripple detection¶
Behind the scenes
→ pipeio_target_paths("sharpwaveripple", group="detect", member="sub-01")
→ reads SWR detection output for sub-01
→ [inspects ripple events: count, rate, frequency, amplitude]
Rate: 12.3/min — within expected range (10-15/min)
Mean frequency: 142 Hz — within expected range (100-300 Hz)
Duration: 45ms mean — within expected range (30-100ms)
→ note_create(note_type="idea",
tags=["observation", "investigate"],
series="coupling_spindle_ripple",
title="Investigation: ripple detection looks normal (12.3/min, 142 Hz)")
Agent: **Ripple detection check (sub-01):**
| Metric | Observed | Expected | Status |
|--------|----------|----------|--------|
| Rate | 12.3/min | 10-15/min | ✓ normal |
| Frequency | 142 Hz | 100-300 Hz | ✓ normal |
| Duration | 45 ms | 30-100 ms | ✓ normal |
Ripple detection looks fine. The events are physiologically plausible
and match the validated milestone evidence.
Moving to spindle detection.
Phase 5: Trace upstream — spindle detection¶
Behind the scenes
→ pipeio_target_paths("spectrogram_burst", group="detect", member="sub-01")
→ reads spindle detection output for sub-01
→ [inspects spindle events: count, rate, frequency, amplitude]
Rate: 25.1/min — UNUSUALLY HIGH (expected: 5-10/min)
Mean frequency: 22.4 Hz — OUTSIDE expected spindle band (10-16 Hz)
Duration: 0.3s mean — shorter than expected (0.5-2.0s)
Agent: **FOUND IT.**
**Spindle detection check (sub-01):**
| Metric | Observed | Expected | Status |
|--------|----------|----------|--------|
| Rate | **25.1/min** | 5-10/min | ✗ way too high |
| Frequency | **22.4 Hz** | 10-16 Hz | ✗ wrong band |
| Duration | **0.3 s** | 0.5-2.0 s | ✗ too short |
This is not spindle detection — this is beta oscillation detection.
25/min at 22 Hz with 0.3s duration is a textbook beta burst profile.
Let me check the spectrogram_burst config.
Behind the scenes
→ pipeio_config_read("spectrogram_burst")
Returns:
blob_detect:
freq_band: [15, 30] ← THIS IS THE PROBLEM
threshold_sd: 2.5
min_duration_s: 0.3
merge_gap_s: 0.1
→ note_create(note_type="idea",
tags=["observation", "investigate"],
series="coupling_spindle_ripple",
title="ROOT CAUSE: spectrogram_burst freq_band is [15,30] not [10,16]")
Agent: **Root cause identified.**
The `spectrogram_burst` config has:
```yaml
freq_band: [15, 30] # ← BETA band, not spindle band
```
It should be:
```yaml
freq_band: [10, 16] # ← spindle band (sigma)
```
**What happened:** the frequency band is set to 15-30 Hz (beta range)
instead of 10-16 Hz (spindle/sigma range). The pipeline is correctly
detecting beta oscillations — but calling them "spindles."
**Why the coupling is flat:** beta bursts have no temporal
relationship with hippocampal ripples. Spindles do. The
cross-correlogram is flat because there IS no coupling between
beta events and ripples — which is the correct result for the
wrong inputs.
The trap
The spindle-detection-validated milestone passed because the detection rate (25/min) wasn't checked against literature expectations at the time — or the validation was done with a different config that was later changed. This is why the agent-as-judge approach matters: pre-scripted validation might have caught this if it checked frequency, but the agent can catch it by reasoning about the full picture.
Human checkpoint
Human: How did this happen? The spindle validation used [10, 16].
Agent: Looking at the git history for the config file...
The config was changed after the spindle validation. It looks like
a subsequent analysis (beta burst exploration) modified freq_band
to [15, 30] and the change wasn't reverted. The spindle detection
milestone was validated with [10, 16], but the coupling analysis
ran with the modified [15, 30].
**This is a config drift issue** — the validated state and the
current pipeline state diverged.
**Proposed fix:**
1. Restore freq_band to [10, 16]
2. Re-run spectrogram_burst for all subjects
3. Re-run coupling_spindle_ripple
4. Check the cross-correlogram for the expected peak
Shall I proceed?
Phase 6: Root cause summary¶
Human: Yes, fix it. But first — is there a way to prevent this kind of
config drift in the future?
Agent: **Root cause trace:**
```
Symptom: flat cross-correlogram (coupling_spindle_ripple)
↓ not the coupling code (verified correct)
↓ not the ripple detection (12.3/min, 142 Hz — normal)
↓ IS the spindle detection (25/min, 22 Hz — wrong band)
Root: spectrogram_burst config freq_band changed from [10,16]
to [15,30] after validation, not reverted
```
**Prevention:** this is exactly what `pipeio_contracts_validate` is
designed for — it checks that pipeline configs match their documented
parameters. If the spindle detection milestone recorded the validated
config parameters (freq_band: [10, 16]) in its evidence, a
contracts check could detect the drift.
For now, let me fix and re-run.
Investigation pattern
The full diagnostic trace: symptom (flat correlogram) → rule out direct cause (coupling code is correct) → check input A (ripples are fine) → check input B (spindles are wrong) → trace to config (frequency band changed). The key insight: always trace the dependency chain systematically. Don't assume the most proximal component is the broken one.
Phase 7: Fix and re-run — iterate loop¶
Behind the scenes
→ pipeio_config_patch("spectrogram_burst",
registry_entry={"blob_detect": {"freq_band": [10, 16]}},
apply=True)
→ pipeio_run(flow="spectrogram_burst",
wildcards={"subject": "sub-*"})
→ pipeio_run_status(run_id)
Returns: completed, 0 errors
→ [spot-check: reads sub-01 spindle detection output]
Rate: 8.1/min ✓, Frequency: 12.8 Hz ✓, Duration: 0.8s ✓
→ pipeio_run(flow="coupling_spindle_ripple",
wildcards={"subject": "sub-*"})
→ pipeio_run_status(run_id)
Returns: completed, 0 errors
→ pipeio_target_paths("coupling_spindle_ripple", group="correlogram", member="sub-01")
→ reads cross-correlogram output
→ note_create(note_type="idea",
tags=["observation", "iterate"],
series="coupling_spindle_ripple",
title="Fix verified: coupling peak present after freq_band correction")
Agent: **Fix verified.**
**Spindle detection (sub-01 spot check):**
- Rate: 8.1/min (was 25.1/min) ✓
- Frequency: 12.8 Hz (was 22.4 Hz) ✓
- Duration: 0.8s (was 0.3s) ✓
**Cross-correlogram (sub-01):**
- **Peak at +95 ms** (spindle leading ripple) ✓
- Peak magnitude: z = 3.8 (well above shuffled baseline) ✓
- Peak width (FWHM): 48 ms ✓
**Comparison to literature:**
| Metric | Our result | Siapas 1998 | Sirota 2003 |
|--------|-----------|-------------|-------------|
| Peak timing | +95 ms | +50-150 ms | +80-120 ms |
| Significance | z=3.8 | p<0.01 | p<0.001 |
The coupling is exactly where the literature says it should be.
The fix worked. Want me to check all subjects and create a result note?
Human: Check all subjects, then record.
Behind the scenes
→ [reads cross-correlogram for all 5 subjects]
→ note_create(note_type="idea",
tags=["observation", "iterate"],
series="coupling_spindle_ripple",
title="Cross-subject coupling: all 5 subjects show peak at ~100ms")
Agent: **Cross-subject coupling results:**
| Subject | Peak (ms) | Magnitude (z) | Significant? |
|---------|-----------|---------------|-------------|
| sub-01 | +95 | 3.8 | Yes (p<0.001) |
| sub-02 | +82 | 3.2 | Yes (p<0.001) |
| sub-03 | +108 | 2.9 | Yes (p<0.01) |
| sub-04 | +91 | 3.5 | Yes (p<0.001) |
| sub-05 | +99 | 3.1 | Yes (p<0.001) |
| **Mean** | **+95 ± 9** | **3.3 ± 0.3** | **All significant** |
Consistent coupling peak across all subjects at +95 ± 9 ms,
matching Siapas 1998 (+50-150ms) and Sirota 2003 (+80-120ms).
Phase 8: Record — investigation trail and result¶
Behind the scenes
→ note_create(note_type="idea",
tags=["observation", "investigate", "decision"],
series="coupling_spindle_ripple",
title="Decision: spindle freq_band corrected from [15,30] to [10,16]")
→ note_create(note_type="result",
title="Spindle-ripple coupling validated — peak at +95ms matches literature",
series="coupling_spindle_ripple",
question=["H2", "H3"],
milestone="delta-ripple-coupling",
subjects=["sub-01", "sub-02", "sub-03", "sub-04", "sub-05"],
metric="coupling_peak_lag_ms",
value="+95 ± 9 ms (all subjects significant, p<0.001)",
confidence="validated")
Agent: **Recorded:**
1. **Investigation trail** (4 observation notes):
- Flat correlogram confirmed across all subjects
- Ripple detection verified normal
- Root cause: freq_band [15,30] instead of [10,16]
- Fix verified: coupling peak at +95ms after correction
2. **Decision note:** freq_band corrected, with root cause analysis
and prevention recommendation (contracts validation)
3. **Result note:** spindle-ripple coupling validated with
cross-subject metrics
The investigation trace is fully documented. Any future agent
encountering a flat correlogram will find the observation notes
explaining that freq_band misconfiguration was the cause.
Human checkpoint
Human: Update the coupling milestone and let's move on.
→ [updates plan/milestones.yml: delta-ripple-coupling → complete,
evidence: [result-arash-20260408-XXXXXX]]
Ecosystem coverage¶
| Subsystem | Tools used | Purpose in this scenario |
|---|---|---|
| pipeio | pipeio_target_paths, pipeio_mod_context, pipeio_config_read, pipeio_config_patch, pipeio_run, pipeio_run_status, pipeio_contracts_validate (recommended) |
Trace outputs, inspect code, read/fix config, re-run |
| codio | codio_get |
Verify coupling code is well-tested |
| biblio | (implicit) | Literature values for expected coupling peak |
| notio | note_create (observation × 4, decision × 1, result × 1) |
Investigation trail and formal evidence |
| questio | (milestone update) | Record coupling validation |
Loop patterns used¶
| Phase | Loop | Pattern |
|---|---|---|
| 1-2. Reproduce and scope | Investigate | Confirm symptom, determine scope (all subjects = systemic) |
| 3. Check coupling code | Investigate | Rule out direct cause |
| 4. Trace ripples | Investigate | Check input A — normal |
| 5. Trace spindles | Investigate | Check input B — anomalous → trace to config |
| 6. Root cause | Investigate | Config drift identified |
| 7. Fix and re-run | Iterate | Config patch → re-run → verify coupling peak |
| 8. Record | (post-loop) | Investigation trail + result note + milestone |
Key insight¶
Cross-flow debugging requires tracing the full dependency chain — the symptom is never where the cause is. The flat correlogram was in coupling_spindle_ripple, but the bug was in spectrogram_burst's config. The agent systematically eliminated suspects: coupling code → ripple detection → spindle detection → config. Without this discipline, the researcher might have spent hours debugging the coupling code or doubting the coupling hypothesis — when the real issue was a frequency band that drifted after validation.
This scenario also demonstrates the investigate → iterate transition: once the root cause was found (investigate loop complete), the agent entered the iterate loop to apply the fix, re-run, and verify. The two loops compose naturally.