Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4810196
fix(newsletter-reels): gallery plays only the m1/m2 stock tracks, nev…
axsaucedo Oct 25, 2026
ea639ff
fix(newsletter-reels): export defaults to the m1 stock track and refu…
axsaucedo Oct 25, 2026
a11dda5
feat(newsletter-reels): build stock-track variants for every reel as …
axsaucedo Oct 25, 2026
7ee0b71
docs(newsletter-reels): issue 410 learnings - stock music only, stale…
axsaucedo Oct 25, 2026
3c06c76
feat(newsletter-reels): slice the licensed Sonniss library into role-…
axsaucedo Oct 25, 2026
0dd89ca
feat(newsletter-reels): recorded-sample mode in sfx.py behind SFX_LIB…
axsaucedo Oct 25, 2026
755f0de
feat(newsletter-reels): build m3 variants (m1 track plus recorded sfx…
axsaucedo Oct 25, 2026
848b486
feat(newsletter-reels): gallery offers m3 and shows the audio buttons…
axsaucedo Oct 25, 2026
16b7485
fix(newsletter-reels): briefs.py accepts any direction letter, not on…
axsaucedo Oct 25, 2026
da01e79
wip(newsletter-reels): three synthesised sfx palettes behind SFX_PALE…
axsaucedo Oct 25, 2026
b54cbb0
Revert "wip(newsletter-reels): three synthesised sfx palettes behind …
axsaucedo Oct 25, 2026
5eb7d91
Revert "feat(newsletter-reels): gallery offers m3 and shows the audio…
axsaucedo Oct 25, 2026
e7296bf
Revert "feat(newsletter-reels): build m3 variants (m1 track plus reco…
axsaucedo Oct 25, 2026
043c8a8
Revert "feat(newsletter-reels): recorded-sample mode in sfx.py behind…
axsaucedo Oct 25, 2026
8918d1b
Revert "feat(newsletter-reels): slice the licensed Sonniss library in…
axsaucedo Oct 25, 2026
318c0bb
feat(newsletter-reels): make motion-first the default - 1-2 s beats, …
axsaucedo Oct 25, 2026
44ec62e
docs(newsletter-reels): record why motion-first won on 410 and catalo…
axsaucedo Oct 25, 2026
effcb9e
feat(newsletter-reels): gallery shows the m1/m2 buttons for every ree…
axsaucedo Oct 25, 2026
e3a8983
feat(newsletter): buffer-post prefers a post folder's reel.mp4 over i…
axsaucedo Oct 25, 2026
5da2d56
fix(newsletter): buffer-post refuses media over jsDelivr's 20 MB limi…
axsaucedo Oct 25, 2026
a6c4007
docs(newsletter-reels): re-encode post reels under 20 MB for jsDelivr…
axsaucedo Oct 25, 2026
47e930a
docs(newsletter-reels): simplify how the worker brief states that Mot…
axsaucedo Oct 26, 2026
c71c3a9
feat(newsletter-reels): base vs motion-max experiment - directions d-…
axsaucedo Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions .claude/skills/newsletter-reels/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
---
name: newsletter-reels
description: Make short motion-graphics reels for a newsletter issue - one 1920x1080 reel for the issue itself and one 1080x1350 reel per article post, 20-30 s with music - five directions each built by parallel Opus workers, reviewed and picked in a local gallery, revised in rounds, scored with a stock track or the composed score, exported once. Use after newsletter-issue step 8, or when asked for reels, shorts or videos of the issue's posts.
description: Make short motion-graphics reels for a newsletter issue - one 1920x1080 reel for the issue itself and one 1080x1350 reel per article post, 20-30 s with music - five directions each built by parallel Opus workers, reviewed and picked in a local gallery, revised in rounds, scored with one of the owner's stock tracks (never the generated score), exported once. Use after newsletter-issue step 8, or when asked for reels, shorts or videos of the issue's posts.
---

# Newsletter reels

Six topics: `0-issue`, the newsletter's own reel (1920x1080, LinkedIn's landscape feed video), and the five article posts (1080x1350). Five reels per topic, each a different direction, built in parallel by headless Opus workers on the vendored motion engine in `engine/`. The owner reviews live HTML pages in a gallery, picks one per article, and every change after that is a round on the same worker. The mp4s are exported once, at the end.

