06a2326bf1
- Phase realign for matched results: drop the "long scene" gate (>1.6x segment, >=6s) in favor of "scene has any meaningful slack beyond the segment". Already-confirmed segments in tight scenes are still skipped to protect strong matches. A repair is only committed if the new score is not meaningfully worse than the original (>=score-0.02). - Recovery stage for unmatched beats: vibe-check (CV) feeds top-K candidate scenes into the semantic action-window search; CV alignment + vision phase validate gate decide whether the candidate becomes a provisional match. Beats without scoreable visual content (logos, title cards, full fades) remain unmatched by design. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
126 lines
5.5 KiB
Markdown
126 lines
5.5 KiB
Markdown
# Handover Notes
|
||
|
||
Stand: 2026-05-03 (Beat-20-Reparatur abgeschlossen).
|
||
|
||
## Zustand
|
||
|
||
- `pytest tests/ -q` → 52/52 grün.
|
||
- `python cli.py match --beat 20 --vision` läuft erfolgreich durch und schreibt
|
||
einen confirmed Match (Score 0.6632, scene 613, in=5284.706s, dur=0.88s).
|
||
- Vorheriger Cache wurde nach `.cache/match_results.json.bak` gesichert.
|
||
- Kein offener PR; lokale Änderungen sind committed (siehe letzter Commit).
|
||
|
||
## Was zuletzt geändert wurde und warum
|
||
|
||
### 1. `cli.py` — `realign_window` wählt das Action-Window pro Segment
|
||
|
||
In `_filter_semantically_invalid_vision_matches.realign_window`:
|
||
|
||
- **Vorher:** `find_action_window_in_scene(action_beat or check_beat, …)` — bei
|
||
segmentierten Beats wurde immer der ganze Beat als semantischer Kontext
|
||
benutzt. Das hat für Beat 20 die Source-Position auf die Kuss-Phase
|
||
(5270 s) gelegt, obwohl das *sichtbare* Segment nur "approaching and pulling
|
||
apart" zeigt — diese Phase liegt im Source erst um 5284 s.
|
||
- **Jetzt:** Es werden zwei Fenster gesucht (Segment-Beschreibung *und* Beat-
|
||
Beschreibung). Der Beat-Kontext gewinnt nur bei deutlichem (>0.06) Score-
|
||
Vorsprung. Der Trailer-Offset-Shift (`visible_content_offset`) wird nur
|
||
angewendet, wenn tatsächlich der Beat-Kontext benutzt wurde — sonst zeigt
|
||
das Segment-Fenster bereits auf die richtige Phase.
|
||
|
||
Effekt für Beat 20: 5270.118 → 5284.706, Score 0.6449 (provisional) → 0.6632
|
||
(confirmed).
|
||
|
||
### 2. `cli.py` — Filter-/Repair-Stufe ist crash-tolerant
|
||
|
||
`_filter_semantically_invalid_vision_matches` hat den Per-Result-Body in eine
|
||
lokale Funktion `_filter_repair_one` herausgezogen und in einen try/except
|
||
verpackt. Wenn die Reparatur abbricht (z. B. weil Vision-API mitten in der
|
||
Antwort wegfällt), wird der bisher gecachte Treffer behalten statt komplett
|
||
verworfen.
|
||
|
||
### 3. `src/llm/vision_cache.py` — Vision-Retry für Lesefehler
|
||
|
||
`_call_vision_model` fängt jetzt zusätzlich `TimeoutError`,
|
||
`socket.timeout`, `ConnectionError` und `OSError` während des Antwort-Lesens
|
||
und retryt mit demselben Backoff wie HTTP-/URL-Fehler. Die Auslöse-Bedingung
|
||
war ein 24-h-DSL-Disconnect mitten im Lauf; davor wurde der Match-Lauf hart
|
||
abgebrochen und der Cache stand auf "kein Match".
|
||
|
||
### 4. `README.md`
|
||
|
||
Zwei kurze Absätze ergänzt, die (1) die Segment-vs-Beat-Window-Auswahl und
|
||
(2) das neue Crash-/Netzfehler-Verhalten beschreiben.
|
||
|
||
## Nicht angefasst, aber relevant für die Übergabe
|
||
|
||
- Der **vollständige FFmpeg-Vollscan** liefert für Beat 20 weiterhin keinen
|
||
bestätigten Treffer (final score 0.419 < provisional 0.430). Den
|
||
Confirmed-Match liefert die Action-Window-Reparatur. Das ist erwartet:
|
||
das sichtbare Segment ist visuell sehr generisch (Two-Shot Profil mit
|
||
unscharfem Hintergrund), die korrekte Phase fällt erst durch die
|
||
semantische Aktionsbeschreibung auf.
|
||
- Die `candidate_points`-Schleife in `realign_window` (lines ~700–765) sucht
|
||
nur ±~2 s um `start_s` herum. Solange `start_s` jetzt aus dem Segment-
|
||
Fenster kommt, liegt der korrekte Source-Punkt in diesem Bereich. Wenn
|
||
künftig Beats mit längeren visiblen Inseln auftauchen, kann diese Range
|
||
zu eng werden — dann den Suchradius erweitern statt das Window-Picking
|
||
rückgängig machen.
|
||
- Es gibt **keine Tests** für `_filter_semantically_invalid_vision_matches`
|
||
oder `realign_window`. Wer das anfasst, sollte Beat 20 als Live-Smoke-Test
|
||
benutzen (siehe unten).
|
||
|
||
## Reproduktion / Smoke-Test
|
||
|
||
```powershell
|
||
.\.venv\Scripts\Activate.ps1
|
||
python cli.py match --beat 20 --vision
|
||
```
|
||
|
||
Erwartet: `Beat 20: realigned semantically valid long scene by motion/action
|
||
windows`, danach `is_confirmed: true` für Beat 20 in
|
||
`.cache/match_results.json` mit `in_point_s ≈ 5284.7` und `match_score ≥ 0.65`.
|
||
|
||
Wenn das fehlschlägt:
|
||
|
||
1. `python -m pytest tests/ -q` — falls rot, ist die Codebasis selbst kaputt.
|
||
2. `.cache/vision_descriptions.json` prüfen — die Schlüssel
|
||
`beat:20:73.560:74.680:…` und `action_window:613:5282.390:5285.430:…` müssen
|
||
existieren, sonst ruft Vision live ab (kostet Credits; braucht Netz).
|
||
3. `match_results.json.bak` zurückspielen, falls der Cache zerschossen ist.
|
||
|
||
## Aktuelle Coverage (vor neuestem Lauf)
|
||
|
||
```
|
||
total beats: 25
|
||
matched: 20 (5 confirmed, 15 provisional)
|
||
unmatched: beats 0, 2, 21, 23, 24
|
||
```
|
||
|
||
Beat 0 ist das SHO-Logo (kein Source-Match möglich, korrekt).
|
||
Beats 22/23/24 haben keine sichtbaren Inseln (Endcredits/Title) — auch
|
||
korrekt unmatched.
|
||
Beat 2 und Beat 21 sind die echten Recovery-Kandidaten; die neue
|
||
Recovery-Stufe versucht sie beim nächsten `match`-Lauf nachzuziehen.
|
||
|
||
## Offene Risiken / Bekannte Schwächen
|
||
|
||
- Die Schwelle `0.06` für "Beat-Kontext gewinnt" in `realign_window` ist
|
||
kalibriert an Beat 20. Andere Beats sollten auch durchlaufen werden, bevor
|
||
weitere Beats angefasst werden — am besten ein voller `python cli.py match`
|
||
ohne `--beat` und Diff der `match_results.json` gegen `.bak`.
|
||
- Die Filter-/Repair-Stufe kann durch Vision-Calls minutenlang laufen. Das
|
||
ist nicht neu, aber bei Netzproblemen sehr sichtbar.
|
||
- Die `_filter_repair_one`-Funktion bekommt viele Argumente durchgereicht
|
||
(closure-Variablen aus dem Parent). Bei einer nächsten Iteration könnte das
|
||
in eine kleine Klasse umgebaut werden.
|
||
|
||
## Useful greps
|
||
|
||
- `find_action_window_in_scene` — semantische Action-Window-Suche (Vision).
|
||
- `_reference_scoreable_segments` — bestimmt die sichtbaren Inseln eines
|
||
Beats.
|
||
- `estimate_usable_source_duration` — kürzt Match-Clips, wenn die Source
|
||
vor Beat-Ende in eine andere Phase wechselt.
|
||
- `_filter_semantically_invalid_vision_matches` — Eintrittspunkt der
|
||
Repair-Stufe in `cli.py`.
|