Fix forehead_touch action group + always-fresh cutter assets

1. Action-group classifier conflated object-touches and person-touches.
   "man touches the red door with a small object" was being tagged as
   forehead_touch because "touch" was in the forehead_touch needles set.
   That made the realign pass yank Beat 16 from scene 451 (correct: man
   painting red door, IV stand) over to scene 623 (woman/man in bed) —
   a totally wrong shot at score 0.344.

   Fix: removed generic "touch*" verbs from forehead_touch's needle set.
   forehead_touch is now added in _semantic_action_groups() only when a
   touch verb is paired with an explicit body-part target (forehead,
   face, cheek, head, hand, ...) and not paired with an object target
   (door, handle, brush, tool, lock, ...).

   Effect on Beat 16 after `match --beat 16 --vision`:
   scene 623 in=5476.28 score=0.344 -> scene 451 in=3912.48 score=0.626.

2. Cutter-report stills/clips were keyed by source-video mtime, so a
   match-position change without a video change served stale frames from
   the previous match. Dropped the mtime cache; both extractors now
   render fresh every time. Slower (~minute per full regen) but correct.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Melbar
2026-05-05 05:23:24 +02:00
parent dbadc3fc26
commit 8aa6fe8323
52 changed files with 73 additions and 46 deletions
+4 -11
View File
@@ -136,18 +136,12 @@ CLIP_WIDTH = 480
CLIP_MAX_DURATION_S = 30.0
def _stale(out: Path, src: Path) -> bool:
try:
return not (out.exists() and out.stat().st_mtime >= src.stat().st_mtime and out.stat().st_size > 0)
except OSError:
return True
def extract_still(video_path: Path, t_s: float, out: Path) -> bool:
"""Always render fresh. The match position can change without the source
video changing, so a mtime-based cache would silently serve stale frames
from the previous match. The cutter expects bit-current previews."""
if not video_path.exists():
return False
if not _stale(out, video_path):
return True
out.parent.mkdir(parents=True, exist_ok=True)
cmd = [
"ffmpeg", "-y", "-loglevel", "error",
@@ -166,10 +160,9 @@ def extract_still(video_path: Path, t_s: float, out: Path) -> bool:
def extract_clip(video_path: Path, start_s: float, duration_s: float, out: Path) -> bool:
"""Always render fresh — see extract_still for rationale."""
if not video_path.exists():
return False
if not _stale(out, video_path):
return True
out.parent.mkdir(parents=True, exist_ok=True)
cmd = [
"ffmpeg", "-y", "-loglevel", "error",