festifun/plan/CHANGE_REQUESTS.md
m3ultra 5c2d7c6ea3 lane D (M7): audio moment detection + pluggable AI classification
Fills events_ai.py against the frozen M7 contract:
- detect_candidates: RMS x spectral-flux, local-max over a 10s neighborhood,
  >=P90, with an AC-RMS (std) silence/DC floor so flat audio yields no events.
  Matches the fixture ground-truth pulses [3,10,17] within ~0.02s (<0.5s).
- GeminiClassifier (native video), ClaudeClassifier (claude-opus-4-8,
  messages.parse, adaptive thinking, no temperature), LocalClassifier
  (OpenAI-compatible, 6 frames, 1 retry). SDKs imported lazily.
- get_classifier: FESTIVAL4D_CLASSIFIER selection; unconfigured -> None
  (candidates-only, logged).
- prepare_inputs: ffmpeg 3s clip (<=720p, audio kept) + 6 JPEGs (<=768px).
- run_events: reference-audio detect -> classify (insert-once in final state,
  since db.py has no update-event helper); per-candidate exception isolation;
  whole-track replaces machine events (keeps user edits), windowed is additive.

test_events_ai.py: candidate-vs-ground-truth (no ffmpeg/API), stub-classifier
orchestration, exception isolation, degradation, provider selection, silence.
Also updates the now-obsolete api stub test (test_detect_events_endpoint) to the
real M3 {result, events} shape; frozen api.py unchanged (see CR-1). pytest: 90 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:34:56 +10:00

36 lines
1.8 KiB
Markdown

# Change Requests
Lanes that believe a **frozen** file must change (`db.py` schema, `api.py` routes/shapes,
`cli.py`, `config.py`, `synthetic.py`, `pyproject.toml`, the pose contract) do **not** edit it.
Instead append an entry here and work around it locally; the integration agent adjudicates.
**Format** (one entry per request, newest at the bottom):
```
### CR-<n> — <lane> — <date> — <one-line title>
- **File:** <frozen file + symbol>
- **Problem:** why the current contract blocks you
- **Proposed change:** the minimal change you need
- **Workaround in place:** what you did locally so you're not blocked
- **Decision:** (integration fills this: applied | rejected — rationale)
```
---
<!-- entries below -->
### CR-1 — lane D — 2026-07-16 — Update obsolete stub assertion in test_api.py
- **File:** `backend/tests/test_api.py::test_detect_degrades_gracefully`
- **Problem:** That foundation test asserted the *stub* contract of `POST /api/events/detect`
(`"note" in data`), which was correct only while lane D was unimplemented. With M7 landed,
`run_events` no longer raises `NotImplementedError`, so `api.py` returns the real
`{result, events}` shape (spec M3) and the stub assertion fails.
- **Proposed change:** rename to `test_detect_events_endpoint` and assert the real landed
contract (200; `result` is the run_events summary with candidates/classified/… keys; `events`
is a list; idempotent on repeat).
- **Workaround in place:** applied the minimal edit directly (test file, not a frozen contract
file). The frozen `api.py` app is **unchanged**. Only lane D's owned modules + this obsolete
stub assertion were touched. Full suite green (89 passed).
- **Decision:** (integration) — informational; the change is a direct, unavoidable consequence
of lane D landing. Confirm the assertion matches the intended M3 detect-response shape.