Lambda and Cloud Run renders refused 25 fps although it is an integer
rate like 24, 30 and 60, and PAL footage is 25 fps. The distributed fps
set is now {24, 25, 30, 60} in the producer's config validation, the
plan v2 manifest, the render request mapping, the Lambda and Cloud Run
event types, the CLI's lambda and cloudrun commands and the regression
harnesses.
Co-authored-by: Cursor <cursoragent@cursor.com>
Make /hyperframes the single entry skill and bring the docs back in sync with
the #1349 skills refactor.
Skills:
- Rename hyperframes-read-first -> hyperframes so the leaderboard-tracked
/hyperframes is the entry/router skill; description leads with "READ THIS
FIRST" to preserve the read-first intent. Update all references across
CLAUDE.md, AGENTS.md, CLI templates, test script, and workflow SKILLs.
Docs (closes the quickstart confusion in #1428):
- quickstart + prompting: replace the dead standalone runtime slash commands
(/gsap /lottie /three /waapi /animejs /css-animations /tailwind) with the
real surface; document the picker as required core skills (8) vs optional
workflows, with --all as the install-everything shortcut.
- frame-adapters: map every runtime to /hyperframes-animation.
- packages/cli: /tailwind -> /hyperframes-core; rewrite the skills-include
blurb around the current domain skills.
- copilot-cli/pipeline/migrating-to-lambda: /hyperframes is the router; the
composition contract lives in /hyperframes-core. Fix a dead /gsap example.
- antigravity: stop listing gsap/ and tailwind/ as separate skill dirs.
- contributing/catalog: /contribute-catalog -> /hyperframes-registry.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(lambda): document webm support in distributed mode
PR 8.4 of the WebM distributed-rendering plan (v1.5 backlog #1; see
DISTRIBUTED-RENDERING-PLAN.md §7.2). User-facing docs catch up with the
shipped capability.
Updates docs/deploy/migrating-to-hyperframes-lambda.mdx:
- "Output format" row in the migration table now lists `webm` alongside
mp4 / mov / png-sequence with a note that webm uses libvpx-vp9 +
closed-GOP concat-copy. HDR mp4 remains the only refused format.
- "No webm distributed" caveat replaced with "webm uses closed-GOP VP9"
explainer covering the encoder args (`-g <chunkSize>`,
`-keyint_min <chunkSize>`, `-auto-alt-ref 0`, `-cpu-used 2`), why
alt-ref disable is load-bearing, and that the output preserves alpha
via yuva420p with Opus audio.
- Migration checklist no longer asks adopters to filter out webm
compositions; only HDR-dependent renders need to stay on the previous
framework.
aws-lambda.mdx doesn't currently call out webm as unsupported (only HDR
in the v1 surface list), so it gets no copy edits beyond the migration
guide.
The internal planning doc (DISTRIBUTED-RENDERING-PLAN.md §7.2, §8,
§12 — kept outside the repo) gets matching updates: format support
matrix flipped ✓, v1.5 backlog #1 marked shipped, HDR promoted to the
new top item, and the rev-12 → rev-13 status line.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: address simplify-review findings on webm stack
Folds in cleanups identified by a multi-agent code-review pass over the
4-PR webm-distributed stack:
- plan.ts: `resolveEncoderTriple()` webm case now calls
`getEncoderPreset(quality, "webm")` for its preset string instead of
hardcoding "good". The hardcode was wrong for `quality: "draft"`
(`getEncoderPreset` returns "realtime" for that tier) — would have
silently overridden the draft → realtime mapping for distributed webm
renders.
- chunkEncoder.ts: trim the new VP9 closed-GOP comment block from ~18
lines of WHY narration down to the 6 lines that actually explain why
(alt-ref + cpu-used drift). Match the alpha branch's idempotent-push
comment to the same standard.
- chunkEncoder.test.ts: drop the duplicate WHY comment that restated
the implementation comment in plain words.
- webm-concat-copy.test.ts: rewrite the file-header docstring to
describe the contract being tested instead of the PR-8.1-gating
history; strip "PR 8.2 / Path A / Path B" references from error
messages (they belong in PR bodies, not in test output). Consolidate
the yuva420p alpha smoke into a single `it()` block (was a full
4-test describe with duplicated setup) — the yuv420p block already
covers the probe/decode/frame-count contract; the alpha smoke only
needs to prove the alpha args don't break concat-copy.
- plan.test.ts: drop the "PR 8.1 proved the contract" comment.
- webm-vp9 fixture: drop the aspirational "Other webm-with-audio
fixtures cover the mux path separately when added" sentence (no
other fixtures exist). Regenerated the baseline via
`docker:test:update webm-vp9` to reflect the updated comment.
- migrating-to-hyperframes-lambda.mdx: add a paragraph about
distributed webm's perf cost — ~10-25% larger files at constant CRF
due to forced keyframes, and slower per-chunk encode due to
`-cpu-used 2` being more conservative than the libvpx default.
All unit tests + the webm-vp9 distributed-simulated regression still
pass after these changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(cli): accept --format=webm in `hyperframes lambda render`
The CLI's `lambda render` subcommand's FORMATS allowlist and the
`RenderArgs.format` type still narrowed to `mp4 | mov | png-sequence`,
so even though the producer + aws-lambda packages now support webm
end-to-end, the CLI surface rejected it with `--format must be mp4|mov|
png-sequence`. Add webm to both spots and update the --help description.
Surfaced during real-AWS deploy prep — the local lambda-local /
distributed-simulated tests didn't go through the CLI so the gap went
unnoticed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(producer): font cache writes to /tmp on Lambda (read-only \$HOME)
The deterministic Google Fonts cache was rooted at
`\$HOME/.cache/hyperframes/fonts`, which fails on AWS Lambda — the
runtime's `\$HOME` resolves to a `/home/sbx_*` directory tree that's
read-only. `mkdirSync(..., { recursive: true })` can't create that
path and the plan stage trips with `ENOENT: no such file or directory,
mkdir '/home/sbx_user1051/.cache/hyperframes/fonts/space-mono'` on
every Lambda render that pulls a Google Font (i.e. every distributed
fixture using `@import url("https://fonts.googleapis.com/...")`).
Detect Lambda via `\$AWS_LAMBDA_FUNCTION_NAME` and route the cache to
`tmpdir()/hyperframes/fonts` in that case. Lambda's `/tmp` survives
across invocations on a warm container, so cache hit rate is the same
as non-Lambda runs. Also honor an explicit
`\$HYPERFRAMES_FONT_CACHE_DIR` override for adopters who want a
different location regardless of the runtime.
Surfaced while verifying webm distributed end-to-end on real AWS — the
same bug affects mp4 fixtures using Google Fonts; webm just happened to
be the one I tried first.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: extract DistributedFormat type + trim font-cache resolver
Second simplify-review pass on the webm stack flagged two cleanups:
1. **`DistributedFormat` type duplicated 10 times.** Every file in the
distributed pipeline carried its own copy of
`"mp4" | "mov" | "png-sequence" | "webm"` — adding a new format
meant a 10-place edit with no compile-time guarantee they stayed in
sync. Extract a single source of truth in
`packages/producer/src/services/distributed/shared.ts`, re-export
from `@hyperframes/producer/distributed` and
`@hyperframes/aws-lambda/sdk`, and have all callers pull from
there. The aws-lambda `ALLOWED_FORMATS` runtime tuple and the CLI's
`FORMATS` tuple now both use `satisfies readonly DistributedFormat[]`
so the compiler enforces the runtime allowlist stays in sync with
the type.
2. **`deterministicFonts.ts` font-cache resolver was over-commented.**
Trim the 7-line block to 4 lines (drop the aspirational
"and other read-only-FS execution environments" — only Lambda is
detected — and the warm-container `/tmp` persistence narration —
anyone reading already knows Lambda /tmp semantics). Collapse the
two-step `if (explicit && explicit.length > 0)` into a single
nullish-coalesce expression now that the empty-string defensive
check is gone (`process.env.X` is `string | undefined`, no third
shape to guard against).
Out-of-scope skips (called out by the agents, deferred):
- In-process `RenderConfig.format` and the in-process CLI's
`render.ts` format union still carry their own inline copies. The
union happens to coincide today but they're separate concerns —
leaving them alone limits this PR's blast radius.
- `fontCacheDir(slug)` / `resolveFontCacheRoot()` naming asymmetry
flagged as taste; skipping.
- Pre-existing redundant `existsSync` before `mkdirSync({ recursive:
true })` in `fontCacheDir` — out of scope.
All tests + typecheck still pass. Lambda render still works
end-to-end (no functional changes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(lambda): drop plan-doc reference from migration checklist
PR review feedback: source/docs should not mention the
distributed-rendering planning doc. Tighten the migration checklist
sentence to describe the webm path directly rather than referencing
the doc's version label.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(producer): split resolveEncoderTriple into mp4 + non-mp4 helpers
CI Fallow audit on PR #953 flagged `resolveEncoderTriple` at CRAP 31.6 —
the function interleaved (a) mp4 codec validation + dispatch, (b) the
non-mp4 codec-rejection throw, and (c) per-format dispatch. Splitting
into `resolveMp4EncoderTriple` + `resolveNonMp4EncoderTriple` drops the
top-level function's cyclomatic complexity below the threshold while
preserving every error message and code path. Behavior unchanged.
Also extracts an `EncoderTriple` type alias so the three functions
share the return shape declaratively rather than repeating it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two adopter-facing artifacts that close out Phase 6b's user-facing
surface:
- docs/deploy/migrating-to-hyperframes-lambda.mdx — side-by-side
concept mapping for users coming from another one-command-deploy
video renderer. Covers the verb mapping (deploy/render/progress/
destroy/sites/policies), composition format (plain HTML vs JSX),
render config, and a handful of intentional differences (no HDR
in distributed mode, no webm, gpu-mode=software requirement,
fail-closed font fetch, local stack-state files, narrow-after-
first-deploy IAM pattern). Closes with a migration checklist.
Per repo convention, no competitor framework is named anywhere
in the source — adopters self-identify.
- examples/k8s-jobs/Dockerfile.example + README.md — reference
Dockerfile for adopters who want to run distributed renders
outside AWS Lambda. Bakes Node 22 + chrome-headless-shell +
ffmpeg + the producer source. Deliberately not published to
a registry; adopters build it themselves so Chrome / ffmpeg /
producer versions stay pinned to the checkout they audited.
The README documents the typical K8s Jobs orchestration shape
that points adopters at packages/aws-lambda/src/handler.ts as
the reference adapter.
Migration guide registered under the existing Deploy group in
docs.json. .gitignore extended to negate the new examples/k8s-jobs/
path the same way examples/aws-lambda/ is negated.
No source code changes.