You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(skills,lint): correct composition-contract claims the code contradicts (#3468)
The runtime absorbed a series of authoring mistakes over time and `runtime/init.ts`
says so in its own comments, but the skills kept teaching the old rules. Four of
them actively cost an agent a failing run: add `crossorigin` (lint rejects it
unconditionally), never build a timeline inside `async` (lint calls that the
documented contract), never `gsap.set` later-scene clips (two fixHints instruct
exactly that), and 12 copyable media snippets with no `id`, which render silent.
Corrected in every place each claim appeared, including `hyperframes-animation`,
three workflow scripts, the scaffolded project instructions, the CLI `docs`
command, and the public docs site: `data-track-index` is a Studio display lane
the render never reads, `class="clip"` is a layout convention rather than a
visibility requirement, timed elements may nest, the visibility window is
half-open, sub-composition host dimensions are backfilled, and the root-fill rule
applies only to the layered-composite path.
Behaviour changes, each backed by a render rather than by reading code:
- `timeline_registry_missing_init` deleted. The runtime creates the registry
before any inline script; a composition without the guard line renders and
animates correctly.
- `video_nested_in_timed_element` kept, message corrected. A rendered repro shows
the nested-with-local-start case really does break, so the rule guards a real
defect, but nothing is "FROZEN": the extractor ignores the wrapper's offset
while visibility uses it, so the clip shows wrong frames and then vanishes.
- `mediaRenderIds` now stamps media whose source is a `<source>` child, closing a
duplicate-id gap the old `[src]`-only selector left open.
- Stale messages fixed on `subcomposition_root_styled_by_class` and
`deprecated_data_layer`.
`coreSkillContent.test.ts` pinned the literal sentence that made root
`data-start` look required, so it is narrowed to structure plus the regression it
genuinely catches.
Not covered, and flagged in the PR: the media global-vs-local start heuristic in
`runtime/init.ts` is the root cause behind the nested-video defect. Removing it
changes the meaning of existing compositions and needs its own deprecation.
Copy file name to clipboardExpand all lines: docs/reference/html-schema.mdx
+5-4Lines changed: 5 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -97,11 +97,12 @@ unbounded animation, and timeline-free compositions need an explicit duration.
97
97
|`id`| Yes | Stable identifier for timing, editing, and animation |
98
98
|`data-start`| Yes | Start in seconds or a relative timing expression |
99
99
|`data-duration`| Yes for DOM, image, and nested-composition clips | Visible slot length in seconds |
100
-
|`data-track-index`|Yes|Timeline lane used to prevent temporal overlap |
101
-
|`class="clip"`|Yes for authored timed DOM and image elements |Lets the runtime own their visibility window|
100
+
|`data-track-index`|No|Studio timeline lane, display only. The render never reads it and it does not prevent overlap |
101
+
|`class="clip"`|Recommended for authored timed DOM and image elements |Layout and tooling convention. Visibility is keyed off `data-start`, but the shared `.clip` rule supplies the full-frame box|
102
102
103
103
`data-track-index` does not control paint order. Use CSS `z-index` for
104
-
front-to-back layering. Two clips on the same track must not overlap in time.
104
+
front-to-back layering. Two clips on the same track may overlap in time; the
105
+
lane is a Studio display row, not a scheduling constraint.
105
106
106
107
Video visibility is managed as media and does not require `class="clip"`.
107
108
Audio has no visual lifecycle.
@@ -124,7 +125,7 @@ Audio has no visual lifecycle.
124
125
| --- | --- | --- |
125
126
| `data-media-start` / `data-playback-start` | Video, audio, nested composition | Offset into the source file, used by trim and split. Two groups of readers disagree, so the right name depends on the element. **Read only `data-media-start`:** the timing compiler, the HTML parser, `hyperframes validate` (which only inspects `<audio>`), and the engine's audio mixer (which feeds ffmpeg `-ss`). **Read `data-playback-start` first, falling back to `data-media-start`:** the runtime player, Studio (which also writes it), and `hyperframes snapshot`. Because the audio mixer reads only `data-media-start`, a `<video>` authored with just `data-playback-start` renders a trimmed picture over untrimmed audio. Set the name by kind: **`<video>` / `<audio>` → `data-media-start`**; **nested composition → `data-playback-start`** — composition hosts are inspected only by the playback-start-first readers (the media-start-only ones are all `<video>`/`<audio>`-scoped). `data-media-start` still works there as a fallback, but `data-playback-start` is the canonical name Studio writes and normalises to for new composition hosts, so the other name works until an edit rewrites it (it is the [child-timeline offset](/concepts/compositions)). |
126
127
|`data-playback-rate`| Video, audio, nested composition | Playback multiplier from `0.1` to `5`|
127
-
|`data-volume`| Video and audio | Static volume from `0`to `1`|
128
+
|`data-volume`| Video and audio | Static gain. `1` is 0 dB, `0`is silence, and values above `1` boost up to `3.98` (+12 dB)|
128
129
|`data-has-audio="true"`| Video | Declares that the video contributes audio |
129
130
130
131
Video and audio may omit `data-duration` when their intrinsic duration is known
Copy file name to clipboardExpand all lines: packages/cli/src/docs/data-attributes.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,12 +6,12 @@ Core attributes for controlling element timing and behavior.
6
6
7
7
-`data-start="0"` — Start time in seconds
8
8
-`data-duration="5"` — Duration in seconds
9
-
-`data-track-index="0"` — Timeline track number (controls z-ordering)
9
+
-`data-track-index="0"` — Studio timeline lane, display only. The render never reads it, and it does not control paint order (use CSS `z-index`) or prevent overlap. Optional.
10
10
11
11
## Media
12
12
13
13
-`data-media-start="2"` — Media playback offset / trim point (seconds)
14
-
-`data-volume="0.8"` — Audio/video volume, 0 to 1
14
+
-`data-volume="0.8"` — Audio/video gain. `1` is 0 dB, `0` is silence, and values above `1` boost up to `3.98` (+12 dB)
15
15
-`data-has-audio="true"` — Indicates video has an audio track
16
16
17
17
## Composition
@@ -23,4 +23,4 @@ Core attributes for controlling element timing and behavior.
23
23
24
24
## Element Visibility
25
25
26
-
Add `class="clip"` to timed elements so the runtime can manage their visibility lifecycle.
26
+
Add `class="clip"` to timed elements. The runtime keys visibility off `data-start`, not this class, but the shared `.clip` rule is what gives a scene its full-frame box and Studio treats it as an edit hint.
Copy file name to clipboardExpand all lines: packages/cli/src/templates/_shared/AGENTS.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -88,8 +88,8 @@ Fix all errors before presenting the result. Warnings should be reviewed before
88
88
89
89
## Key Rules
90
90
91
-
1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
92
-
2.Elements with timing **MUST** have `class="clip"` — the framework uses this for visibility control
91
+
1. Every timed element needs `data-start` and a duration. `data-start` is what marks it as timed; `data-track-index` is an optional Studio display lane the render never reads
92
+
2.Give timed visual elements `class="clip"`. The framework keys visibility off `data-start`, not the class, but the shared `.clip` CSS is what gives a scene its full-frame box, and `lint` warns without it
93
93
3. Timelines must be paused and registered on `window.__timelines`:
Copy file name to clipboardExpand all lines: packages/cli/src/templates/_shared/CLAUDE.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -88,8 +88,8 @@ Fix all errors before presenting the result. Warnings should be reviewed before
88
88
89
89
## Key Rules
90
90
91
-
1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
92
-
2.Elements with timing **MUST** have `class="clip"` — the framework uses this for visibility control
91
+
1. Every timed element needs `data-start` and a duration. `data-start` is what marks it as timed; `data-track-index` is an optional Studio display lane the render never reads
92
+
2.Give timed visual elements `class="clip"`. The framework keys visibility off `data-start`, not the class, but the shared `.clip` CSS is what gives a scene its full-frame box, and `lint` warns without it
93
93
3. Timelines must be paused and registered on `window.__timelines`:
`Root element has class="${rootClasses.join(" ")}" and is styled by ${offenders.length} rule(s) keyed off that class (e.g. ${example}). `+
963
-
`At render, every sub-composition rule is scoped to [data-composition-id="${rootCompositionId}"] <selector>, so a selector whose leftmost part is the ROOT's own class becomes a descendant selector that cannot match the root — the scene renders unstyled (tiny text top-left, full-size images). `+
964
-
`lint/validate/inspect and Studio's per-frame iframe preview do not scope, so this passes every static check and looks correct in preview.`,
967
+
`At render, every sub-composition rule is scoped to [data-composition-id="${rootCompositionId}"] <selector>, so a selector whose leftmost part is the ROOT's own class becomes a descendant selector that cannot match the scoped element itself. `+
968
+
`Since #1886 the producer preserves the authored root as an inner wrapper, so this no longer renders the scene unstyled, but #root is the shape the scoper special-cases and the registry blocks model. Use it so preview, render, and Studio agree.`,
965
969
selector: example,
966
-
fixHint: `Give the root id="root" and style it with \`#root { ... }\` plus plain descendant selectors (\`.kicker\`, \`#hero\`) — the runtime already scopes each sub-composition by data-composition-id, so a class namespace on the root is redundant and breaks under scoping.`,
970
+
fixHint: `Give the root id="root" and style it with \`#root { ... }\` plus plain descendant selectors (\`.kicker\`, \`#hero\`) — the runtime already scopes each sub-composition by data-composition-id, so a class namespace on the root is redundant.`,
0 commit comments