**Music rule, no exceptions:** every reel the owner hears or receives plays one of the owner's stock tracks from the main checkout's `.music/pixabay/`. The worker's composed score is only a beat grid that the cuts and sound effects lock to: it is never played in the gallery, never offered as a choice, and never exported. `gallery.html` plays only the m1/m2 stock variants, `export.py` refuses `score`, and `stock_music.sh` builds both variants for every reel as soon as it lands (step 6).

In the commands below `$SK` is this skill's directory and `$R` is `tmp/reels/<N>/`. Scripts live in `$SK/scripts/`.

## 1. Prepare the inputs
Expand All @@ -18,7 +20,9 @@ In the commands below `$SK` is this skill's directory and `$R` is `tmp/reels/<N>

## 2. Write the directions

Read `references/directions.md` and `references/learnings.md`. Write `$R/directions.md` in the same format: per topic a palette line and five directions `- **<n><a-e> Name (capability):** mechanism`. Each direction names the mechanism the film acts out, and the five per topic show off different capabilities (3D, UI simulation, data viz, kinetic type, particles, hand-made). Directions are written for the article, never reused across topics.
Read `references/directions.md` and `references/learnings.md`. Write `$R/directions.md` in the same format: per topic a palette line and five directions `- **<n><a-e> Name (capability):** mechanism`. Each direction names the mechanism the film acts out, and the five per topic each commit to a different primary style from the worker brief's Motion-first menu (kinetic type, HUD/technical, 3D camera fly-through, generative/organic, device in scene), so the owner compares five genuinely different films. Directions are written for the article, never reused across topics. Do not write a calm explainer direction: the worker brief's Motion-first section is the default because the owner picked it over the calm reels in four of five topics on 410.

**Running experiment (from issue 411): base vs motion-max.** In every topic, write directions a-c on the base brief and directions d-e as the motion-max arm, by putting `motion-max` in the capability brackets, e.g. `- **2d Dive through the stack (motion-max, 3D fly-through):** ...`. `briefs.py` appends `briefs/motion-max.md` to those two: hard targets (12+ visual states, 3+ moving layers on every frame, a camera move per scene, 3+ transition kinds), cuts allowed, and the shot lists in `references/motion-references.md`. After the owner picks, record in `references/learnings.md` how many picks came from each arm. Once two issues agree, fold the winner into `worker.md` and drop the experiment from this step.

Then run `python3 $SK/scripts/briefs.py <N>`. It renders one brief per direction from `briefs/worker.md` into `$R/briefs/<N>-<n><a-e>.md`; the `0-issue` briefs get `briefs/issue-reel.md` appended and the 1920x1080 format. Existing briefs are kept unless you pass `--force` (`--only 3a` renders one).

Expand All @@ -28,11 +32,13 @@ Explain the launch to the owner (how many workers, what each builds) and wait fo

About a minute after the launch, check that the processes are alive and the logs exist before you report anything as running.

Write `$R/music-variants.txt` (step 6) before or right after the launch, then run `bash $SK/scripts/stock_music.sh <N>` in the background. It builds the m1 and m2 stock variants for each reel the moment its page lands, and exits when the launcher does, so no reel ever reaches the gallery with only its score.

`launch.sh` and `resume.sh` were only exercised with a stand-in command when the skill was written (the 409 workers ran from the prototype scripts). If either misbehaves on a real run, fix the script, note what broke in `references/learnings.md`, and carry on.

## 4. Gallery and picks

1. Run `python3 $SK/scripts/gallery.py <N>` in the background and open `http://127.0.0.1:8767/gallery.html` in the owner's browser. Always open review URLs in the browser for the owner; never only print them.
1. Check nothing else holds port 8767 first (`lsof -nP -iTCP:8767 -sTCP:LISTEN`): a previous issue's gallery server left running serves the old issue silently. Run `python3 $SK/scripts/gallery.py <N>` in the background and open `http://127.0.0.1:8767/gallery.html` in the owner's browser. Always open review URLs in the browser for the owner; never only print them.
2. **Gate:** the owner plays the reels and picks one per topic. Picks land in `$R/selections.json`.

## 5. Revision rounds
Expand All @@ -46,13 +52,13 @@ Before you resume, explain to the owner which reels you will resume and with wha
## 6. Music

