diff --git a/.agents/skills/changelog-video/SKILL.md b/.agents/skills/changelog-video/SKILL.md index 93b7ac7225..6eb6bb73fb 100644 --- a/.agents/skills/changelog-video/SKILL.md +++ b/.agents/skills/changelog-video/SKILL.md @@ -147,7 +147,7 @@ scaffold. ### 6 · Gates (all green before presenting) -1. `bun run --cwd packages/cli hyperframes check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"` (or the installed +1. `node packages/cli/bin/hyperframes.mjs check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"` (or the installed `hyperframes` CLI from the repo-local `skills/hyperframes-cli/` skill) — 0 errors (contrast: dim text ≥ .66 alpha; scene content stays above the caption rail). Do NOT reach for diff --git a/.agents/skills/changelog-video/examples/master-skeleton.html b/.agents/skills/changelog-video/examples/master-skeleton.html index 401b22285a..53b23add95 100644 --- a/.agents/skills/changelog-video/examples/master-skeleton.html +++ b/.agents/skills/changelog-video/examples/master-skeleton.html @@ -23,7 +23,11 @@ border: 1px solid rgba(255,255,255,.14); border-radius: 12px; padding: 10px 18px; } #progress { top: 58px; right: 76px; display: flex; gap: 10px; } #progress .dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(245,246,244,.18); } - .slide { position: absolute; inset: 0; opacity: 0; } + /* The clip element itself carries NO opacity base and is never targeted by GSAP: + the framework owns a clip's visibility window, and `gsap_animates_clip_element` + is an ERROR-severity lint rule. Seams animate the inner .swrap instead. */ + .slide { position: absolute; inset: 0; } + .swrap { position: absolute; inset: 0; opacity: 0; } .sec-chip { position: absolute; top: 128px; left: 76px; font-family: 'TT Norms Mono', monospace; font-size: 22px; letter-spacing: .05em; color: rgba(245,246,244,.72); background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.14); @@ -52,17 +56,23 @@
- +
+ +
-
01 ·
-
- +
+
01 ·
+
+ +
@@ -81,6 +91,15 @@ tl.to('#kicker-chip', { autoAlpha: 1, y: 0, duration: 0.5, ease: 'power3.out' }, 0.1); // tl.set('#dot1', { backgroundColor: '#f5f6f4' }, ); … + // Title entry — authored HERE, never stamped (seam-stamp owns every other + // wrapper entry). immediateRender:false is load-bearing: seam-stamp emits a + // zero-duration tl.set(#w-title, autoAlpha:0) at the title's cut, and a + // zero-duration GSAP tween immediate-renders at BUILD time. Without the flag + // this fromTo captures that 0 as its start value and tweens 0 → 0, which the + // seam gate reports as `exit-visible … op 0.00` while exit-vector PASSES. + tl.fromTo('#w-title', { autoAlpha: 0 }, + { autoAlpha: 1, duration: 0.30, ease: 'power2.out', immediateRender: false }, 0); + // ══ per-scene internal beats ══ // init states: gsap.set(...) at build time; motion: sequential tl.to(...) only. // Every beat lands on a vo-words.json timestamp (local = master − scene start). diff --git a/.agents/skills/changelog-video/references/build-spec.md b/.agents/skills/changelog-video/references/build-spec.md index 395d6bf8dd..6a70d7a621 100644 --- a/.agents/skills/changelog-video/references/build-spec.md +++ b/.agents/skills/changelog-video/references/build-spec.md @@ -81,11 +81,18 @@ otherwise) and must stay flat 2D (no 3D ancestors). ## Seams + internal life (doctrine mechanics) - `ledger.json`: every ordinary seam `cut-the-curve LEFT` (x, dir −1), exit - and entry selectors = the slide wrappers. Outro entry `travel: 8`. + and entry selectors = the inner `.swrap` wrappers (`#w-*`), NEVER the `#s-*` + clip element — `gsap_animates_clip_element` is an error-severity lint rule and + every stamped seam writes `autoAlpha`. Outro entry `travel: 8`. - `seam-stamp.mjs --ledger ledger.json --write index.html` owns ALL wrapper entries/exits — author none yourself. Title (film open) authors its own - entry only. -- Slides: CSS `opacity: 0` base; `data-start` = exactly the cut time. + entry only, and that entry MUST pass `immediateRender: false`: the stamped + zero-duration `tl.set(autoAlpha: 0)` at the title's cut renders at BUILD time, + so an entry without the flag captures 0 as its start and tweens 0 → 0. The + symptom is `exit-visible … op 0.00` with `exit-vector` PASSING — moving but + invisible. Neither a CSS opacity base nor a longer clip window fixes it. +- Slides: the `.swrap` carries the CSS `opacity: 0` base, not the `.slide` clip + element; `data-start` = exactly the cut time. - Each scene's shell (chip, headline, mock chrome, initial state) is COMPOSED at local t=0 — the wrapper flies it in. Internal reveals start ≥0.4s after the cut and end ≥0.45s before the next cut (stamped exits diff --git a/.agents/skills/motion-doctrine/scripts/seam-gate.mjs b/.agents/skills/motion-doctrine/scripts/seam-gate.mjs index 569c91bbbd..16893a18a7 100644 --- a/.agents/skills/motion-doctrine/scripts/seam-gate.mjs +++ b/.agents/skills/motion-doctrine/scripts/seam-gate.mjs @@ -73,9 +73,27 @@ async function ensureServer() { delete env.HYPERFRAME_RUNTIME_URL; // wrong value fails silently as 200 HTML // `preview` backgrounds itself when stdin/stdout aren't TTYs, which they never are here: the // launcher would exit 0 before the server is up and detach it out of our process group. + // Default to the REPO-LOCAL CLI whenever this skill is running from its repo + // checkout. The gate this script backs is defined against that build, and the + // skill forbids `npx hyperframes@latest` for it, so defaulting to the published + // package here would let the two gates measure different runtimes. npx stays the + // fallback for a copy of the skill living outside the repo. + const repoCli = join( + import.meta.dirname, + "..", + "..", + "..", + "..", + "packages", + "cli", + "bin", + "hyperframes.mjs", + ); const cmd = flag( "server-cmd", - `npx --yes hyperframes preview --foreground --no-open --port ${port}`, + existsSync(repoCli) + ? `node "${repoCli}" preview --foreground --no-open --port ${port}` + : `npx --yes hyperframes preview --foreground --no-open --port ${port}`, ); const child = spawn("sh", ["-c", cmd.replace(/\{port\}/g, String(port))], { cwd: project, diff --git a/.claude/skills/changelog-video/SKILL.md b/.claude/skills/changelog-video/SKILL.md index 93b7ac7225..6eb6bb73fb 100644 --- a/.claude/skills/changelog-video/SKILL.md +++ b/.claude/skills/changelog-video/SKILL.md @@ -147,7 +147,7 @@ scaffold. ### 6 · Gates (all green before presenting) -1. `bun run --cwd packages/cli hyperframes check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"` (or the installed +1. `node packages/cli/bin/hyperframes.mjs check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"` (or the installed `hyperframes` CLI from the repo-local `skills/hyperframes-cli/` skill) — 0 errors (contrast: dim text ≥ .66 alpha; scene content stays above the caption rail). Do NOT reach for diff --git a/.claude/skills/changelog-video/examples/master-skeleton.html b/.claude/skills/changelog-video/examples/master-skeleton.html index 401b22285a..53b23add95 100644 --- a/.claude/skills/changelog-video/examples/master-skeleton.html +++ b/.claude/skills/changelog-video/examples/master-skeleton.html @@ -23,7 +23,11 @@ border: 1px solid rgba(255,255,255,.14); border-radius: 12px; padding: 10px 18px; } #progress { top: 58px; right: 76px; display: flex; gap: 10px; } #progress .dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(245,246,244,.18); } - .slide { position: absolute; inset: 0; opacity: 0; } + /* The clip element itself carries NO opacity base and is never targeted by GSAP: + the framework owns a clip's visibility window, and `gsap_animates_clip_element` + is an ERROR-severity lint rule. Seams animate the inner .swrap instead. */ + .slide { position: absolute; inset: 0; } + .swrap { position: absolute; inset: 0; opacity: 0; } .sec-chip { position: absolute; top: 128px; left: 76px; font-family: 'TT Norms Mono', monospace; font-size: 22px; letter-spacing: .05em; color: rgba(245,246,244,.72); background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.14); @@ -52,17 +56,23 @@
- +
+ +
-
01 ·
-
- +
+
01 ·
+
+ +
@@ -81,6 +91,15 @@ tl.to('#kicker-chip', { autoAlpha: 1, y: 0, duration: 0.5, ease: 'power3.out' }, 0.1); // tl.set('#dot1', { backgroundColor: '#f5f6f4' }, ); … + // Title entry — authored HERE, never stamped (seam-stamp owns every other + // wrapper entry). immediateRender:false is load-bearing: seam-stamp emits a + // zero-duration tl.set(#w-title, autoAlpha:0) at the title's cut, and a + // zero-duration GSAP tween immediate-renders at BUILD time. Without the flag + // this fromTo captures that 0 as its start value and tweens 0 → 0, which the + // seam gate reports as `exit-visible … op 0.00` while exit-vector PASSES. + tl.fromTo('#w-title', { autoAlpha: 0 }, + { autoAlpha: 1, duration: 0.30, ease: 'power2.out', immediateRender: false }, 0); + // ══ per-scene internal beats ══ // init states: gsap.set(...) at build time; motion: sequential tl.to(...) only. // Every beat lands on a vo-words.json timestamp (local = master − scene start). diff --git a/.claude/skills/changelog-video/references/build-spec.md b/.claude/skills/changelog-video/references/build-spec.md index 395d6bf8dd..6a70d7a621 100644 --- a/.claude/skills/changelog-video/references/build-spec.md +++ b/.claude/skills/changelog-video/references/build-spec.md @@ -81,11 +81,18 @@ otherwise) and must stay flat 2D (no 3D ancestors). ## Seams + internal life (doctrine mechanics) - `ledger.json`: every ordinary seam `cut-the-curve LEFT` (x, dir −1), exit - and entry selectors = the slide wrappers. Outro entry `travel: 8`. + and entry selectors = the inner `.swrap` wrappers (`#w-*`), NEVER the `#s-*` + clip element — `gsap_animates_clip_element` is an error-severity lint rule and + every stamped seam writes `autoAlpha`. Outro entry `travel: 8`. - `seam-stamp.mjs --ledger ledger.json --write index.html` owns ALL wrapper entries/exits — author none yourself. Title (film open) authors its own - entry only. -- Slides: CSS `opacity: 0` base; `data-start` = exactly the cut time. + entry only, and that entry MUST pass `immediateRender: false`: the stamped + zero-duration `tl.set(autoAlpha: 0)` at the title's cut renders at BUILD time, + so an entry without the flag captures 0 as its start and tweens 0 → 0. The + symptom is `exit-visible … op 0.00` with `exit-vector` PASSING — moving but + invisible. Neither a CSS opacity base nor a longer clip window fixes it. +- Slides: the `.swrap` carries the CSS `opacity: 0` base, not the `.slide` clip + element; `data-start` = exactly the cut time. - Each scene's shell (chip, headline, mock chrome, initial state) is COMPOSED at local t=0 — the wrapper flies it in. Internal reveals start ≥0.4s after the cut and end ≥0.45s before the next cut (stamped exits diff --git a/.claude/skills/motion-doctrine/scripts/seam-gate.mjs b/.claude/skills/motion-doctrine/scripts/seam-gate.mjs index 569c91bbbd..16893a18a7 100644 --- a/.claude/skills/motion-doctrine/scripts/seam-gate.mjs +++ b/.claude/skills/motion-doctrine/scripts/seam-gate.mjs @@ -73,9 +73,27 @@ async function ensureServer() { delete env.HYPERFRAME_RUNTIME_URL; // wrong value fails silently as 200 HTML // `preview` backgrounds itself when stdin/stdout aren't TTYs, which they never are here: the // launcher would exit 0 before the server is up and detach it out of our process group. + // Default to the REPO-LOCAL CLI whenever this skill is running from its repo + // checkout. The gate this script backs is defined against that build, and the + // skill forbids `npx hyperframes@latest` for it, so defaulting to the published + // package here would let the two gates measure different runtimes. npx stays the + // fallback for a copy of the skill living outside the repo. + const repoCli = join( + import.meta.dirname, + "..", + "..", + "..", + "..", + "packages", + "cli", + "bin", + "hyperframes.mjs", + ); const cmd = flag( "server-cmd", - `npx --yes hyperframes preview --foreground --no-open --port ${port}`, + existsSync(repoCli) + ? `node "${repoCli}" preview --foreground --no-open --port ${port}` + : `npx --yes hyperframes preview --foreground --no-open --port ${port}`, ); const child = spawn("sh", ["-c", cmd.replace(/\{port\}/g, String(port))], { cwd: project,