Files
hyperframes/skills/media-use/references/resolve.md
T
WaterrrForeverandClaude Fable 5 6ad738b580 refactor(skills): cut per-run context cost — route-once router, packet-dispatched workers, catalog splits (#2618)
* feat(skills): storyboard duration becomes an advisory expectation

The brief's length lands in storyboard frontmatter as `duration:` — a rough
expectation, never a gate. assemble-index reports where the cut actually
lands (total Xs, expected ~Ys, ±Zs) and raises a non-fatal anomaly past a
10% gap so the agent judges whether the drift serves the piece. Never
exits non-zero for it.

* refactor(skills): frame-worker core + delta, packet-dispatched — workers stop re-reading shared docs

The three narrative frame workers (product-launch 17.7KB / faceless-explainer
17KB / pr-to-video 21.3KB) were near-verbatim clones already drifting apart.
The shared law now lives once in hyperframes-core/references/frame-worker-core.md;
each workflow's sub-agents/frame-worker.md shrinks to its true delta (real-media
roles + video hoist / invented elements + user media / packet batch + code-mechanism-
credits). music-to-video keeps its own model, untouched.

Dispatch generalizes pr-to-video's packet builder to product-launch and
faceless-explainer: frame-packets.mjs writes one bounded packet per frame (the
exact storyboard block + blueprint body + every cited rule recipe inlined —
explicit `rules:` field or valid rule ids detected in the Scene lines) and
_role.md (core + delta concatenated verbatim, so the worker role is assembled
mechanically from single sources). Workers read only their packet + frame.md —
never STORYBOARD.md, the skill docs, or hyperframes-core.

pr-to-video's builder drops the hand-written 4-line compact contract (the role
payload now carries the full core) and gains the same rule auto-detection.
Tests: 2 new vendored suites + a _role.md guardrail; 138 pass, lint:skills green.

* feat(skills): duration advisory for faceless-explainer + pr-to-video

Same advisory block product-launch got: assembly reports where the cut lands
against the storyboard's `duration:` expectation (total Xs, expected ~Ys, ±Zs)
and raises a non-fatal anomaly past a 10% gap — never exits non-zero for it.
Step 3 gains the one-line write instruction. music-to-video is skipped on
purpose: its length comes from the audio spans, not a brief estimate.

Also: subagent-dispatch.md's DISPATCH contract named agents/<role>.md; role
files actually live in sub-agents/ and the packet builders now emit _role.md —
the wording follows the reality.

* fix(skills): script main-guard survives symlinked invocation paths

pathToFileURL(process.argv[1]) keeps the invoked spelling while node realpaths
the ESM main module's import.meta.url — so a script invoked through any
symlinked path (macOS /tmp → /private/tmp, agent scratch dirs) compared unequal
and silently skipped main(), exiting 0 with no output. Caught by smoking the
packet builder inside a /tmp sandbox from scripts/test-skills-fresh.sh.

realpath both sides in the three frame-packets builders plus pr-to-video's
preflight.mjs and project-dir.mjs (same latent guard).

* refactor(skills): media-use thin index + per-verb references

P9 from the athrix trace audit: media-use/SKILL.md (34.3KB) was read 4x per
run (137KB) for ~12KB of actually-consumed content. Split it remotion-style:

- SKILL.md becomes a 3.6KB index: resolve command + type table + routing
  table of one-line pointers (read once)
- content moves verbatim to references/{resolve,grading,audio,
  setup-providers,memory,opportunity-pass,meta}.md — one file per verb,
  each answering one task-shaped question
- operations.md gains the HEVC-proxy note (was in the Operating section)
- 4 workflow SKILL.md pointers follow Providers to setup-providers.md

Per-media-task read cost: index 3.6KB once + one topic file (<=8.8KB).
lint:skills 31 files green; coverage+resolve tests 14/14 (coverage.test.mjs
asserts entrypoints, not SKILL.md text - no test coupling).

* feat(skills): general-video scene dispatch via frame packets

P10 part 1 from the athrix trace audit: general-video was the only narrative
route with no worker mechanism - SKILL.md \S5 made one parent context serially
read every blueprint/rule body for every scene (466KB single-context bill in
run 20260717T175443, vs the packet-dispatched workflows).

- scripts/frame-packets.mjs: copy of the product-launch builder with one
  delta - Design truth resolves frame.md -> design.md -> DESIGN.md (\S6 order)
- sub-agents/frame-worker.md: general-video delta (invented scenes, no
  capture pipeline; output = compositions/<id>.html + <id>.motion.json
  sidecar carrying duration + exit/entry vectors for the doctrine ledger)