1. Stock tracks live in the main checkout's `.music/pixabay/` (gitignored). The owner downloads them; you never download music yourself. Before the run, tell the owner which moods the library is thin on, and what fits best: 1-3 minute instrumental tracks at about 118-130 bpm that hit full energy early, across a few moods (dark cinematic, upbeat house, minimal techno, playful), from the Pixabay Music licence. New tracks get analysed with `python3 $SK/scripts/analyse_tracks.py` (it skips the ones already in `analysis.json`).
2. Pick two tracks per picked reel by tempo and mood (the reel's `work/<slug>/beatmap.md` names its sound), and write them to `$R/music-variants.txt`.
2. Pick two tracks for every reel, all 30, by tempo and mood (the topic's mood and the reel's `work/<slug>/beatmap.md`), spreading the library so the set does not repeat one sound, and write them to `$R/music-variants.txt` as `<slug> <m1|m2> <track file>` lines. Avoid the previous issue's picks for the issue reel.
3. `python3 $SK/scripts/swap_music.py <N> <slug> <m1|m2> <track>` builds `<slug>-<m>`: the track from its first full-energy beat, cut to exactly the film's length and faded out over the last 2.5 s, the sound effects kept. A track longer than the film makes the player hold the last frame while the music plays on. `resume.sh --music` rebuilds these after every round.
4. **Gate:** the owner chooses score, m1 or m2 per reel in the gallery.
4. **Gate:** the owner chooses m1 or m2 per reel in the gallery (m1 plays by default). There is no score option anywhere: `stock_music.sh` has already built both variants for every reel, and `export.py` defaults to m1 and refuses `score`.

## 7. Export and hand over

Only when the owner says the set is final, run `python3 $SK/scripts/export.py <N>`. It exports each pick with its chosen audio, sequentially, and copies it to `tmp/issue-<N>/posts/<n>-<slug>/reel.mp4` next to the post text Buffer takes (the issue reel to `posts/0-issue/`). It also copies every reel, named by topic, to `~/Desktop/newsletter-<N>-reels/`, which is where the owner uploads from. Look at one frame of each mp4, then `open` that Desktop folder for the owner.
Only when the owner says the set is final, run `python3 $SK/scripts/export.py <N>`. It exports each pick with its chosen audio, sequentially, and copies it to `tmp/issue-<N>/posts/<n>-<slug>/reel.mp4` next to the post text Buffer takes (the issue reel to `posts/0-issue/`). It also copies every reel, named by topic, to `~/Desktop/newsletter-<N>-reels/`, which is where the owner uploads from. Buffer fetches the post copy through jsDelivr, which refuses anything over 20 MB, and the exports run 25-30 MB: re-encode each `posts/<n>-<slug>/reel.mp4` to about 18 MB (`ffmpeg -c:v libx264 -preset slow -b:v 5000k -c:a aac -b:a 160k -movflags +faststart`), keeping the full-quality Desktop copy for direct uploads. `buffer-post.mjs` refuses a file over the limit. Look at one frame of each mp4, then `open` that Desktop folder for the owner.

Nothing from this skill is committed: `tmp/` and `.music/` are gitignored, and the logos and tracks are other people's work borrowed for the post.

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/newsletter-reels/briefs/issue-reel.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

## This reel is different: it promotes the whole issue
- Format 1920x1080 (16:9), 30 fps: `python3 reel.py init {{SLUG}} --format 1920x1080`. It posts as LinkedIn's landscape feed video, so use the width: visual on one side, headline and logo on the other, like a keynote title card.
- It is the newsletter's own reel: issue #{{N}} and its five stories, then the call to action. 25-30 s, ~3-4 s per story (headline + its real logo + at most one fact from facts.md), paced for reading.
- It is the newsletter's own reel: issue #{{N}} and its five stories, then the call to action. 25-30 s, ~3-4 s per story (headline + its real logo + at most one fact from facts.md). The 409 issue reel with this skeleton reached ~25x the earlier issue posts, so keep it: each story's headline and logo stay readable, while the motion inside each story follows the Motion-first section (stamps on the beat, living detail, camera moves).
- Frame 0 (thumbnail, read at feed size): the Institute logo (horizontal lockup from assets/logos) + "ML Engineering Newsletter #{{N}}" + a hook like "5 stories this week", fully formed at t=0.
- The story logos are in assets/logos too. Use them as SVG vectors and transform them through their own anatomy, one into the next; no particle dissolves. Every logo must be exact at rest (render it from its SVG path; the 409 Meta logo broke when it was redrawn by hand).
- End card: the Institute logo, the subscriber call to action from post.txt, "ethical.institute/newsletter". The final motion settles before the last frame; nothing is left half-finished.
9 changes: 9 additions & 0 deletions .claude/skills/newsletter-reels/briefs/motion-max.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

## Motion-max (experiment arm: this reel goes further than Motion-first)
This reel is in the experiment arm that tests whether even more motion wins. Read `{{SKILL}}/references/motion-references.md` before you write the beat map: those shot lists are the bar, so your film should feel like them, not like a faster explainer.
- **At least 12 distinct visual states in 25-30 s.** A state is a new composition, not the same layout with new text. List them with timestamps in `beatmap.md`.
- **At least 3 moving layers on every frame:** a foreground subject, a living mid layer (field, grid, particles, readouts) and a background or camera move. Nothing on screen is ever fully still, the end card included (a slow drift or a breathing light is enough there).
- **A camera move in every scene:** dolly, orbit, push-through, rack focus or a parallax pan.
- **Use at least 3 kinds of transition:** a hard cut on the beat, a match cut (a shape in one shot becomes a shape in the next), a masked wipe, a whip pan with motion blur, or a scale-through (the camera flies into an object to reach the next scene).
- **craft.md's "nothing cuts" rule does not apply to you.** Hard cuts on the beat are part of the vocabulary. Keep its carries for the moments where a match cut is the point.
- In REPORT.md, give your counts of states, layers and transition kinds, so the owner's pick can be compared against them.
27 changes: 22 additions & 5 deletions .claude/skills/newsletter-reels/briefs/worker.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
To make a motion graphics video that shows what an incredible motion designer you are, like it's your showreel for a global expo keynote. Go all out.

You are ONE of {{COUNT}} parallel workers. Each is building one short social reel (LinkedIn/X feed, 1080x1350 portrait, 30 fps, 20-30 s, with composed music and sound effects) for one article in issue {{N}} of the ML Engineering newsletter from The Institute for Ethical AI Alignment & Safety (ethical.institute). The owner will see five reels per article, each in a radically different direction, and pick one. Earlier batches were judged "pretty bad" when they were generic, timid or too fast to read. A safe, template-looking result is the failure case. Yours must be polished and must show off a real capability.
You are ONE of {{COUNT}} parallel workers. Each is building one short social reel (LinkedIn/X feed, 1080x1350 portrait, 30 fps, 20-30 s, with sound effects; the music is one of the owner's stock tracks, swapped in after you build) for one article in issue {{N}} of the ML Engineering newsletter from The Institute for Ethical AI Alignment & Safety (ethical.institute). The owner will see five reels per article, each in a radically different direction, and pick one. Earlier batches were judged "pretty bad" when they were generic, timid or too fast to read. A safe, template-looking result is the failure case. Yours must be polished and must show off a real capability.

## Your assignment
- Slug: {{SLUG}}
Expand All @@ -10,21 +10,38 @@ You are ONE of {{COUNT}} parallel workers. Each is building one short social ree
{{DIRECTION}}

## How to work
1. Read in full, in this order: the skill at {{ENGINE}}/ - SOCIAL.md first (it is your operating mode and overrides the keynote defaults), then ENGINE.md, craft.md, prompts.md, music.md, template/API.md.
1. Read in full, in this order: the skill at {{ENGINE}}/ - SOCIAL.md first, then ENGINE.md, craft.md, prompts.md, music.md, template/API.md.
2. Run the pipeline exactly as SOCIAL.md says, from the engine dir, with:
export REEL_HOME={{REELS}}/work
export REEL_OUT={{REELS}}/out
and `python3 reel.py init {{SLUG}} --format 1080x1350`, run from {{ENGINE}}. There is no user sign-off: write beatmap.md and proceed.
3. Compose your own score.py for this direction (genre, tempo and mood that suit it; not the example score).
4. At least 3 review rounds on the check sheets (look at them with Read), with the SOCIAL.md checklist and the prompts.md feedback lens. Every headline held >= 1.5-2 s, key numbers 2-3 s. Captions on for sound-off viewing. No text clipping, overlaps or tiny type at phone size.
3. Compose a score.py for this direction at 118-130 bpm, but only as the beat grid your cuts and sound effects lock to. The owner never hears it: every reel ships with one of the owner's stock tracks, cut from its first full-energy beat to your film's length, so land your hits on a steady 4/4 grid and keep the sound effects carrying the accents.
4. At least 3 review rounds on the check sheets (look at them with Read), with the SOCIAL.md checklist and the prompts.md feedback lens, read through the Motion-first section. Every word on screen at least 0.7 s and readable on a phone; no text clipping, overlaps or tiny type.
5. `build` only. Do NOT export: the owner reviews the HTML page, and the mp4 is exported once at the end.

## Frame 0 and the brand
- Frame 0 is the feed thumbnail. At t=0 a viewer instantly sees the brand (the real logo) and the topic in big punchy type, fully formed, held ~0.8 s. No generic labels ("SCHEMATIC") on it.
- Frame 0 is the feed thumbnail. At t=0 a viewer instantly sees the brand (the real logo) and the topic in big punchy type, fully formed; it may start moving after 0.3 s. No generic labels ("SCHEMATIC") on it.
- The logos in assets/logos/ are SVG: embed their path data as Path2D vectors in film.js, never as bitmaps, and keep them crisp and correctly proportioned at rest.
- The logo transforms at least 3 times (out of the thumbnail, on the drop, back on the end card) through its OWN anatomy: its real pieces move as solid parts, its outline deforms with morphPath, strokes draw and undraw, it flips or extrudes in 3D. No particle dissolves of the logo (at most one small particle accent in the film). The reference the owner loved: Mistral's pixel-M blocks flying into the finish-line squares and the 50.4 digits, then re-stacking into the M.
- End card: the brand mark next to the "ethical.institute" wordmark.

## Motion-first
This section wins over SOCIAL.md wherever the two disagree.
The calm explainer is the failure case: 5-7 scenes of 4 s, a held headline, a caption, a number, an end card. Every reel in the first runs came out like that, all alike. The references the owner loves are motion-design showreels that change every 1-2 s, fill every frame with living detail and feel like a music video, and on issue 410 he picked the motion-first reel over the calm one in four of five topics. Think "what would make a motion designer stop scrolling", not "how do I explain the article".
- **Pace:** a new visual idea every 1-2 s (half a bar to a bar at 120-128 bpm). Hard cuts on the beat are allowed and encouraged, mixed with continuous carries. A `cuts` or `holds` WARN from `check` may stand when the cut or hold is a deliberate beat: list it in REPORT.md with the reason. FAILs still get fixed.
- **Story:** one claim and at most two numbers from the article, carried by the style. No five-scene fact tour.
- **Captions:** optional; use one only where a word is the beat.
- **Decorative motion is wanted:** layered living detail (HUD furniture, fields, particles, grids, ticking readouts, light sweeps, parallax planes) is what makes these feel rich. SOCIAL.md's "no decorative motion" rule does not apply. Keep text legible over it with contrast plates, dimming or depth of field.
- **Type:** free. Giant heavy display words, mono micro-labels, words wrapped around shapes, letters resolving one by one, inverted words, mixed scale in one line.
- **Camera:** move it. Dolly, orbit, push through layers, whip pans with motion blur (blend 2-4 offset frames), rack focus.

Your direction names a primary style; borrow from the others freely:
1. **Kinetic type / lyric video:** one giant word stamped per beat with scale overshoot, small words typing in beside it, words wrapping a ring or path, inverted highlight words, a terminal prompt typing a line with a block cursor.
2. **HUD / technical:** crop marks, hairline grids, a mono code or coordinates column, ticking timecodes, odometer digits (clipped digit columns easing in, faint vertical blur copies), callout boxes on leader lines growing out of an object, dimension arrows, line art drawn stroke by stroke with a glowing comet head (lineDashOffset on Path2D, additive dot, shadowBlur bloom).
3. **3D camera fly-through:** the subject as a three.js set (tilted UI panels, architecture, a chip, a corridor of layers) on a continuous camera spline with depth of field and whip pans; flat UI extruded into 3D slabs.
4. **Generative / organic:** noise-displaced contour-ring fields, flow fields, branching networks, swarms, with a glowing path cutting through and drifting on the beat.
5. **Device in scene:** the film playing on a screen inside a dark three.js scene (a laptop or phone on a glossy black table, lit only by its screen, with a mirror reflection and a slow camera move), cutting between full screen and the device.

## Rules
- Facts only from post.txt and facts*.md; copy figures exactly; never invent numbers, names or claims. Illustrative visuals that are not data get labelled as such.
- On-screen text ASCII punctuation only (straight quotes, "-", "...").
Expand Down
Loading
Loading