feat(skills): pre-plan persistent-subject choreography (Addition C)#551
feat(skills): pre-plan persistent-subject choreography (Addition C)#551vanceingalls wants to merge 1 commit intovance/04-28-content-culturefrom
Conversation
User feedback from eval 5 exposed a real R4 failure mode: - p2 Thread: Maya's card persisted across scenes (good) but often landed in positions that didn't fit the scene's content hierarchy (bad). Semantic-mismatch. - p3 Vermeer: painting zoomed to 2.1x in scene 2, blocking the right-column metadata the scene subagent had authored. Size- collision. Root cause: expansion identifies the subject, scene subagents author their scenes INDEPENDENTLY (picking their own layouts), and the scaffold tweens the subject's position/scale across transitions as an afterthought. Scene subagents don't know how big the subject will be in THEIR scene, so they place their chrome wherever, and then the scaffold's tween scales the subject up or moves it into regions already filled. Fix: expansion pre-plans the choreography BEFORE the per-scene detailed breakdown. Per-scene block specifies: - position (center x, y) - scale - size_envelope (actual on-screen footprint in this scene) - role (focal / background anchor / data-point / glyph-slot / ...) - reserved_region (rectangle scene subagent must leave empty) - scene_must_avoid (short instruction) Orchestrator (multi-scene.md) passes each scene subagent its scene's choreography block plus the contract: - The scaffold owns the subject's DOM + timeline. - Your scene layout MUST respect the reserved region. - Design your scene chrome around the element's role in this scene. - Do NOT animate the persistent subject in your timeline. Invariants: - Size envelope reflects the SETTLED state (if the subject scales up during the scene, reserve for the larger size). - Scene-to-scene positions trace a visually-coherent path, not leapfrogs. - Role changes drive reserved-region size. Branch worktree only. Not synced to installed main skill path.
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
jrusso1020
left a comment
There was a problem hiding this comment.
Staff review
TL;DR: references/multi-scene.md is the strongest piece of this stack — the Scene Fragment Spec (3 markers, ID prefix, prohibited patterns) is exactly the kind of explicit guardrail that converts implicit failure modes into checkable rules. The persistent-subject choreography YAML is a good fix for a real problem (subject scaling up into reserved region collides with content). But there's a runtime claim that doesn't match the codebase, R4 is still undefined, and the "Phase 2b" / "Step 0a" pattern continues the accreted-edit numbering smell.
Blocking
1. @hyperframes/core/assemble doesn't exist. Phase 3's example code:
const { assembleScenes } = await import("@hyperframes/core/assemble");will throw Cannot find module. I checked packages/core/package.json — the subpath exports are ./lint, ./compiler, ./runtime, ./studio-api, ./text, ./registry. There is no ./assemble, and no assemble* source file under packages/core/src/. Either ship the assembler in this PR (add packages/core/src/assemble/index.ts + the export) and write tests against it, or change Phase 3 to a documented procedure that doesn't depend on a nonexistent module.
2. R4 still not defined. Grepped the cumulative state at the top of the stack — R4 (persistent-element continuity morph) is referenced in #550 and used as the gating condition here ("When R4 applies...") but never has a definition anywhere. If this PR introduces the persistent-overlay rule, define R4 explicitly (one-liner is fine: "R4 — when a named real-world subject persists across 2+ scenes, render it in a shared overlay layer outside .scene containers and migrate it across transitions"). Otherwise readers can't tell when the rule fires.
3. Phase 2b without Phase 2a is the same accreted-edit smell as #549's 0a/0b. Renumber: "Phase 1: Scaffold + Scene subagents" / "Phase 2: Streaming evaluation" / "Phase 3: Assembly" reads cleanly.
Significant
4. 1920×1080 is hard-coded into the choreography YAML. The example uses { x: 1380, y: 540 }, { w: 540, h: 680 }, etc. Hyperframes supports any dimension declared via data-width/data-height (square, 1080×1920, etc.). Either generalize the YAML (percentages, or "relative to the composition's declared dimensions"), or label the example explicitly as "for a 1920×1080 composition; scale to your data-width/data-height."
5. multi-scene.md is 188 lines and has multiple self-contained sub-specs. The Scene Fragment Spec alone is dense and consumed by both the assembler code and the orchestrator. Worth extracting to references/scene-fragment-spec.md so the assembler can link it independently. Trade-off accepted if you'd rather keep one file — but note that 188 lines is at the upper end of "reference loaded just-in-time."
6. Density target is unmotivated. "15+ animated elements? 3 parallax layers?" appears in Phase 2b's content validation as a checkable target with no source or rationale. Where do these numbers come from? Are they enforced anywhere? Either link to the rule's origin (house-style.md? a benchmark?) or drop.
7. Persistent-subject scaffold contract is incomplete. The text says "the scaffold owns the subject's DOM + timeline" and "the scaffold authors the subject's tweens between choreography positions" — but the scaffold-author's instructions don't show how the choreography YAML translates to scaffold tweens (which .to() calls, with what eases, on what timeline). If the scaffold is supposed to consume the YAML, give it an example. If a separate "scaffold scaffolder" handles this, name it.
8. Stacks on #549's design.md schema gap. Scene subagents receive "the design.md (or its values summarized)" — but the schema isn't pinned down in #549, so "summarized" can mean anything.
Smaller things
- Contrast section wording: "Small text (under 24px) has no large-text exemption" reads as an exception to a rule that wasn't stated. WCAG AA is 4.5:1 for normal text, 3:1 for large text (24px+ regular / 19px+ bold). If the policy is "always 4.5:1, no exceptions" just say that — it's clearer.
- "Maximum 2 retries per scene — if a scene fails 3 times...": 2 retries = 3 attempts total. Fine, but worth confirming the orchestrator counts the same way (some implementations would call this "3 retries").
- Visibility kills:
tl.set("#sceneN", { visibility: "hidden" }, exitEndTime)—tlis referenced before the scaffold'sgsap.timeline({ paused: true })line is shown. Reorder or add a one-line "the scaffold's timeline variable istl" so the example is self-contained. - "Run
npx hyperframes lint" / "Runnpx hyperframes validateif available": both commands exist inpackages/cli/src/commands/(verified). Drop "if available" onvalidate. - Empty PR template + no test plan, same as #549/#550.
What this PR gets right
- Scene Fragment Spec is excellent. The explicit prohibited-patterns list (
<!DOCTYPE,<script>/</script>tags wrapping the GSAP section,tl.from,gsap.timeline(, bare class names withouts{N}-prefix, CSStransformon GSAP-animated elements) is exactly the kind of guardrail that converts past failures into checkable rules. The "Why this prohibition exists" comments inline are the difference between "rule" and "rule that future Claude actually follows." - "Who runs this pipeline" section is great defensive design. Explicitly handling the nested-subagent case (no
Agent/Tasktool available → build serially, note the constraint in your final report) prevents the silent-broken-pipeline failure mode. - Persistent-subject contract addresses a real failure: scenes authored without knowing the subject's bounding box at its FINAL state, then the scaffold scales it up into reserved region. The YAML format with
position,scale,size_envelope,role,reserved_region,scene_must_avoidis well-thought-out. The Vermeer example with three scenes at 1.0x → 2.1x → 0.85x is concrete enough to copy. - Streaming Phase 2b (evaluate as scenes finish, don't batch) matches modern subagent orchestration best practices.
Recommended path
Block on (1) — either ship the assembler module or change Phase 3 to not depend on it. Block on (2) — define R4 inline. Renumber Phase 2b → Phase 2 (3). Generalize the YAML coordinates (4). De-block on (8) once #549 lands. The Scene Fragment Spec, "Who runs this pipeline" section, and choreography YAML format are keepers.

What
Brief description of the change.
Why
Why is this change needed?
How
How was this implemented? Any notable design decisions?
Test plan
How was this tested?