# Tasks: 808 Bass Sidechain Ducking

## Phase 1: Schema — Foundation

- [x] 1.1 Add `CCEvent` dataclass (`controller: int`, `time: float`, `value: int`) to `src/core/schema.py`
- [x] 1.2 Add `midi_cc: list[CCEvent] = field(default_factory=list)` to `ClipDef` in `src/core/schema.py`
- [x] 1.3 Update `asdict` if used; verify `song.validate()` passes with empty `midi_cc`

## Phase 2: Kick Cache + CC Generation

- [x] 2.1 Add constants `_KICK_CONFIDENCE_THRESHOLD=0.6`, `_CC11_DIP=50`, `_CC11_HOLD=0.02`, `_CC11_RELEASE=0.18` to `scripts/compose.py`
- [x] 2.2 Add `_get_kick_cache(drumloop_paths: list[str], bpm: float) -> dict[str, list[float]]` to `scripts/compose.py`
- [x] 2.3 Modify `build_bass_track()` to accept `kick_cache: dict[str, list[float]]` parameter; generate CC events for kicks in range
- [x] 2.4 Update `main()` to build kick cache from drumloop paths and pass to `build_bass_track()`

## Phase 3: Builder CC Emission

- [x] 3.1 Modify `_build_midi_source()` in `src/reaper_builder/__init__.py` to merge `notes + cc` events and emit `E B0 0B {value:02x}` lines
- [x] 3.2 Verify delta cursor correctly advances across CC events (CC events contribute zero ticks)

## Phase 4: Testing

- [x] 4.1 Unit test `CCEvent` dataclass round-trip in `tests/test_schema.py`
- [x] 4.2 Unit test `_build_midi_source()` emits `B0 0B` lines for clips with `midi_cc`
- [x] 4.3 Integration test `build_bass_track()` populates `midi_cc` when kick cache present
- [x] 4.4 Regression: run existing 261 tests, verify all pass unchanged
