feat(skills,studio,media-use): the intent layer, review loop, and user memory — BRIEF.md, companion mode, recipes; /website-to-video folds into /product-launch-video#2133
Conversation
…d, status-aware preview landing Per-frame comment boxes on the storyboard board batch into .hyperframes/frame-comments.json (a resubmit wins per frame; unconsumed comments on other frames are kept). Submitted-but-unconsumed comments stay visible — a toolbar banner plus a per-tile echo — until the agent consumes the file; the banner also says what to do next (reply anything in the agent chat). The board keeps itself current: GET /projects/:id/signature exposes the watcher-cached project signature, the storyboard payload carries the signature it was derived from, and the view polls at 2s (hidden tabs skipped, re-checked on visibility), refetching in place with no loading flash. Posters bake the signature into their URL so tiles fill in as sketches land and a poster that failed mid-write retries on the next version; the empty state upgrades itself when STORYBOARD.md appears, and its handoff prompt now points the agent at the review loop and uses the parser's real status vocabulary (outline, not planned). preview lands the browser on the storyboard view while the board is the review surface — any frame built, or pure planning (srcs declared, none on disk yet) — and on the timeline once the video is assembled.
…cess hyperframes-core/references/review-loop.md is the single source for the three-pass collaborative review: the plan proposed on a live board (§ 1), wireframe sketches marked built with one layout question (§ 2 — real words on plain blocks, run no CLI; a confirmed board is itself a valid deliverable when the user asked for a storyboard, not a video), the build dressing confirmed layouts (§ 3, worker or inline), and the final look (§ 4). Autonomous runs skip every gate and keep one question before render. The three narrative workflows' Steps 3/4/6 collapse to references plus their sketch stand-ins (captured-asset blocks for product-launch-video, plain code panels for pr-to-video); the confirmed-sketch handoff stays in each frame-worker prompt. general-video plans on a board for multi-scene narrative pieces in collaborative mode — its sketch pass is layout-before-animation with the user watching. The router treats "I want a storyboard" as a process request rather than a route, and closes exploratory intake by recommending a route plus how the run will review. The supporting contracts land next door: the comments channel (silent submit, one reply picks it up, check the file before the words) in brief-contract § 1; the sidecar schema and the built status rung in storyboard-format; the mode question asked first and alone in the three workflows' Step 0.
Two tiers of memory on media-use's existing two-tier storage split. Preferences (lightweight): confirmed brief answers — destination, aspect, language, mode, voice, style preset — recorded to the project's .media/preferences.json (committed, the team inherits it) and promoted to the personal ~/.media/preferences.json once the same value is confirmed in two different projects (a sightings ledger accumulates the cross-project evidence user-side, since project files can't see each other). prefs.mjs get/record; merge reads project-over-user; a changed value restarts its provenance. Recipes (heavyweight): one approved run frozen as a named, versioned bundle — frame.md, the storyboard skeleton (structure kept: durations, transitions, srcs, Video direction; statuses reset to outline; content blanked to per-frame fill-ins naming the beat's role), and the confirmed brief values. Named folders, not content hashes: re-freezing bumps version and archives <name>@v<N>; a freeze is already confirmed, so it promotes to the user tier immediately. recipe.mjs freeze/list/use, plus resolve --type recipe --entity <name> delegating like grade/lut. 16 new node --test cases; the media-use lib suite is 168/168.
brief-contract § 2 gains Remembered defaults: read the merged preferences before Round 2 and let a remembered value become the recommended option with a receipt naming its source project. Memory changes the default, never the question — every ask-marked field still gets asked, and what the request says this time beats what was picked last time. Record only what the user actually confirmed (a defaulted voice nobody chose is not an answer; a "go" that accepts the recommended defaults is). The first record announces itself once; after that the receipts carry the reminder. In autonomous mode a remembered value becomes the decided value, receipt included. The three narrative workflows read the remembered defaults before Round 2, record the confirmed answers at the Step 0 gate, record the chosen preset at the Step 2 gate (pr-to-video excepted — its preset is fixed), and fall back to the remembered voice when the request names none. general-video's discovery reads the same defaults. Recipes wire in at both ends: Step 0 checks for a matching recipe before the mode question — one question, plural-aware, and adopting one fills the brief, skips the design step, and drafts the storyboard from the frozen skeleton while every review gate still runs. The review loop's final look (§ 4) offers the freeze once after approval, and the confirmation teaches the recall phrase — the name is something the system reminds the user of, never something they must remember. The router recognizes a named recipe or "like last time" as a route.
miga-heygen
left a comment
There was a problem hiding this comment.
Review — feat(skills,studio,media-use): the review loop and user memory
Big feature, well-structured. Six interleaved pieces that compose into the three-pass review flow + user memory. I'll take them in the order they build on each other.
1. Project signature polling (studio-server + studio)
resolveProjectSignature extracted from preview.ts into projectSignature.ts and reused by preview, projects, and storyboard routes — good SSOT improvement, was inline before.
New /projects/:id/signature endpoint is a cheap poll target. useProjectSignaturePoll polls every 2s, skips hidden tabs, avoids concurrent requests, and no-ops when the baseline is undefined (initial load not yet done). Well-tested: signature mismatch fires, match stays quiet, hidden tabs skip, re-arms after caller refresh, cleanup on unmount.
The storyboard response now carries signature in both the absent and present branches — tested in storyboard.test.ts. The board uses it as posterVersion on FramePoster (cache-bust on the thumbnail URL via a ?sig= param).
2. Preview landing on storyboard view
studioLandingSearch determines the landing view from frame statuses: any built → board (sketch review in progress), all outline with declared srcs but no files → board (planning), otherwise timeline. Exported and tested. The deep-link URL puts ?view=storyboard before the hash — documented in preview-render.md.
3. Frame comments (studio + shared contract)
Clean separation between pure logic (frameComments.ts — all the data transforms, fully tested) and React state (useFrameComments.ts — the hook that wires drafts, submit, and pending-refresh to the file manager context).
The buildCommentsFile merge logic is right: a resubmit before the agent consumes the previous batch keeps unconsumed entries for frames the new batch doesn't mention, overwrites the ones it does, sorts by frame index. The passForFrames derivation (furthest status on the board determines the pass) is simple and correct.
The comment box per tile (StoryboardFrameTile.tsx) and the batch submit bar (CommentsSubmitBar in StoryboardLoaded.tsx) are clean UI additions. The useEffect on data.signature in StoryboardLoaded re-checks the comments file when the board refreshes — catches the moment the agent deletes it after consuming.
The contract is defined in storyboard-format.md § Frame comments and cross-referenced from brief-contract.md § 1 ("the comments channel"). Good.
4. Preferences — two-tier remembered defaults (media-use)
prefs-store.mjs: atomic writes (tmp + rename), tolerant reads (corrupt = empty), two-tier with a clean promotion rule (PROMOTE_AT = 2 distinct projects). style_preset keyed per workflow via preferenceKeyFor. User-tier sightings ledger keeps cross-project evidence in the only place that can see it (the user file). Best-effort user-tier writes — a read-only home directory never fails a brief.
Test coverage is solid: record + merge, no one-project promotion, two-project promotion, idempotency, changed-value provenance reset, project-overrides-user, per-workflow keys, unknown key rejection, corrupt file handling.
The consumption contract (brief-contract § 2, "Remembered defaults") is clear: a remembered value changes the recommended default with a receipt — never skips a question, and explicit request content always wins. Each workflow's Step 0 references this section.
5. Recipes — frozen video bundles (media-use)
recipe-store.mjs: skeletonizeStoryboard is the interesting piece — parses markdown, keeps structure (durations, transitions, src paths, Video direction block, style frontmatter), blanks content (message, audience, narration, per-frame prose → one <fill in> placeholder per frame naming the beat's role), resets all statuses to outline. Well-tested against a realistic storyboard fixture.
freezeRecipe: merges preferences into recipe.json, copies frame.md, generates the skeleton, appends a manifest record, promotes to user-tier immediately (a freeze is already confirmed). Re-freezing bumps version and archives the old folder as <name>@v<N>. useRecipe: imports from user tier if the project doesn't have it, copies frame.md over, returns brief values + skeleton path. listRecipes: two-tier merged (project wins), skips @v archives.
Also wired into resolve.mjs as --type recipe — consistent with the existing resolve pattern for other asset types.
6. Skill updates (faceless-explainer, pr-to-video, product-launch-video, general-video, hyperframes)
The same changes replicated across all storyboard-planning workflows:
- Recipe check before Round 1
- Round 1 sent alone (explicit "wait before Round 2" wording)
- Remembered defaults before Round 2 questions
- Sketch pass at Step 4 with workflow-specific stand-ins
- Worker context includes confirmed-sketch flag + keep-layout rule in frame-worker.md
- Gates include sketch board confirmation (collaborative)
- Voice fallback to remembered voice before pipeline default
- Review loop reference at plan presentation and final look
This is intentional replication — each workflow SKILL.md is a standalone document loaded independently. The shared process lives in review-loop.md; each skill wires its steps to specific sections. Correct pattern for the skills architecture.
SSOT check
resolveProjectSignatureunified — previously inline inpreview.ts, now shared across three routes. Good.- Frame comments contract: defined once in
storyboard-format.md, implemented inframeComments.ts, cross-referenced frombrief-contract.md. Single source. - Preferences consumption rule: defined once in
brief-contract.md§ 2, referenced by every workflow. Single source. - Review loop process: defined once in
review-loop.md, wired into each workflow at the appropriate steps. Single source. - Skill replication: intentional — standalone docs, not shared code.
CI note
Skills: manifest in sync is failing — the manifest hash/count may need a refresh after the skill file changes.
Verdict
LGTM. Clean architecture, good test coverage, well-separated concerns. The CI manifest check needs a fix before merge. Looking forward to the additional tests Miao mentioned.
— Review by Miga
jrusso1020
left a comment
There was a problem hiding this comment.
HF-specialist review — COMMENT (external author; stamp = James's call)
Audited (in addition to Miga's general coverage): the new shared-contract file (review-loop.md) end-to-end and its integration with brief-contract.md + storyboard-format.md; the Studio-side signature-polling design (useProjectSignaturePoll.ts + server-side projectSignature.ts); the frame-comments file protocol (frameComments.ts); the preferences store's two-tier promotion logic (prefs-store.mjs); and skeletonizeStoryboard's structure-keep/content-blank behavior (recipe-store.mjs).
Trusting: Miga's coverage on the 6-piece composition (project-signature polling, preview landing, frame comments, prefs two-tier, recipes, skill updates). Not duplicating; adds below focus on the HF architectural angle.
Design fit — shared-contract pattern
review-loop.md (+41 lines, new) follows the exact shape established by brief-contract.md and story-spine.md (HF#2058): shared rules file + workflow SKILLs bind to it via markdown pointers + explicit cross-references between siblings. Same "invariants centralized, executable script local" split. Load-bearing cross-refs from review-loop.md §§ 1-4 back into brief-contract.md § 1 (interaction mode / comments channel) and storyboard-format.md (frame statuses, comments file) are all present and land correctly on the referenced targets. Fits cleanly.
brief-contract.md gets two additions — "The comments channel" (§ 1) and "Remembered defaults" (§ 2) — that widen its scope from interaction to interaction + memory. Reasonable expansion; the file already handled ordering + gates + fields, so preferences memory as a field-level attribute belongs here.
Studio storyboard board — signature polling design
useProjectSignaturePoll.ts:29-67: 2s interval, skips when document.hidden, skips when previous request inFlight, visibility change triggers immediate re-check, no baseline (signatureRef.current === undefined) → no compare (safe), any fetch failure → skip tick (fails open). Solid shape. Comparison baseline is the signature the caller's data was loaded with, so refetch naturally re-arms.
Server-side projectSignature.ts computes a stable content-aware hash: text files ≤ 2MB use content, everything else uses mtime + size. Cache keyed by (mtime + size + relative-path) fingerprint avoids re-reading unchanged files — good. Excluded dirs list at projectSignature.ts:18-30 covers the standard cases (.git, node_modules, outputs, renders, .next, .cache, dist, build, coverage, .hyperframes, .vite).
Concern (nit-level, not blocking): STUDIO_SIGNATURE_MANIFEST_PATHS at projectSignature.ts:32-35 is a hand-maintained allowlist of .hyperframes/* files that DO count toward the signature (studio-manual-edits.json, studio-motion.json). The .hyperframes/ dir itself is otherwise excluded. This means a future Studio subsystem writing to .hyperframes/newthing.json will silently NOT trigger poll refreshes until someone remembers to add it here. Two mitigations to consider (either fine, both fine): (a) invert to a denylist that skips .hyperframes/frame-comments.json (outbound feedback, doesn't need to trigger self-refresh) and includes everything else under .hyperframes/; or (b) add a test that pins this list against the actual Studio-server write surface so a mismatch fails CI.
Concern (nit-level): collectProjectSignatureFiles at projectSignature.ts:65-99 recurses without a depth or file-count cap. Excluded-dirs handles the common problem cases, but a non-standard build directory (someone's .next renamed to custom-build) would churn the 2s poll traversing files. The cache short-circuits on unchanged fingerprints so steady state is cheap, but the first-hit or after-a-refactor cost is uncapped. Consider a soft cap or a file-count guard. Not blocking.
Frame comments file — the review's structured feedback channel
frameComments.ts:104-115 buildCommentsFile correctly handles the "resubmit before agent consumed" case: keeps entries for frames the new batch doesn't mention, overwrites the ones it does. Right invariant — a partial resubmit doesn't lose feedback on other frames.
parseCommentsFile:79-97 is lenient — malformed JSON, missing fields, or non-array comments returns null → treated as "no file". Aligned with the storyboard parser's lenient pattern (storyboard-format.md "The parser is lenient"). Good consistency across the read-side contract.
The cross-repo contract comment at frameComments.ts:6-10 explicitly pins the file shape to skills/hyperframes-core/references/storyboard-format.md § Frame comments. Nice discipline — keeps the shape from drifting between the Studio writer and the SKILL.md documented contract. A regression test that reads both the TS interface and the SKILL.md section and asserts field parity would tighten this further; not blocking.
skeletonizeStoryboard — structure/content split
recipe-store.mjs:123-141. What's kept: frame count, headings, duration, transition_in, src, style-ish frontmatter (format, arc). What's blanked: frontmatter content keys (message, audience, mode), per-frame content keys (voiceover / vo / voice_over / narration / scene / description / summary / caption / asset_candidates), every frame's status → outline, per-frame prose → placeholder <fill in: this video's content for the "..." beat — keep the layout role, replace the words.>.
Verified correct: mode correctly drops (interaction mode is per-run content, not reusable structure) per brief-contract.md § 1. arc correctly kept (reusable narrative structure) per story-spine.md. Case-insensitive detection via .toLowerCase() in skeletonFrameLine:96-101 matches how storyboard-format.md defines these keys.
Consistency observation: FRAME_CONTENT_KEYS at recipe-store.mjs:37-49 includes asset_candidates which isn't documented in storyboard-format.md's per-frame table. If that's an emerging convention worth documenting, add it to the storyboard-format table so both sides stay in lockstep. Not blocking — the skeletonizer treating unknown-but-content-shaped keys as content is the safer default.
Concern (nit-level): recipe archive accumulation. freezeRecipe:174-181 renames the old folder to <name>@v<N> before writing the new one, and scanRecipesDir:229-238 filters out @v entries from listings. No garbage collection — long-lived projects that re-freeze the same name repeatedly accumulate @v1, @v2, @v3, ... indefinitely. Not a bug, but worth a follow-up (either a hard cap like keep-last-3 or an explicit recipe.mjs gc command).
Preferences store — two-tier promotion
prefs-store.mjs:130-146 recordUserSighting: accumulates project sightings under sightings[fullKey][value] (per-value), promotes to preferences[fullKey] when seenIn.length >= PROMOTE_AT (2). Atomic writes via writePrefsFile:70-76 (tmp + rename) — good. Best-effort user-tier write in recordPreference:167-173 so a read-only home directory never fails a brief.
Concern (subtle, nit-level): sightings[fullKey][value] is keyed on basename(resolve(projectDir)) at prefs-store.mjs:161. Two distinct repos with the same basename (git clone in two different parents, e.g. ~/work/foo and ~/personal/foo) both writing the same value would deduplicate to ONE sighting via the dedupe([...]) on line 134, delaying promotion. Conversely, two separate projects with different basenames but the same identity (a directory rename) would double-count. The unaffected common case is the one that matters here (most people don't clone the same repo twice), so this is a low-frequency concern. Fix if it comes up: salt with a project-hash slice.
The keepProvenance logic in recordProjectTier:113-124 is nice: a changed value starts provenance over (old confirmations vouched for the old value), so a mid-flight preference change doesn't inherit stale trust. Right shape.
Manifest CI failure
Skills: manifest in sync fails at the "Verify skills-manifest.json matches skills/" step. Confirmed via the failing job log + the diff shape: hyperframes-core shows +1 file (14 → 15 — the new review-loop.md) and multiple hash rows updated for touched skills. This is a regen-only fix — bun scripts/gen-skills-manifest.ts (or whatever the local generator command is) will produce a manifest that matches. Miga's read is correct.
Verdict
COMMENT — the review-loop / preferences / recipes design fits HF's shared-contract pattern cleanly (same shape as brief-contract.md + story-spine.md); the Studio signature polling is thoughtful (visibility-aware, in-flight-guarded, fails-open); skeletonizeStoryboard's structure/content split is coherent with brief-contract.md § 2's memory semantics; frame-comments file protocol matches its SKILL.md documented contract. Concerns found are all nit-level (allowlist maintenance risk on signature manifest paths, no file-count cap in signature traversal, no GC on @v archives, project-basename collision in sightings ledger). Manifest CI failure is regen-only. Stamp = @jrusso1020's call (external author WaterrrForever; requester Miao is allowlisted but not a trusted-stamper, per delegation rule).
Given Miao is explicitly holding on merge for more tests to land, none of these nits need to block the eventual merge — noting them for the next follow-up.
— Rames Jusso
# Conflicts: # skills-manifest.json
…lity menu, BRIEF.md format
…/storyboard preference keys
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
…f intent, not a pipeline branch
…form (route yours, laws hold)
…cope lists leave the workflows
# Conflicts: # .claude-plugin/plugin.json # .codex-plugin/plugin.json # .cursor-plugin/plugin.json # README.md # skills-manifest.json # skills/hyperframes-cli/SKILL.md # skills/product-launch-video/SKILL.md
…e + route eligibility
…es, menu-probe split
…sts pitch the telling
…int/rule citation discipline
…s npx on the branch CLI
…eral-video tables
… records require workflow scope Two holes found by a live companion-run freeze: the agent-supplied --workflow contradicted the run's actual workflow (recipe.json said faceless-explainer, brief-skeleton said general-video), and the style_preset lookup missed because the preference had been recorded under the bare key. - freezeRecipe resolves the workflow from BRIEF.md frontmatter; the flag is a fallback for briefless projects and a contradicting flag is ignored (noted). - recordPreference refuses a bare style_preset — the scoped key is the only writable shape; freeze tolerates legacy bare records via read fallback. - review-loop § 4 / media-use SKILL / brief-format wording follow the machinery.
jrusso1020
left a comment
There was a problem hiding this comment.
Re-review — COMMENT (external author; stamp = James's call)
Delta from my prior review at 451699db260b: 250 new commits, 559 ahead / 7 behind, 143 files touched (+3764/-5121). Not a merge-only re-ping — this is the substantial redesign Miao described on 2026-07-13 (intent layer at /hyperframes, companion/automation modes, pitch round for unformed requests, product-launch absorbs website-to-video as the tour angle). Not duplicating Miga's substance walkthrough — additive on design-fit + status of my prior-round nits.
Strengths on the intent-layer redesign
- State-table-first at
/hyperframes§ 1 — six state rows evaluated top-to-bottom (Remotion port / project operation / edit /BRIEF.mdexists /hyperframes.jsonorSTORYBOARD.mdexists / fresh creation). Fresh creation is the only path that triggers the intent layer. Edits, project ops, briefed projects, and Remotion ports skip cleanly. That's the correct discipline — the intent layer is expensive (5+ mandatory reference reads), and it should only run when there's genuinely no state to resume from.references/workflow-catalog.mdas the canonical route contract before lazy-installed workflow skills are present is a nice pre-install gate. - Two run-shape questions kept orthogonal at § 4.6 —
flow×storyboard, four combos valid, "never merge them into one menu" spelled out explicitly (companion-with-storyboardis a real user choice, not an "outcome-of-a-3-way-branch" artifact). This is exactly the invariant that gets silently violated when a diagram summarizes outcomes as three branches; codifying it in prose before any workflow's Step 0 sees the state is the right shape. BRIEF.mdas durable output (contract athyperframes-core/references/brief-format.md) — replaces ad-hoc in-conversation briefs with a file the workflow reads. Handoff at § 4.8 is right: workflow's Setup writesBRIEF.mdas its first action afterhyperframes init(not before —initrefuses non-empty dirs), then never asks brief questions again. Load-bearing invariant.- Pitch round for unformed requests (
references/pitch-round.md) — "sample five concepts along five genuinely different paths, at least two from the distribution's tail" is a strong sampling discipline. The "concept names its capabilities in the plain language ofcapability-menu.md" idea keeps the pitch grounded in what the system can actually do vs abstract vision. - Recipe-adoption gates — "make the offer earn the yes" with specific "what adopting saves" — the discipline for what a recipe answers vs what still gets asked is now explicit at § 4.1.
Design concerns worth eyeballing
-
Cognitive load on the router. § 4's mandatory-reads table lists 5 references to consult on the routing path (
workflow-catalog.md,route-briefs.md,pitch-round.md,capability-menu.md,brief-contract.md). Plus the domain-skill table at § 6. That's a lot of reference-material load for a single agent turn — the LLM opens 5 files, reads route-specific sections, applies the state-table + route-table + ambiguity-resolution, all before asking any question. Sanity-check that the intent-layer walkthrough completes in a reasonable turn budget on a real fresh request; if not, consider consolidating the mandatory reads into a single "intent-router.md" reference (or making some conditional/deferred). -
Pitch round's five-diverse-concepts discipline is aspirational without a pinning example. "At least two from the distribution's tail" is easy for a reviewer to endorse and hard for an LLM to actually execute — most LLMs settle on 3-4 similar concepts around the modal case. Consider adding one worked example inside
pitch-round.mdshowing what "genuinely different paths" looks like for a specific input (e.g. a launch video for X → 5 concepts spanning: literal-demo / metaphor / customer-testimonial / behind-the-scenes / animation-first). A calibration example is worth several paragraphs of "should sample diversely." -
"Route absorbs website-to-video as the tour angle" (
/product-launch-video). This is a meaningful scope shift. The old routing distinguished "launch/promo → product-launch-video" from "general site tour → website-to-video". The new state at § 3 folds site tours into product-launch-video with the note that "a tour is brief intent, not a pipeline branch." That's cleaner architecturally, but the routing entry at row 8 says "Market or showcase a website, product site, app, or company from a URL or site-specific brief" which is broad. Miga's substance walkthrough will catch whether the product-launch-video SKILL.md now handles both intents without regressions on the pure-tour case.
Status of my prior-round nits
Comparing to the 4 nits I flagged at head 451699db:
- Recipe
@varchive GC (was: "no garbage collection;<name>@v1,@v2,@v3accumulate forever"): essentially addressed.freezeRecipeatrecipe-store.mjs:225-227doesrmSync(archive, {recursive:true, force:true}); renameSync(dir, archive)— the OLD@v<N-1>archive is deleted before renaming the current dir. Effectivelykeep-last-1per recipe name, one archive at a time. Not the full GC I proposed but the invariant is bounded. Fine. STUDIO_SIGNATURE_MANIFEST_PATHShand-maintained allowlist (was: "future Studio subsystems writing new.hyperframes/*files will silently miss the poll"): still open.projectSignature.ts:32still uses the hand-maintained include-list.- Uncapped file collection in
collectProjectSignatureFiles(was: "no depth or count cap"): still open.SIGNATURE_EXCLUDED_DIRSat :18 unchanged; still traverses recursively without a soft cap. - Prefs
basenamecollision (was: "two repos with same basename collapse in the sightings ledger"): still open.prefs-store.mjs:165still usesbasename(resolve(projectDir)).
All 3 remaining are nits I flagged as non-blocking previously; they remain non-blocking here. Just noting the status so a follow-up can pick them up cleanly.
Verdict
COMMENT — intent-layer redesign is well-shaped, the state-table + two-orthogonal-questions + BRIEF.md-as-durable-output patterns are load-bearing invariants worth having in-source. Cognitive-load + pitch-round-diversity concerns are worth pin-testing in real runs but don't block. Prior-round nits mostly still open (1 of 4 resolved). CI green on b831ed8fc238. Stamp = @jrusso1020's call (external author WaterrrForever; requester Miao is allowlisted but not a trusted-stamper, per delegation rule).
— Rames Jusso
miga-heygen
left a comment
There was a problem hiding this comment.
Review — #2133 feat(skills,studio,media-use): intent layer, review loop, user memory
Verdict: LGTM — 143 files, 3554/5123 lines. Reviewed in three parallel passes (skills layer, studio/server, media-use memory). Strong SSOT discipline throughout. No blocking issues.
Skills layer — SSOT verified clean
Every major new concept has a single canonical owner:
| Concept | Canonical Owner |
|---|---|
| BRIEF.md artifact shape | brief-format.md |
| Mode derivation | brief-contract.md §1 |
| Review loop checkpoints | review-loop.md |
| Production loop stages | production-loop.md |
| Intent layer procedure | /hyperframes SKILL.md §4 |
| Per-route questions | route-briefs.md |
| Workflow contracts | workflow-catalog.md |
| Preferences | media-use SKILL.md (impl) + brief-contract.md §2 (consumption) |
| Recipes | media-use SKILL.md (storage) + review-loop.md §4 (offer) |
No duplicated decisions. Cross-reference graph verified — every "see X section N" points to a file and section that exists and says what's claimed.
Skill count (20→19): verified consistent across CLAUDE.md, README.md, skills-manifest.json (19 entries), all plugin manifests.
Three-branch rule: verified consistent across all five main creation workflows (PLV, faceless, pr-to-video, general-video, music-to-video). Lighter workflows have appropriate variants.
website-to-video removal: complete. All 13 files deleted, all routing/disambiguation/catalog surfaces cleaned, zero remaining w2v/w2h references.
Stale concepts: Round 1/Round 2 templates fully removed. "Collaborative or autonomous?" question properly replaced by flow × storyboard mode derivation.
Non-blocking (skills)
-
Docs files not in the diff — 4 docs pages (
introduction.mdx,showcase.mdx,hyperframes-vs-remotion.mdx,docs.json) still reference "website-to-video" as a concept name. They link to the retained/guides/website-to-videoguide page (which was retargeted), so the links aren't broken — butintroduction.mdxnaming it as a skill could confuse readers. Follow-up cleanup. -
inspect→checkrename —hyperframes-core/SKILL.md's validation checklist (outside the diff's hunk range) may still readnpx hyperframes inspectwhile the references were updated tocheck. Worth verifying.
Studio / server — clean React, one minor URL nit
Signature polling (useProjectSignaturePoll): well-designed. disposed flag handles unmount, inFlight prevents concurrent fetches, document.hidden skips background tabs, ref-based comparison avoids stale closures. The two-level cache in createProjectSignature (fingerprint → content hash) keeps the 2s poll cheap.
Frame comments (useFrameComments, frameComments.ts): clean data model. File is versioned, parser is tolerant, merge semantics (resubmit preserves unconsumed comments for other frames) are correct. buildCommentsFile tested for merge behavior.
useStoryboard background refresh: hasDataRef / lastProjectRef pattern elegantly avoids loading-spinner flash on background refresh. cancelled flag handles concurrent fetches.
Type safety: no any types, no unsafe as assertions. parseCommentsFile uses runtime type guards. fetchProjectSignature casts + validates.
Non-blocking (studio)
-
studioDeepLinkURL construction —${url}/${studioLandingSearch(...)}#project/...inserts an extra/before the hash whenstudioLandingSearchreturns"". Produceshttp://host:port/+#project/nameinstead ofhttp://host:port+#project/name. Most SPAs handle both, but it's an unnecessary behavioral change. -
Redundant
onChangeduring slow refetch — if the storyboard reload is slow (>2s), the next poll tick sees the stalesignatureRefand firesonChangeagain.useStoryboard'scancelledflag handles this gracefully (no data corruption), but it triggers 2-3 redundant fetches per change event. Tracking "last fired signature" in a ref would prevent it. -
useFrameCommentshook untested — the pure functions are tested inframeComments.test.ts, but the hook's state machine (draft tracking, submit flow, error recovery,refreshPendingon focus) has no dedicated test.
Media-use memory engine — well-designed for CLI context
Preference store: two-tier model (project + personal) is clean. PROMOTE_AT = 2 threshold is a named constant. Atomic writes (tmp + rename). Tolerant reads (corrupt/missing → empty). Input validation at the store level (unknown keys rejected, empty values rejected, style_preset without workflow rejected).
Recipe store: slugifyRecipeName thoroughly prevents path traversal (strips everything except [a-z0-9-]). Versioned re-freeze with archive (@vN suffix). BRIEF.md is the workflow source of truth (flag is fallback). Skeletonization correctly keeps structure and blanks content.
Trust model: enforced at the store level, not just docs. Best-effort user tier (try/catch, never breaks project-tier write).
Non-blocking (media-use)
-
Stale sightings in promotion ledger —
recordUserSightingaccumulates project associations but never retracts them when a project's value changes. If project A confirmsdestination = "x-feed", then later changes to"youtube", the sightings for"x-feed"still include project A. A retracted value could re-promote from phantom evidence on a future confirmation cycle. The project tier correctly restarts provenance — this is only about the personal-tier sightings ledger. Worth documenting the design choice or adding retraction logic. -
Three independent "content key" sets —
FRONTMATTER_CONTENT_KEYS,BRIEF_CONTENT_KEYS, andFRAME_CONTENT_KEYSinrecipe-store.mjsare hardcoded independently. If the storyboard format adds a new content key, it needs to be added to the right set with no shared constant or test to catch a miss. Acceptable for v1 — tech debt if the format evolves.
Summary
| Domain | Blocking | Non-blocking |
|---|---|---|
| Skills layer | 0 | 2 (docs refs, inspect→check) |
| Studio/server | 0 | 3 (URL nit, redundant onChange, hook test gap) |
| Media-use memory | 0 | 2 (stale sightings, content key sets) |
Strong SSOT discipline across a massive PR. No blocking issues. Ship it.
jrusso1020
left a comment
There was a problem hiding this comment.
Approving on @jrusso1020's call.
What
The intent layer — every creation run starts with one front-door conversation in the
/hyperframesrouter: it reads remembered defaults and recipes first, triages the input, picks the route, asks that route's own questions, closes with two run-shape questions — storyboard? and automation or companion? — and offers a capability menu before handing a locked brief to the workflow. The confirmed brief becomesBRIEF.mdat the project root; workflows ask no brief question of their own. The old collaborative/autonomous first question is gone — the execution mode derives (automation+storyboard: yes→ collaborative,automation+storyboard: no→ autonomous,companion→ general-video), and companion mode turns general-video into a co-creation session with every HyperFrames capability on the table.The review loop — collaborative runs of the narrative workflows review in three passes of rising fidelity: text plan → wireframe sketch board → animated build, each gated on the user, with a per-frame comment channel on the Studio board that batches into a sidecar file the agent picks up on its next reply. Autonomous runs are untouched: no board, no sketch pass, one preview-or-render question before render.
One website route —
/website-to-videois removed (skill count 20 → 19)./product-launch-videoowns every website input, and its own pipeline is untouched: a "show the site as-is" ask is brief intent, not a pipeline branch — the intent layer asks sell it or show it?, writes the answer intoBRIEF.md("feature the site's own captured screens as the video's assets"), and the workflow's normal steps carry it, since captured screens already flow intoasset_candidates.User memory — two tiers on media-use's existing storage split. Preferences: confirmed brief answers (destination, aspect, language, flow, storyboard, voice, style preset) become next run's recommended defaults with receipts naming their source. Recipes: one approved run frozen as a named, versioned bundle (design spec + storyboard skeleton + brief skeleton + confirmed values); the next "make another weekly-promo" fills the brief, skips the design step, and drafts from the skeletons — while every review gate still runs.
Changes — skills (the intent layer)
hyperframes/references/intent.md(new) — the sequence: memory before questions (a mandatoryprefs.mjs get+ recipe check), input triage, route pick, the route's must-haves, the two run-shape questions, the nice-to-have capability offer, and the handoff. Edit requests never trigger it; a briefed project (BRIEF.mdon disk) is read, not re-asked; a pre-BRIEF project resumes from its storyboard frontmatter — never re-interrogated.hyperframes/references/route-briefs.md(new) — per-route question sets, adaptive to the input: the three narrative workflows' fields (pr-to-video's PR-size → length table moves here whole), general-video's former Discovery, website-to-video and music-to-video with declared deferred asks (message/arc after capture; brand/genre at plan time), and the footage/deck routes where the run-shape questions don't apply (their in-flow picks — caption identity, render strategy — are announced up front, asked where the probe data lives).hyperframes/references/capability-menu.md(new) — one table, two readers: the intent layer shows the user a route-filtered slice (capture, beat analysis, blueprints, presets, media generation, figma, registry blocks…); companion-mode general-video uses it as the execution map, withskills update <workflow>before borrowing a lazily-installed workflow's script. Its design ask is a three-state question — they have a spec (frame.md/design.md→ brand truth) / the look matters (open 2–3 presetframe-showcase.htmlfiles in the browser and pick by eye; the choice records as thestyle_presetpreference) / don't care or nothing fits (workflow decides, or the design picker as a declared deferred ask). On capture routes the showcases come with the honest line: the pick is the layout bones — the site's brand colors remix on top.One owner per rule — the router keeps recognition and pointers; the conversation lives in
intent.md; field semantics stay canonical inbrief-contract.md; per-route practice lives inroute-briefs.md. Duplicated guidance (exploratory intake, mode derivation, aspect table, recipe protocol) was collapsed to its single owner.hyperframes-core/references/brief-format.md(new) — theBRIEF.mdartifact: frontmatter = the confirmed deterministic fields (keys align with the registry and the preference store), body = prose sections (Intent / Assets / Customizations / Notes) that stay project-local. Written by each workflow's Setup as its first action afterhyperframes init; the storyboard'smode:wins on resume; recipes carry its skeleton.brief-contract.md— § 1 opens with the mode derivation; § 2's registry gainsflowandstoryboard; § 3's two-round template is replaced by the intent layer's invariants, including the deferred-asks rule (declared, never sprung).All ten workflow SKILL.mds open with the same three-branch rule (BRIEF.md → read; existing project → resume; fresh creation → intent layer) — the narrative three lose their Round 1/Round 2 templates and recipe checks (Step 0 is Setup only: init,
BRIEF.md, preference recording, auth preflight); general-video gains a Setup and a Companion section and its board keys offstoryboard: yes.hyperframes-core/references/production-loop.md(new) — the stages from an approved plan to a delivered video (blocks & assets, audio, frames, duration sync, assembly, transitions, captions, verify, deliver) written as dependencies, not numbered steps: independent stages schedule freely, chains don't. It never addresses the user — every user-facing pause stays inreview-loop.md, and the file only marks the two attach points (plan approval in, final look out). The narrative workflows implement these stages with their own scripts; a freeform build follows the file directly.general-videorewritten freeform — the fixed Step 1/2/3 pipeline is gone. The skill now reads: Setup → the route is yours (three postures: automation brief / companion / edit; resemble a genre → skim that workflow's SKILL.md as a worked example, borrow the shape and the taste, never the machinery) → the laws (admission rule: a law names the action it guards and the bad render without it — scope, identity-before-HTML with the HARD-GATE, end-state-before-tween, run-shape contract, composition contract, media through media-use) → the reading map → companion as conversation (the menu doubles as the trigger list; offer at the moment, one at a time, do-then-show; the conversation is the gate when there's no board; decisions write back toBRIEF.md, which § Lifecycle now keeps as the run's living truth).capability-menu.mdv2 — every row's last column normalized to home → entry → what you get; five capabilities added (generative video: HeyGen avatar / image-to-video / dub, publish-to-stable-link, real map scenes, transcript-driven footage cuts, designed overlays on user footage with a whole-ask-vs-one-scene rule); a § Genre lenses section maps piece-resemblance → which narrative workflow'sstory-design/visual-design/motion-language/cut-catalogto borrow.Changes — skills (the review loop)
hyperframes-core/references/review-loop.md(new) — the single source for the three passes: the plan proposed on a live board (§ 1), wireframe sketches markedbuiltwith one layout question (§ 2 — real words on plain blocks, run no CLI; a confirmed board is itself a valid deliverable when the user asked for a storyboard, not a video), the build dressing confirmed layouts (§ 3, worker or inline), the final look plus a once-only recipe-freeze offer whose confirmation teaches the recall phrase (§ 4).brief-contract.md§ 1; the sidecar schema and thebuiltstatus rung instoryboard-format.md.Changes — routing (
/website-to-videoremoved)w2h-verify.mjs, bundled SFX) are deleted;hyperframes skills updateremoves it from installed machines automatically./product-launch-videois byte-identical to before except two clauses that pointed at the deleted route./guides/website-to-videoguide page keeps its URL, retargeted), andhyperframes capture's generated agent prompt.Changes — studio + server (the surfaces)
Submit comments (N)writing.hyperframes/frame-comments.json(a resubmit wins per frame; unconsumed comments on other frames are kept); submitted-but-unconsumed comments stay visible — toolbar banner + per-tile echo — until the agent consumes the file.GET /projects/:id/signature(cheap: the CLI adapter's watcher-invalidated cache), the storyboard payload carries the signature it was derived from, and the view polls at 2 s (hidden tabs skipped), refetching in place with no loading flash. Posters bake the signature into their URL so tiles fill in as sketches land; the empty state upgrades itself when STORYBOARD.md appears, and its handoff prompt points the agent at the review loop.previewlands where review is — the storyboard view while any frame isbuiltor the board is pure planning; the timeline once assembled.Changes — media-use (the memory engine)
scripts/lib/prefs-store.mjs+scripts/prefs.mjs— project.media/preferences.json(committed, the team inherits it) + personal~/.media/preferences.json; a value earns the personal tier by being confirmed in two different projects (a user-side sightings ledger accumulates the cross-project evidence), so a one-off choice never pollutes the global defaults. Keys: destination, aspect, language, flow, storyboard, voice, style_preset (per workflow).scripts/lib/recipe-store.mjs+scripts/recipe.mjs— named versioned folders, not content hashes (a recipe is an evolving bundle):freezeskeletonizes the storyboard (structure kept — durations, transitions, srcs, Video direction; statuses reset; content blanked to per-frame fill-ins naming each beat's role) and, when the project has aBRIEF.md, the brief (reusable frontmatter kept; run-shape keys and prose blanked — a recipe never locks the run's shape);listmerges tiers,useimports and copies the frame spec in, handing back both skeleton paths (recipes frozen beforeBRIEF.mdexisted degrade gracefully).resolve --type recipe --entity <name>delegates like grade/lut.The trust model
flow/storyboardreorders the recommendations; the two run-shape questions are never skipped.