Place prepared Takes on an independent Timeline with gaps and overlaps. Present picture, sound and captions through explicit Use rules while preserving source timing and project-authored styles.
Unify Companion bands, parameter ownership and source writeback in Studio. Migrate components, examples and authoring guidance, remove retired Speech/Semantic Track interfaces, and retain protocol identifiers at @1.
Validation: repository suite (786 tests; six sandbox-blocked tests passed on permitted rerun), 43 Studio tests after audit fixes, TypeScript, public declarations, four example-package builds, chat Source check and npm file inventory.
Speech publishes semantic timing and original audio. Media Performance and project components consume the prepared material, preserving source offsets across Takes while controlling their own framing and motion.
Add explicit renderer programs for coordinated HTML/CSS/JavaScript scenes, with typed media children, backdrop filtering and blending. Remove stand-in and estimated-take preview paths, migrate Sources and documentation, and demonstrate a Moment-driven video-to-diagram handoff.
A project-local package builds its picture in TypeScript: its Producer returns a list of elements, and
the field names on those elements are fixed by the Composition Types it imports. Nothing authored
carries them, so `check` had no Record to validate — the object does not exist until the Producer
runs, which is at Build. A media element's artifact field is `artifact`, and a package writing
`source` instead passed `check` completely green and failed a paid Build.
The compiler decides those names without running anything, and it never ran on a project: the CLI
loads TypeScript through tsx, which strips types without reading them, and the tree's own
`tsconfig.json` covers the packages it ships rather than the ones a project writes.
`hypit check` now typechecks `<project>/packages/*/src`, with the options the Distribution checks
itself by, so a project package is held to the standard the packages it imports are held to. Node
resolution reaches none of those from a project — Hypit finds `@hypit/x` by its own walk to
`<root>/packages/x` and nothing links it into `node_modules` — so the mapping is read out of each
Distribution package's own `exports`.
Measured on a real project: `hypit check` takes 1.8s in total. As authored it reports one error, a
real one nobody had seen — a label passed to an assertion that takes none, so its refusal names
nothing. With the reported typo injected it reports that one plus exactly
`TS2353: 'source' does not exist in type 'VisualMediaElement'`, at the file and line. Every shipped
example reports none.
Separately, the Build-time refusal was describing a state it manufactured. Spreading an absent
artifact produced `{}`, so the field the author actually wrote was dropped without a word and the
identity check one call later reported `.artifact` as invalid — a field they had not typed. A media
element with no artifact now says so.
`hypit-whisperx-prepare` failed for a reason other than the one reported: `download_dir` is explicit,
so `NLTK_DATA` never decided anything. `nltk.download` fetches its index before it looks at what is on
disk, so preparing an installation that needed nothing still needed the network. Already installed is
now already done.
Two facts a package author needs, placed where the author is already reading rather than in a
document: a Type is its owning Module and its name, and two Types with identical value shapes are
distinct; `blobArtifact` is declared by `@hypit/artifact`, which is the Module that identifies it
whatever re-exports it.
A full reconstruction reported fourteen places the route cost it time. Twelve held. What is here is
the subset that changes the next run rather than the last one, and most of it is code: a fact stated
in a document is a fact every future reader has to carry, so where the tool could stop being wrong
instead, it does.
**The cut re-anchors instead of dropping.** An element given the span it occupies and a Moment to
close it — `during="program" until={story.moment.done}` — is on screen for the whole of any stretch
that ends before that word. `render_element` dropped it, so the one element a reference is about was
unrenderable over every Segment but the last, while the loop asks for it to be compared over each of
them. A close that has not arrived is now removed from the tag and the element stays. Marked before
the cut it really is gone, and marked inside it survived already. Measured on the run that found it:
one Segment renderable before, four after, and the two that still refuse now name a Producer failure
the drop was hiding.
**The package root comes from the Run.** It was the working directory, so naming a Run by path
resolved its imports somewhere else and the project's own elements read as packages that do not
exist. It is now the nearest `package.json` at or above the Run — the same walk `hypit check` makes.
`--package-root` still overrides it and is no longer needed to check a project from anywhere.
**The mock's frame is a hairline.** At a twentieth of the shorter side it was fifty pixels thick on a
full-frame mock, and an observer measuring what an element covers read that as the element stopping
short of the edge — reported every round, about the mock rather than about the Source.
**The comparison names its own pair.** The prompt asks for each difference to say which of the two it
is in, and `observers.md` says which is which: one is the reference, two is the render. The observer
is not told, and the caller was not either.
**`--reference-id` is in the command block** it was missing from, with what it decides. Six of the
run's first comparisons were refused for a mismatch this flag exists to prevent, and it was found by
grepping the tool's source.
Corrections that cost no length: `vocabulary.md` had the inspect output's shape wrong — Recipe
properties are inside the `recipe` attribute, not beside `attributes` — which is why the run read the
file's central claim as false. `styles.md` and `composition.md` documented two Cue keys the Style
refuses and a `display=` that is `document=`.
Two facts moved to where the reader already is rather than into the skill: `media-track`'s `extent`
summary says the ratio is what it decides, and `VisualElement` says position is measured from the
parent. The other runtime-only errors already name their own fix.
Four conflicts. Two were mechanical: `align-transcript.ts` and
`svml-playground/src/pipeline/placeholder.ts` are modify/delete, and the
deletion is this branch's intent — local alignment removes the need to
fabricate a program-audio digest, and Studio has no stand-ins to draw.
The other two are the same bug fixed twice. Both sides found that CSS
`-webkit-text-stroke` is centred on the glyph edge, so half the stroke eats
the letterform, and both moved to `VisualTextPaintLayer` with
`placement: "outside"` — which already existed at the merge base on
`VisualTextFlowElement`. That shared starting point is why they converged an
hour apart.
`packages/caption-fine/src/render.ts` and its test take main's version. Main
extends `VisualTextElement` and the HyperFrames renderer rather than moving
Caption Fine onto text-flow, and it carries two fixes this branch does not:
CSS blur is a radius covering about two deviations while the filter takes a
deviation, so passing the authored number through doubles every shadow and
glow in the repository, and its filter-definition dedup takes 80 defs to 16.
Neither file is touched by `a4f2b31f`, so taking main's reverts `d5ee32c7`
exactly and nothing else. `renderFineCaption` works on `ProgramSpace`, which
survives here as a projection of `SemanticTrack`, so the semantic API change
does not reach it.
The Pill-as-paint-layer and structured-typography work in `d5ee32c7` is a
real gain and is not preserved here; it belongs on top of main's
implementation as its own change.
`pnpm check` clean; `pnpm test` 510 pass, 0 fail, 15 skipped.
A text stroke is centred on the glyph edge. Half its width is inside the letter,
always, and the only thing left to decide is which half gets painted over —
either the outline eats the letterform, or it shows at half the width the author
asked for. Neither is an outline around the outside of a word.
Placing it needs Paint rather than style, and Text Flow already has that
vocabulary: a stroke layer carries `placement`, and the renderer builds the ring
by dilating the glyph's alpha and subtracting the glyph from it. Nothing of the
letter is spent. Plain text elements could not say this, so they now carry the
same `paints` the Text Flow element does — the same type, validated by the same
assertion, drawn by the same filters. Caption Fine keeps its per-word elements,
its karaoke layers and its Pill exactly as they were.
Measured on "Coffee" at size 64 through the real pipeline, with the outline at
8px: the letter body is 2957 pixels with the outline and 2957 without it, to the
pixel, while the ring adds 9345 outside. The centred stroke it replaces showed
3044 — half the width, because that is all a centred stroke can show.
Two smaller things fall out. A filter layer now sets `text-shadow:none`, because
it shapes itself from its own alpha and an inherited shadow would be dilated
into the outline. And a plain text element may not declare Paint beside a raw
stroke style, since that is two ways to say one thing and both would draw.
Known limit: shadow, glow and long shadow stay CSS text-shadow — long shadow is
up to 32 stacked shadows and the IR shadow Paint is one offset — so they paint
above the ring rather than beneath it. Outline and shadow are both dark in every
recipe here, so it does not show; a light shadow over a dark outline would.
The three example package locks conflicted: both sides had regenerated
them. Neither side's digests were still correct, since the merged tree
combines their source refactor with the license field added to every
manifest, so the locks were rebuilt from the merged bytes rather than
resolved by picking a side.
Every workspace manifest omitted a license field, so a scanner reading
the published repository would resolve all 90 of them as unknown. Both
Python services went further and declared plain Apache-2.0, which is
looser than the LICENSE they actually ship under.
Package bytes feed the example package locks, so adding the field
invalidated all three; refreshed them. Only closureDigest and artifacts
move — same 31 selected packages, nothing added or removed.
Also record what the READMEs never said: ffmpeg is a real prerequisite
for a Build and had zero mentions, and the media Lambda layer is a GPL
FFmpeg build. That build is deliberate — libx264 is GPL-only and the
encode path requires it — and it stays lawful only because the operator
builds and deploys it into their own account. Publishing a prebuilt
layer would break that, so the boundary is now written down.
The umbrella video-contracts package collapsed eight unrelated
vocabularies into one release unit. Public video communication now
lives in small, independently versioned contract packages: narrative,
media, program-space, speech, speech-evidence, semantic-map, visual-ir
and composition. Speech implementation packages are renamed for what
they do (speech-spine, speech-basis, speech-alignment), realization
folds into run, and hyperframes-render becomes render-hyperframes with
a renderer-neutral Visual IR spec. Every model package gains a direct
activation entry, and shared video test fixtures move to test-support.
Example package locks are regenerated for the decomposed closure; the
graph-check, live, echo-pro-aroll and bootstrap sources all compile
against the new package set.