fix(skill): w2h-verify v2 — kill false positives from real-agent debrief

A fresh agent session ran the v1 verify script and the disclosure pasted
into their final summary showed 3 FAIL rows for things that weren't
actually defects:

  Headline font-size: flagged Beat 2 (wordmark SVG), Beat 3 (UI grid),
    Beat 5 (terminal). None of these legitimately have text headlines.
  Timeline coverage: flagged 5/6 beats because the script's regex only
    saw `tl.X(..., 2.5)` literal positions and missed forEach loops,
    variable-position tweens, and long-duration scaler tweens.
  Beat durations: flagged 2 beats because my "duration X.Xs near beat
    label" fallback false-matched non-beat durations
    (e.g., "shader runs — duration 0.7s" near a "Beat 1" mention).

The agent had to write ~5 paragraphs defensively justifying each false
FAIL. That's friction we can fix.

Tested against the agent's actual project (huly-launch-v4): went from
4 FAIL (3 false positives + 1 real bare-table parser miss) to 0 FAIL.
Also re-verified huly-v3 still correctly catches its 3 real issues
(48px wordmark, missing shaders, 3 SFX drifts) — no regression.

**Brand visuals check**

Switched from "≥30% asset usage" (gameable, rewards quantity over quality)
to "at least 1 beat references a captured hero/image/svg" — quality
signal that's cheap to satisfy when real, hard to fake. Excludes fonts,
logos, favicons, contact-sheets.

**Headline check**

Now only flags beats where the LARGEST font-size is in the 40–<80px
range — the "aspiring headline but too small" zone. Below 40px = beat
has no text headline by design (terminal, UI labels, SVG-only); skip.
≥80px = proper headline; pass. Eliminates the false positives on
SVG-dominated and UI-grid beats while still catching the real "headline
too small" failure (Beat 4 at 72px in this run; Beat 1 wordmark at 48px
in another).

**Timeline coverage check**

Three improvements:
1. Detects forEach loops + for-loops containing tl.X() calls — beats
   with these have events at positions the static parser can't read;
   mark as INFO-skipped rather than failed.
2. Detects long-duration tweens — if a single tween's duration covers
   ≥70% of the beat duration (camera dolly, breathing animation), the
   beat has full coverage via persistent motion; skip the position check.
3. New paren-balanced parser for extracting tl.X() position arguments —
   the v1 regex was matching `rgba(86,131,218,0.35)` and capturing 0.35
   as a tween position. The new parser walks paren depth and only
   captures top-level trailing numeric args. No more rgba false matches.

**Shader transitions check**

Two fixes:
1. Filter out inventory lines — lines listing 3+ shader names are
   "what's available," not "what's planned for use." Real use
   mentions one or two shaders per line.
2. Apply the same SFX-context exclusion to the declared side that the
   present-check side already had — "glitch" inside `sfx/glitch-1.mp3`
   no longer counts as a declared shader transition.

For huly-v3: was 6 declared (1 phantom from inventory + 5 + glitch
from SFX), now 2 declared (light-leak, cinematic-zoom) — matches the
storyboard's actual plan.

**Beat duration check**

1. Dropped the "duration X.Xs within 200 chars of beat label" fallback
   — too loose; matched shader durations, animation durations, anything
   labeled "duration". This was the source of the 0.70s misread in the
   debrief.
2. Added a bare-number timing-table parser for the format
   `| 1 | 0.00s | 5.20s | 5.20s | ... |` (with optional `>` blockquote
   prefix). Computes duration = end - start.
3. Added a negative lookahead so `\bB3\b` doesn't false-match "B3.1"
   sub-beats and grab the wrong row.
4. Filter buildBeatIds to only numbered beats — skips the root
   composition (`data-composition-id="main"`) so it doesn't inflate
   "parseable" count.

**Brand visuals + asset count**

Excluded fonts/ subdirectory (always-used via @font-face → would
always pass) and contact-sheet-*.jpg (pipeline outputs, not website
inputs). Both inflated the denominator and weakened the signal.

**Edge case fixes**

- Removed `basename` unused import (oxlint).
- Fixed shader-name substring overlap: longest-name-first matching so
  "cross-warp-morph" doesn't double-count as "cross-warp".
- SFX timestamps now collect ALL audio tags per file (multi-timestamp
  SFX like click×3); picks closest index timestamp to each storyboard
  timestamp instead of just keeping the last.

**Step 6 doc**

Updated the skill's "w2h-verify — the source of truth" section to
describe the new checks accurately and what failure mode each catches.

2 files changed, +486/-109. Format + lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ukimsanov
2026-05-22 09:03:35 -07:00
co-authored by Claude Opus 4.7
parent 190f1ec71a
commit f4a7961bc1
2 changed files with 490 additions and 113 deletions
@@ -27,12 +27,14 @@ Score each item 15. If any item scores below 3, fix it before continuing. **D
The skill ran for months on agents reading "REQUIRED" and skipping anyway. The verify script ends that — it computes facts the agent cannot fudge:
- **Asset usage %** (assets referenced in compositions ÷ assets captured)
- **Shader transitions consistency** (shaders declared in STORYBOARD.md vs shaders present in index.html)
- **SFX timestamp drift** (storyboard `t=X.Xs` vs index.html `data-start=X.X`)
- **animation-map.json existence**
- **Rendered MP4 existence**
- **Required artifacts present** (STORYBOARD.md, DESIGN.md, SCRIPT.md, index.html)
- **Brand visuals used** — at least 1 beat must reference a captured `hero-*`, `image-*`, or `svgs/*.svg` asset (logo doesn't count). Catches the "9% asset usage / brand isn't visually present" failure.
- **Headline font-size** — per-beat, the largest CSS `font-size` must be ≥80px. Catches the "headlines too small to read" failure that surfaces later as inspect `clipped_text` errors.
- **Timeline coverage** — per-beat, GSAP event positions must span ≥70% of the beat's `data-duration`. Catches "webpage not shot" failures where the beat has entrance tweens then goes static.
- **Shader transitions consistency** — shaders declared in STORYBOARD.md must appear in index.html (with HyperShader runtime present, not just as SFX file references).
- **SFX timestamp drift** — storyboard `t=X.Xs` vs index.html `data-start=X.X`, picks the closest index timestamp per file for multi-timestamp SFX.
- **Beat duration consistency** — storyboard's beat ranges (`B4 — Name | 16.600 21.000s |`) must match `data-duration` in index.html within ±0.5s. Catches storyboard staleness.
- **Rendered MP4 existence** — INFO only; flagged when claiming verified motion without rendering.
Run it as the LAST gate in your DoD pass, after fixing everything else: