Commit Graph
5 Commits
Author SHA1 Message Date
James b7769b2364 feat(skills): remotion-to-hyperframes SKILL.md + orchestrator (7/7)
The leaf PR. Replaces the placeholder SKILL.md from PR 1 with the real
5-step workflow that loads the per-topic references on demand
(skill-creator's progressive-disclosure pattern), and adds a top-level
orchestrator that runs every tier and reports a pass/fail summary.

SKILL.md changes:
  - Frontmatter unchanged from PR 1 (already covers the trigger phrases
    and out-of-scope cases)
  - Body rewritten as a 5-step workflow:
      1. Lint (load escape-hatch.md if blockers)
      2. Plan (load api-map.md, then per-topic references on demand)
      3. Generate (HF index.html with paused GSAP timeline)
      4. Validate (render_diff.sh against per-tier threshold)
      5. Document gaps (TRANSLATION_NOTES.md if needed)
  - Includes a "Source contains -> Load reference" table so the agent
    only loads the references the source actually needs
  - Documents the validated baseline numbers (T1 0.974, T2 0.985,
    T3 0.953, T4 8/8) so reviewers can reproduce
  - Calls out the critical Remotion encoder config (PNG + BT.709) that
    avoids the ~0.05 SSIM hit from yuvj420p vs yuv420p

Orchestrator (assets/test-corpus/run.sh):
  - Iterates tier-1-* through tier-4-* directories
  - T1-T3: setup -> lint -> npm install (lazy) -> render Remotion ->
           render HF -> SSIM diff at the fixture's expected threshold ->
           generate strip on failure
  - T4: validate.sh (lint-only)
  - Emits run-report.json with per-tier pass/fail and aggregate counts
  - Accepts a single-tier argument for fast iteration: ./run.sh tier-1-title-card

Validated end-to-end on a clean checkout:
    ▶ tier-1-title-card → mean SSIM 0.9739 (≥ 0.95) ✓
    ▶ tier-2-multi-scene → mean SSIM 0.985292 (≥ 0.95) ✓
    ▶ tier-3-data-driven → mean SSIM 0.952941 (≥ 0.9) ✓
    ▶ tier-4-escape-hatch → 8/8 cases ✓
    passed 4/4, failed 0, skipped 0

Closes the 7-PR stack: scaffold, eval harness, 4 tiers of corpus,
references, and now the SKILL.md body that ties everything together.
2026-04-28 00:00:10 +00:00
James 7509916227 feat(skills): remotion-to-hyperframes corpus T4 (5/7)
Adds the escape-hatch tier — lint-only fixtures that test the skill's
ability to refuse translation cleanly when it sees patterns that don't map
to HF's seek-driven model.

Cases (8 total):
  01-use-state.tsx          blocker: r2hf/use-state
  02-use-effect-deps.tsx    blocker: r2hf/use-effect-deps (multi-line body
                            with internal commas — regression target for
                            the regex bug fix in PR 2)
  03-async-metadata.tsx     blocker: r2hf/async-metadata
  04-third-party-react.tsx  blocker: r2hf/third-party-react-ui (@mui/material)
  05-lambda-config.tsx      blocker: r2hf/lambda-import
  06-warnings-only.tsx      warnings: delayRender / useCallback / useMemo
                            (no blockers — translates after dropping wrappers)
  07-custom-hook.tsx        warning: r2hf/custom-hook (pure useFadeIn)
  08-mixed.tsx              multiple blockers + warnings (aggregate test)

Each case documents:
  - The Remotion pattern it demonstrates
  - Why it's a blocker / warning / info
  - What the skill should do (refuse / drop-and-translate / translate-as-is)

Validation harness (validate.sh):
  Runs lint_source.py against each case, asserts:
    - Each expected blocker rule fires with severity="blocker"
    - Each expected warning rule fires with severity="warning"
    - lint_source.py exit code is 1 when blockers expected, 0 otherwise

T4 has no renders to diff. The skill is graded on lint correctness — that's
the gate that decides whether to translate or recommend the runtime interop
pattern from PR #214.

Result: 8/8 cases pass.
2026-04-27 23:55:24 +00:00
James efa7164ab4 feat(skills): remotion-to-hyperframes corpus T3 (4/7)
Adds the data-driven tier — a purpose-built fixture (option 2 from the
stack discussion, not a port of PR #214's examples/remotion-full/) that
exercises the realistic shape of a production Remotion composition
without using the runtime adapter.

Stargazed.tsx (10s @ 30fps, 1280x720):
  Sequence 0-3s    TitleScene   (title + subtitle)
  Sequence 3-7s    StatsScene   (3 reused StatCards staggered 12 frames apart)
  Sequence 7-10s   OutroScene   (UnderlinedText with scaleX-from-left underline)

Composition shape exercises:
  - <Composition schema={z.object({...})} defaultProps={...} />
  - nested array prop (stats[]) materialized as repeated HTML
  - custom React subcomponents (StatCard, AnimatedNumber, UnderlinedText)
    reused with different props
  - per-instance delay via prop (delayInFrames -> GSAP timeline offset)
  - frame-driven count-up (AnimatedNumber, manual cubic ease-out)
  - two different spring configs in the same composition
    (damping:12 -> back.out(1.4), damping:14 -> back.out(1.2))
  - useCurrentFrame, useVideoConfig

Translation choices documented in README.md and expected.json:
  - Zod props -> data-* on root #stage div
  - Custom subcomponents inline as repeated HTML using prop interface
    as the template
  - AnimatedNumber's frame-driven count-up -> GSAP onUpdate tween on a
    { v: 0 } counter object, ease power3.out
  - Two different spring configs -> two different back.out overshoots
    (1.4 vs 1.2 approximates the damping difference)
  - delayInFrames={i * 12} -> GSAP offset (i * 0.4)s

Validated end-to-end: rendered Remotion baseline + HF translation, ran
scripts/render_diff.sh.
  measured mean SSIM 0.953
  measured min  SSIM 0.927
  measured p05  SSIM 0.938
  threshold 0.90 (~0.04 below p05)

The wider gap vs T1/T2 reflects T3's bigger approximation budget
(2 spring instances + count-up timing + font fallback on multiple text
sizes). Mean SSIM below 0.90 = structural mismatch (wrong durations,
wrong stagger, missing prop wiring), not approximation drift.

Same Remotion config as PR 3: setVideoImageFormat("png") +
setColorSpace("bt709") to match HF's yuv420p output.

Lint: 9 files scanned, 0 blockers / 0 warnings / 0 infos.
oxlint, oxfmt, typecheck all pass.

The fixture is not yet wired into CI; render + diff is documented in
README.md and runs by hand via the harness from PR 2. PR 7's orchestrator
will wire all four tiers into a CI eval run.
2026-04-27 23:54:43 +00:00
James 9ff46d79b7 feat(skills): remotion-to-hyperframes corpus T1+T2 (3/7)
Adds the first two test fixtures the skill is graded against. Each fixture
ships:
  - remotion-src/  full Remotion project (package.json, src/, remotion.config.ts, tsconfig.json)
  - hf-src/        hand-translated HyperFrames composition (index.html)
  - expected.json  tier metadata + SSIM threshold + translation notes + measured validation
  - README.md      human walk-through of the translation choices
  - setup.sh       (T2 only) generates binary assets (PNG, WAV) via ffmpeg

T1 — title-card-fade
- 3 s @ 30 fps, 1280x720
- Single AbsoluteFill, single useCurrentFrame interpolate
  with multi-segment input [0,15,75,90] -> [0,1,1,0]
- Validated mean SSIM 0.974, threshold 0.95
  (~0.025 gap from font-fallback divergence between Remotion's bundled
   Chromium and HF's chrome-headless-shell)

T2 — title-image-outro
- 6 s @ 30 fps, 1280x720, three Sequences (TitleScene, ImageScene, OutroScene)
- Exercises spring, interpolate, Audio, Img, staticFile
- Spring -> GSAP back.out(1.4) translation
- Validated mean SSIM 0.985, threshold 0.95
  (translation came out cleaner than predicted; spring->back.out drift was
   smaller than the ~0.05 budget I'd expected)
- setup.sh generates a 200x200 blue PNG and a 6 s silent WAV via ffmpeg
  so binaries stay out of the repo

Calibration done end-to-end: rendered Remotion baseline + HF translation,
ran scripts/render_diff.sh, set thresholds ~0.02 below measured p05.

Critical Remotion config: setVideoImageFormat("png") + setColorSpace("bt709").
The default JPEG output writes yuvj420p (full-range) which costs ~0.05 SSIM
vs HF's yuv420p (limited-range). Both fixtures' remotion.config.ts encode
this so render_diff.sh measures translation fidelity, not encoder differences.

Both fixtures lint clean (0 blockers via scripts/lint_source.py).
T2 staticFile() references correctly flagged as info-level findings.

The fixtures are not yet wired into CI — that comes with PR 7's orchestrator.
For now, render and eval are documented in each README and run by hand.
2026-04-27 23:54:28 +00:00
James 7ed3d0485a feat(skills): scaffold remotion-to-hyperframes skill (1/7)
Adds the directory + SKILL.md frontmatter for a new skill that translates
Remotion (React) compositions to HyperFrames (HTML+GSAP). This is the
foundation PR; subsequent PRs in the stack add the eval harness, test
corpus, translation references, and finally the SKILL.md body.

The frontmatter description enumerates trigger phrases and explicit
out-of-scope cases (useState/useEffect, async metadata, @remotion/lambda)
so the skill bows out cleanly when a Remotion composition isn't a clean
translation target — those should use the runtime interop pattern from
PR #214 instead.

Validated with skill-creator's package_skill.py.
2026-04-27 05:16:02 +00:00