commit 4b873202e0b8865bc07dc26a74d40b4475155e37
parent 702b87bc3d42c2d6ac75d903af6a1a3686436859
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Thu, 1 Oct 2026 16:51:34 -0400
plans: Teaser V1, as built — the beat, nothing squeezed, the gates and the four variants
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 51 insertions(+), 0 deletions(-)
diff --git a/plans/deck-posts.md b/plans/deck-posts.md
@@ -358,3 +358,54 @@ Found and left:
- `onscreen-posts.spec.ts`'s timings at 15 fps (above) — resolved: 3c70c6d3 on the main line made
the posts and mute specs follow whole-frame holds and the build's mute fade, and the merge
carries it.
+
+## Teaser V1, as built
+
+Branch `deck/teaser-v1` from 07d1fa08. The ask: a little more delay between the teaser's hits, and
+variation clips to judge before the full stitch.
+
+| Commit | What |
+|---|---|
+| 6869bbc7 | `deck.mjs`: `TEASER_BEAT` (0.4–2.5 s), `teaserMotion(beat)`, `teaserSeconds(entry)`; `teaserTimes(lines, tail, m)` no longer compresses and reports `need`; `validateTeaser` checks `beat` and refuses a `seconds` short of `need`; `estimatedDuration` of a teaser is `teaserSeconds`. chrome-teaser, compose-chrome, verify-build and `buildTeaserSegment` (now exported) read `teaserSeconds` / `teaserMotion`. Tests in `chrome-teaser.test.mjs` and `teaser-audio.test.mjs` |
+| 8a0b1b7e | README (`beat`, `seconds` optional) and the teaser's `[Unreleased]` bullet amended in place |
+
+Rulings, as built:
+
+- **`beat`** is the seconds from one line's pop to the next, hits included; default
+ `TEASER_MOTION.gap` (0.7). The second tier's delay stays 3/7 of the beat and the tail's wait
+ 8/7 (the default's 0.3 and 0.8 of 0.7), so a slower beat is the same rhythm slowed. The first
+ landing (timed to the dissolve), the slam's hit and settle, the tail's 1.7 s fade (and its
+ swell) and the 1.2 s end room do not scale. The hits are still `teaserHits` from `teaserTimes`.
+- **Nothing is squeezed.** A `seconds` shorter than `need` (the last pop, the tail's fade, the
+ 1.2 s hold) is refused with the length needed, rounded up to a tenth. Without `seconds` the
+ card is that length, at least 3 s; one that would need more than 20 s is refused.
+- `teaserTimes` keeps `scale` (always 1) and `T` (now only rounding): the page carries `scale` in
+ its data, and `need` is stripped from it, so a teaser without `beat` — or with `beat: 0.7` —
+ composes a byte-identical page. A test pins the ferret page's sha256 at 07d1fa08.
+
+Gates on 8a0b1b7e:
+
+- Workspace tsc clean (21 s). `test:scripts` 370 pass, 2 skipped, 2 failed: the queue-lock FIFO
+ and banner cases under load; `queue-lock.test.mjs` alone 11/11, three times.
+- Capped umtool `next build` with the corpus linked: exit 0 (58 s), link removed.
+- Byte-identical without `beat`: the ferret teaser's frames key is `b8ebcaf5…` at 07d1fa08, at the
+ tip and with `beat: 0.7`; the segment key `7c87943c…` the same at both. A fresh render at the tip
+ wrote 210 frames whose PNGs are byte-identical to the ones the cut was built from, and its
+ `fin.mp4` is the same file as the cut's (same sha256, same decoded video and audio md5).
+
+The variants (scratch build through `buildTeaserSegment`, `cutJoins`, `cutOffsets`, `xfadeGraph`;
+the deck's own frames laid over c20; trimmed to c20's last 3 s; the encode's `encodeArgs`):
+the reference is the manifest's entry, and the others keep its still hold after the tail
+(about 1.05 s past `need`), so their `seconds` grows. The manifest's `seconds: 7` holds beats up to
+about 0.99; at 1.05 and 1.3 it is refused with 7.2 and 8.1.
+
+| Beat | `seconds` | `need` | Clip | Hits in the teaser's clock (overline, title, second tier, kicker; swell) |
+|---|---|---|---|---|
+| 0.7 (none set) | 7 | 5.95 | 9.5 s | 0.75, 1.45, 1.55, 2.45; 3.05 |
+| 0.9 (+29 %) | 7.7 | 6.66 | 10.2 s | 0.75, 1.65, 1.84, 2.94; 3.76 |
+| 1.05 (+50 %) | 8.3 | 7.2 | 10.8 s | 0.75, 1.80, 2.05, 3.30; 4.30 |
+| 1.3 (+86 %) | 9.1 | 8.09 | 11.6 s | 0.75, 2.05, 2.41, 3.91; 5.19 |
+
+Each clip's video and audio are the same length; the measured level onsets (an 8 dB rise per
+50 ms window) land on the overline, title and kicker hits at every beat — the second tier's
+lighter hit falls inside the title's decay, as designed.