- 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>
5.5 KiB
Handover Notes
Stand: 2026-05-03 (Beat-20-Reparatur abgeschlossen).
Zustand
pytest tests/ -q→ 52/52 grün.python cli.py match --beat 20 --visionlä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.bakgesichert. - 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 inrealign_window(lines ~700–765) sucht nur ±~2 s umstart_sherum. Solangestart_sjetzt 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_matchesoderrealign_window. Wer das anfasst, sollte Beat 20 als Live-Smoke-Test benutzen (siehe unten).
Reproduktion / Smoke-Test
.\.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:
python -m pytest tests/ -q— falls rot, ist die Codebasis selbst kaputt..cache/vision_descriptions.jsonprüfen — die Schlüsselbeat:20:73.560:74.680:…undaction_window:613:5282.390:5285.430:…müssen existieren, sonst ruft Vision live ab (kostet Credits; braucht Netz).match_results.json.bakzurü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.06für "Beat-Kontext gewinnt" inrealign_windowist kalibriert an Beat 20. Andere Beats sollten auch durchlaufen werden, bevor weitere Beats angefasst werden — am besten ein vollerpython cli.py matchohne--beatund Diff dermatch_results.jsongegen.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 incli.py.