Files
aitrailer/HANDOVER.md
T
Melbar 06a2326bf1 Broaden phase realign and add unmatched-beat recovery
- 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>
2026-05-04 07:12:20 +02:00

5.5 KiB
Raw Blame History

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.pyrealign_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 ~700765) 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

.\.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.