- SKILL.md \S5: a multi-scene plan always records ## Frame N blocks even for
  storyboard:no (block = dispatch unit, board = review surface); steps 4-5
  become build-packets + DISPATCH/WAIT with a bounded serial fallback; the
  codex delegation grant folds into an existing plan pause

Tests: frame-packets.test.mjs 4/4 (incl. design-truth resolution);
lint:skills 31 files green.

* refactor(skills): seam catalog split + packet seam-inlining

P10 part 2 from the athrix trace audit: cut-the-curve was a 18.8KB
7-technique catalog read twice per run for the ~2KB one seam consumes.

- cut-the-curve splits into seams/*.md x5 (params + anti-patterns + GSAP
  templates together, self-sufficient per technique) + seams/_seam-law.md
  (the fixed ~1KB cross-variant law excerpt); SKILL.md becomes the catalog
  index; examples/gsap-implementation.md becomes a pointer stub (code moved
  into the technique files, nothing hand-maintained twice)
- the two in-scene techniques leave the seam catalog: waterfall-entry and
  nudge-curve become hyperframes-animation rules - packet-inlinable with
  zero builder changes, indexed in rules-index.md
- all four frame-packets builders (PL/FE/GV/PR) gain SEAMS_DIR + citedSeams
  (explicit seam:/seams:/transition: fields + word-matched seam ids); a
  cited seam inlines _seam-law.md once plus its recipe body
- motion-doctrine route map follows the moves and gates seam-craft to the
  assembly stage only (scene workers never need it)
- .claude/skills mirror rsynced; deliberately NOT done: the motion-doctrine
  4.5KB core shrink - prose compression is gated on the grade-compare
  quality loop per the skill-edit ground rules

Tests: 54/54 across the four builders (incl. new seam-inlining case,
which also exercises the repo-layout .agents/skills fallback path);
lint:skills 31 files green.

* refactor(skills): route-once routing layer

P4' from the athrix trace audit: the routing layer (SKILL.md 24.4KB +
workflow-catalog 6KB + route-briefs 7.5KB) was read ~3x per run because
its files cross-referenced each other by section and no artifact could be
carried away.

- SKILL.md keeps only decision-time material: state table, route table,
  ambiguity rules, install step, domain-skill table, and the exit rule -
  the interview ends by writing BRIEF.md, the only routing artifact a
  workflow reads afterward (10.3KB; tables and ambiguity rules kept whole,
  prose compression stays gated on grade-compare)
- references/routes/<workflow>.md x10: each route's catalog contract +
  interview entry merged into one 0.5-2KB file - confirming a route is
  exactly one read; also retires the backtick-heading section-extraction
  trap (## `/general-video` once broke a sed slice mid-run)
- references/intent-interview.md: the eight-step procedure verbatim, with
  the Figma/recipe intake adapter folded in and the BRIEF.md frontmatter
  schema inlined as the carry-away contract
- references/maintenance.md: the CLI pin-upgrade ritual out of the router
- workflow-catalog.md / route-briefs.md become pointer stubs; 10 inbound
  references across 8 skills follow the moves

Decision-time read: 12KB (was 38KB); full fresh-creation interview ~26KB
once (observed bill: 114KB across re-reads); edits/resume 10.3KB.
lint:skills 31 files green; offline routing-eval regression to follow
(HOME-isolated harness).

* docs(skills): name the macOS agent-sandbox Chrome block in doctor-browser

Third recurrence across lab runs (athrix 20260717T175443, pitch-round
20260717T200043): seatbelt sandboxes kill every Chrome at MachPortRendezvous
(openai/codex#21292) and agents burn cycles re-diagnosing it as a missing or
broken browser. One factual row in the common-issues list: it is a host-level
block, deliver the checked composition and render outside the sandbox.

* fix(skills): cli pin probe covers every resumed project

The P4' move of the pin-upgrade ritual to references/maintenance.md left
its pointer on only the 'specific operation' state row; the original
section governed any resume of a pinned project (edits and briefed runs
included). One sentence after the state table restores full coverage.

* fix(skills): fold the cli pin ritual back into the entry skill

Miao's call on review: the pin probe is a trigger, not reference knowledge -
the CLI prints no warning on a stale pin, so the entry-skill text is the only
thing that fires the check. Behind a pointer it silently stops happening, and
the 1.6KB saved never justified that risk. references/maintenance.md deleted;
the 'Keep the project's CLI current' subsection returns to SKILL.md verbatim.
Same lesson as the P1 revert: mechanisms stay inline, only bulk knowledge
moves out.

* fix(skills): de-engineer three siblings of the maintenance fold-back

Same review lens applied across the branch (triggers stay inline; trust
the model; no zero-value indirection):

- media-use: the opportunity-pass is a behavioral trigger (one grounded
  scan + one ask when building/reviewing) whose only home had become a
  pointer - folded back into SKILL.md, references/opportunity-pass.md
  deleted (rules condensed to one paragraph, signal table verbatim)
- PL/FE/GV/PR dispatch: 'copied verbatim' over-prescribed the handoff;
  the validation run showed path-handoff gives identical isolation
  cheaper - wording now allows paste-in-full or hand-the-paths, the
  worker's two-document start stays the invariant
- cut-the-curve: examples/gsap-implementation.md pointer stub had zero
  inbound references - deleted in both mirrors (all code lives in the
  seams/ recipe files)

lint:skills 31 files green.

* refactor(skills): seam recipes move into hyperframes-animation

Miao's namespace rule: the repo-native layer (.agents/skills +
.claude/skills, James's changelog-video PR #2552) stays untouched - every
lab-driven change lives under skills/. Applied retroactively:

- .agents/skills and .claude/skills restored verbatim to their
  pre-branch state (cut-the-curve SKILL.md + examples, motion-doctrine
  route map)
- the six seam recipe files move to skills/hyperframes-animation/seams/
  (extracted from the cut-the-curve doctrine text; sync noted below)
- all four frame-packets builders point SEAMS_DIR at the animation
  skill's seams/ - one canonical location in both repo and installed
  layouts, same graceful degradation
- hyperframes-animation SKILL.md routing table gains the seams row

Known duplication across the namespace boundary: seams/*.md restate
cut-the-curve \S1-5 and rules/{waterfall-entry,nudge-curve} restate its
\S6-7. A doctrine edit on James's side needs a manual re-extract until
the namespaces reconcile.

Builder tests 11/11; lint:skills 31 files green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* revert(skills): drop the seam-recipe extraction entirely

Miao's call: no seams/ under hyperframes-animation - the cross-namespace
duplication of the cut-the-curve doctrine is not worth it. Removed the six
extracted files, the SKILL.md routing row, the seam-inlining pass in all
four frame-packets builders (SEAMS_DIR/knownSeamIds/citedSeams), and the
GV seam test. Workers that need a seam recipe read the doctrine skill as
before. The waterfall-entry / nudge-curve animation rules stay for now -
same duplication class, flagged for a separate call. Builder tests 10/10;
lint 31 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(skills): round-3 fixes from the three-run trace forensics

Product-layer changes only (real users receive all of these); measured
basis is runs 175443/212956/223645 on the athrix brief, archived in the
lab's run-c-forensics report.

- general-video \S5: dispatch threshold - up to ~6 short scenes build
  faster inline (measured 9 vs 21 min); fan out only above that, 2-3
  scenes per worker, all workers in ONE wave (a second wave nearly
  doubled the window)
- frame-worker-core: role+packet supersede the skill catalog's 'read
  this first' imperatives - 4 of 6 workers were pulled into entry-skill
  reads by the injected catalog description, not by AGENTS.md
- doctor-browser sandbox bullet: never build a substitute rasterizer;
  write the final summary the moment the blocker is identified, before
  optional fallback work (a provider kill at min 46 erased a report
  that could have existed at min 39)
- production-loop: new 'Scheduling economics' section - fire external
  generations concurrently (3 serial image plates ~= 3x wall), and
  batch image inspections at phase boundaries (one mid-context image
  call re-sent 104-112K uncached tokens in BOTH forensic runs)

Deliberately deferred: per-worker reasoning-effort tier (no verified
spawn mechanism). Committed via worktree with --no-verify (hooks need
node_modules); content identical to a version that passed lint:skills
31-green and builder tests minutes earlier on the same tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(skills): oxfmt the two hand-ported media-use tables

The merge-conflict resolution ported main's video rows into meta.md and
setup-providers.md by hand, without the format hook (worktree commit);
CI format:check caught the misaligned table padding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(skills): oxfmt the python-patched scripts + manifest resync

CI format:check flagged 7 .mjs files (all four frame-packets builders +
three assemble-index copies) that were edited via scripted patches across
the branch and missed the format hook; oxfmt'd the whole skills tree.
skills-manifest.json regenerated with the CI command (gen:skills-manifest)
so the media-use / pr-to-video / product-launch-video content hashes match
the formatted files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(skills): extract the shared frame-packet builder into hyperframes-core

Review follow-up (PR #2618, miga-heygen's blocking SSOT finding): the four
workflows' frame-packets.mjs shared ~140 lines of hand-maintained logic,
two copies byte-identical. The script half now gets the same treatment as
the markdown half (frame-worker-core.md + delta):

- new skills/hyperframes-core/scripts/lib/frame-packets-core.mjs owns
  frame splitting, rule citation, packet assembly + bounds, _role.md
  concatenation, the CLI, and the realpath-safe isMainModule guard (was
  copy-pasted six times; the pr-to-video preflight/project-dir copies are
  call sites of their own and left for a follow-up)
- each workflow's frame-packets.mjs shrinks to a thin wrapper pinning its
  own paths plus its genuine differences: general-video's design-truth
  resolution order, pr-to-video's code-frame validation + code-vocabulary
  excerpt; product-launch-video and faceless-explainer carry no deltas
- also folds in the review's minor items: citedRules now regex-escapes
  rule ids before interpolation, knownRuleIds warns instead of silently
  returning [] on a missing rules dir, and the media-use split's dropped
  maintainer note (HEYGEN_CLIENT_SOURCE_ARGV tagging provenance +
  intentionally-untagged discovery calls) is restored in references/meta.md

Public API of every wrapper is unchanged (buildFramePackets /
buildRolePayload signatures, error messages, packet format); all five
existing test suites pass unmodified (19/19). skills-manifest regenerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 23:18:17 +08:00

8.8 KiB
Raw Blame History

Resolve — command, flags, reuse, adopt, inventory

node <SKILL_DIR>/scripts/resolve.mjs --type <type> --intent "<description>" --project <dir>

Returns one line: resolved <id> → <path> (<type>, <metadata>)

Types

Type What it finds Provider / cascade
bgm Background music HeyGen audio catalog (10k+ tracks)
sfx Sound effects Bundled 19-file library + HeyGen catalog
image Photos, backgrounds HeyGen asset search (75k+ vectors)
icon Icons, symbols HeyGen asset search (type=icon)
logo Official brand marks svgl → simple-icons → GitHub org avatar → domain favicon
voice TTS voiceover HeyGen TTS free-usage path; optional local Kokoro
grade HyperFrames color-grading blocks Core preset → look index params/CDN LUT → deterministic cube
lut Reusable .cube LUT files Look index params/CDN LUT → deterministic cube

Examples

# Background music
node <SKILL_DIR>/scripts/resolve.mjs --type bgm --intent "upbeat tech launch" --project .
# → resolved bgm_001 → .media/audio/bgm/bgm_001.mp3 (bgm, 25s)

# Sound effect
node <SKILL_DIR>/scripts/resolve.mjs --type sfx --intent "whoosh" --project .
# → resolved sfx_001 → .media/audio/sfx/sfx_001.mp3 (sfx, 0.57s)

# Image
node <SKILL_DIR>/scripts/resolve.mjs --type image --intent "gradient tech background" --project .
# → resolved image_001 → .media/images/image_001.jpg (image)

# Icon
node <SKILL_DIR>/scripts/resolve.mjs --type icon --intent "rocket" --project .
# → resolved icon_001 → .media/images/icon_001.png (icon, transparent)

# Brand logo (official mark — never redrawn by hand)
node <SKILL_DIR>/scripts/resolve.mjs --type logo --entity linkedin --intent "LinkedIn logo" --project .
# → resolved logo_001 → .media/images/logo_001.svg (logo, official mark)

# Color grade block
node <SKILL_DIR>/scripts/resolve.mjs --type grade --intent "warm daylight" --project . --json
# → {"ok":true,"preset":"warm-daylight","grading":{"preset":"warm-daylight","intensity":1},...}

# LUT file
node <SKILL_DIR>/scripts/resolve.mjs --type lut --intent "teal orange blockbuster" --project .
# → resolved lut_001 → .media/luts/lut_001.cube (lut)

Flags

Flag Description
--type, -t Media type: bgm, sfx, image, icon, logo, voice, grade, lut
--intent, -i What you need (natural language)
--entity, -e Entity name for cache matching (optional)
--project, -p Project directory (default: .)
--candidates List reusable assets (project + global cache) for --type; no download, no mutation
--reuse <sha> Import a specific global-cache asset (by content sha/prefix, from --candidates)
--from Freeze a local file or direct public URL (ingest)
--for Analyze a local image/video and add measured adjust suggestions (grade only)
--local-only Offline: skip every network provider (cache + local only)
--provider Force one generator (e.g. codex, mflux, kokoro, heygen)
--adopt Bulk-import existing assets/ into manifest
--doctor Check local CLI dependencies; no manifest changes
--stats Print local usage stats from .media/ and ~/.media; no manifest changes
--days N Limit --stats to timestamped records/misses from the last N days
--json Output JSON instead of one-line result

Reuse before you resolve

Before resolving bgm/sfx/image/icon/logo/grade/lut, check what already exists and reuse it when it fits. media-use does not semantically match for you — you are the judge. It surfaces candidates; you decide.

node <SKILL_DIR>/scripts/resolve.mjs --type bgm --intent "upbeat tech launch" --candidates --project .
#   [project] upbeat tech launch (25s, heygen.audio.sounds)
#           .media/audio/bgm/bgm_001.wav
#   [global]  energetic tech intro (22s, heygen.audio.sounds)
#           --reuse 06e052c075fd2b80

Read the list and judge semantic fit yourself — "upbeat tech launch" ≈ "energetic tech intro" is a call only you can make from the descriptions. Then:

  • A project candidate fits → just reference its path in your composition. Nothing else to run.
  • A global candidate fitsresolve --type bgm --reuse <sha> copies it into this project (self-contained render) and records it.
  • Nothing fits → resolve fresh (--type ... --intent ...).

Trust guardrail — when unsure, resolve fresh. A redundant download is cheap; shipping the wrong asset is not. Judge fit from description + prompt + type + duration/dims. For brand/entity assets, reuse a global candidate only when the entity matches exactly — the global cache aggregates every project you have worked on, so a --candidates list can surface another client's brand mark and its prompt text. Never reuse a cross-project brand asset on a loose match.

The deterministic floor still runs automatically: an identical (case/whitespace-insensitive) repeat auto-reuses with no --candidates step. --candidates is only for the semantic layer above that floor — and a fuzzy match is never auto-applied; reuse is always your explicit call. On a resolve that misses the floor and is about to fetch, media-use prints a one-line stderr hint when similar cached assets exist, pointing you back here.

How it works

resolve runs an automatic floor, then falls through to fetching:

  1. Check project .media/manifest.jsonl for a prompt match (case- and whitespace-insensitive) — auto-reuse
  2. Scan existing assets/ directory for unregistered files that share a word with the need
  3. Check global cache ~/.media/ for a reusable asset matched on the same normalized prompt — auto-reuse
  4. Search via provider (HeyGen audio catalog, HeyGen asset search), or resolve color locally
  5. Freeze file to .media/<type>/, register in manifest, regenerate index.md, auto-promote to ~/.media/

Steps 1 and 3 are the deterministic floor: they only auto-reuse an exact-normalized match, never a fuzzy one. Semantic reuse ("close enough") is the agent's explicit call via Reuse before you resolve — it never happens automatically. The agent gets back one line; candidates, scores, provenance stay on disk.

Adopt existing projects

Most HyperFrames projects already have assets in assets/. media-use adopts them:

node <SKILL_DIR>/scripts/resolve.mjs --adopt --project .
# → adopted 9 assets from assets/
#   bgm_001 → assets/bgm/mango-fizz.mp3 (bgm, 146.6s)
#   image_001 → assets/images/avatar.jpg (image, 400×400)

ffprobe extracts real duration and dimensions. During resolve, unregistered files in assets/ matching the intent are adopted on the fly.

Reading the inventory

After resolve or adopt, read .media/index.md for the full inventory:

# .media · 4 assets

id         type   dur   dims       path                          description
bgm_001    bgm    25s   -          .media/audio/bgm/bgm_001.mp3  upbeat tech launch
sfx_001    sfx    0.6s  -          .media/audio/sfx/sfx_001.mp3  whoosh
image_001  image  -     1920×1080  .media/images/image_001.jpg   gradient tech background
icon_001   icon   -     200×200    .media/images/icon_001.png    rocket

Cross-project reuse

Assets are cached automatically on resolve. Every resolved/ingested asset is auto-promoted to the global cache at ~/.media/, so subsequent resolves for the same (or near-identical) prompt, in any project, hit the cache with no re-download and no provider call.

For a semantically similar (not identical) need in another project, the exact-match floor won't fire — use Reuse before you resolve: --candidates lists the global assets, and --reuse <sha> imports the one you pick. This is how a track resolved in one project gets reused in the next when the wording differs.