Skip to content
40 changes: 40 additions & 0 deletions .claude/skills/t27f/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,46 @@ the reels. Describing T27F as #1677 is the error this section exists to stop (ow
When asked "how is T27F going", report against the core rows first. Progress on the applications
is not progress on the core.

**Trap: a name declared in two specs cannot be imported (t27#10556).** Measured on t27c 0.5.2:
`use t27f::lesson_reel::phrase_start` returns `UNRESOLVED ... ambiguous, not spliced`, because
`token_floor_mux.t27` declares a private `fn phrase_start`. The same happens to `cta_from` and
`voice_fits`. The explicit path does not help. Before you plan a new module on top of a reel spec,
grep `specs/` for every name it would import. If any of them collides, put the new code inside the
spec that owns those names, under a prefix such as `beat_`. The beat clock (t27#10596, PR #10598)
lives in `lesson_reel.t27` for this reason.

**Cutting video at a ms time: land on a frame, then measure the runs (t27#10603, PR #10605).**

ffmpeg `xfade` starts on the first frame whose time is at or after `offset`. This is how to get the cut you planned:

- Floor the cut to a base frame: `f = (c - half) * fps / 1000`.
- Print the offset as `trunc(f * 1000 / fps)` ms. Rounding up misses frame f on 2 frames in 3.
- Put every input through `fps=30,settb=AVTB` so the offset is not rounded again.
- Chained `xfade` offsets are absolute reel times when each clip starts at its own frame 0.

To check a graph without looking at pictures:

1. Feed it solid `color=` inputs at `s=2x2`.
2. Write `-f rawvideo -pix_fmt rgb24`.
3. Count the runs with `od -An -tu1 -w12 -v | cut -c1-12 | uniq -c`.

The runs are the scene lengths and must equal the spec's frame counts. Expect one extra frame when the offset falls on a whole ms.

Lesson main functions read `std.c.getenv`, so build them on the lab with `zig build-exe -lc`.

**Adding a mode to a graph printer without moving the old ones (t27#10609, PR #10610).**

- Pass the old callers a flag (`graph(..., false)`) instead of copying the printer.
- Give the input numbers a function with a test that every input is used exactly once, in each layout.
- Prove the old output did not move: on the lab, build the old spec (from `git show <ref>:path`) and the new one, run both on a grid of lessons, languages and voices, and `cmp -s` every graph. PR #10610 compared 96 graphs and 0 differed.
- For a large frame, render lossless (`-c:v libx264 -qp 0`) and crop a 2x2 pixel outside every text box (`crop=2:2:x:y,format=rgb24`). The solid runs must still equal the spec's frame counts.

Traps from this tick:

- An untyped array literal in a test (`var a = [_]usize{...}`) becomes a Zig tuple, which cannot be indexed at runtime ("tuple field index must be comptime-known"). Declare `var a : [8]usize = undefined;` and fill it in a loop.
- `fs-lab.sh "echo $b64a ... ; echo $b64b ..."` with two large files exits 0 and writes nothing. Upload one file per call and compare md5 on both sides.
- zsh does not word-split `set -- $p`. Write one call per file.

## Show a spec (the core: exporter for what it reads, by hand for the rest)

1. Load the spec: `mcp__t27__t27_spec` with `path` and `source: true`, or the file from a
Expand Down
28 changes: 28 additions & 0 deletions .trinity/seals/skills_skill_t27_reel_beat.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"built_by": "t27c-bootstrap@0.5.2+9448ab4be",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:5642bc71a3f58fb401c34d2e9260f2190b2335994b58272bfd47bdb2e50cca07",
"gen_hash_rust": "sha256:706879bc5d6542378435b0982dbb44e52cc1b9b85b34d7f06149ec7668b5fdd7",
"gen_hash_verilog": "sha256:596ced743ed9651f967ac93bae97d97f44b882ea6ed659d4ebc6ec67a16829b8",
"gen_hash_zig": "sha256:24befaf4ec44f81b268d7a1f58e3e7312c16da94c50b0442ac76e23e5ea1440a",
"module": "skill_t27_reel_beat",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T19:16:57Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:b609e2d632860b22d61a44f12e3d8e3bf11663d080dada9cec88c19ac9e5becd",
"spec_path": "specs/skills/t27-reel-beat.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 1,
"total": 1,
"vacuous": 0
},
"toolchain": {
"test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10",
"zig": "0.16.0"
}
}
20 changes: 12 additions & 8 deletions .trinity/seals/t27f_lesson_reel.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
"spec": "specs/automation/owner_voice_reel.t27",
"spec_hash": "sha256:76c0df8e57686a90b3bce85bcb0148cbed7fd05da724b217ef53fa468664f151"
},
{
"spec": "specs/t27f/reel.t27",
"spec_hash": "sha256:c47b21cea1d7169da6528e321cf2d2c513d3f552c0cd04fe9d37bbd5314ccacf"
},
{
"spec": "specs/t27f/reel_marketing.t27",
"spec_hash": "sha256:ab07f6476d900cc0e089ba1d851f0b610a77b778f00fd906d1b98a29281941c7"
Expand All @@ -27,23 +31,23 @@
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:74d5f5d8fad98ead782f6c26309ed349da58471650642503a13a8e32cb7427ae",
"gen_hash_rust": "sha256:ccc5014bd6bf09ba55b21e4d5bd5fb80383c36cce4d83468812bdd4fadd04835",
"gen_hash_verilog": "sha256:f956226ada7459fc5d856060971f9160ab41ee78b1a5dbf713ad8531e3d41abd",
"gen_hash_zig": "sha256:42635d596d160c7c49521929d8e4f8f640f89a3180b5e1ac118c0177c4d14545",
"gen_hash_c": "sha256:6cf89f561d2471d4ea92d3f00e18aac2a2587b2788098e8d7e552ec83de63dbe",
"gen_hash_rust": "sha256:f6920ee461def04b74ff3634e89c24b41717f1ad44b8dfdc94a16f883ca9d5a3",
"gen_hash_verilog": "sha256:2694b8d7796169dfba074e585072006e53d2230160d1fba51094c4819a653e8c",
"gen_hash_zig": "sha256:b1479ef2a482771dd7f65dbc217a6e92afe1e49ed06a10b009329a8ec6265d6d",
"module": "lesson_reel",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T15:14:32Z",
"sealed_at": "2026-10-18T18:59:43Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:0c527e6415d5ffe600cd425db2456e1691e47bcaaf69cf5af0b856bb8bfec66e",
"spec_hash": "sha256:714fad38cc4f8d4a1d31544730ce7a5678eab133c825ecd89fdaa5352134c612",
"spec_path": "specs/t27f/lesson_reel.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 6,
"total": 6,
"passed": 19,
"total": 19,
"vacuous": 0
},
"toolchain": {
Expand Down
1 change: 1 addition & 0 deletions docs/reports/t27b_expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1406,6 +1406,7 @@
{"path": "specs/skills/t27-measure-corpus.t27", "t27b": "pass"},
{"path": "specs/skills/t27-phi-loop.t27", "t27b": "pass"},
{"path": "specs/skills/t27-providers-steward.t27", "t27b": "pass"},
{"path": "specs/skills/t27-reel-beat.t27", "t27b": "pass"},
{"path": "specs/skills/t27-self-host.t27", "t27b": "pass"},
{"path": "specs/skills/t27-t27f.t27", "t27b": "pass"},
{"path": "specs/skills/t27-tri-pipeline.t27", "t27b": "pass"},
Expand Down
48 changes: 48 additions & 0 deletions specs/skills/t27-reel-beat.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
; specs/skills/t27-reel-beat.t27 -- skill t27/reel-beat
; Source of truth for the skill card on t27.ai (#/skills?skill=t27/reel-beat).
; How TRI CLAW cuts a lesson reel on the beat: LR_MODE=4 crossfades the open, main and close
; scene clips at the beat clock's cuts, on whole frames, and LR_MODE=5 lays mode 3's captions,
; cards and address over that video. Mode 5's inputs are 0..2 the scene clips, 3..5 the
; phrases' wavs and 6 the music. The law lives once, in specs/t27f/lesson_reel.t27 (beat_*
; functions and their tests); this card points at it and copies none. The scene clips are
; inputs: lesson_reel does not read the content factory's scene plan yet. There is no
; SKILL.md behind it yet, so the card is spec-only (see "Witness labels" in
; specs/skills/README.md). Issues gHashTag/t27#10603 and
; gHashTag/t27#10609, epic gHashTag/t27#10560.
; The JSON the site serves is generated from this file by the real compiler;
; nothing below is parsed with a regex.
; phi^2 + 1/phi^2 = 3 | TRINITY

module skill_t27_reel_beat;

pub const KIND : str = "skill";
pub const ID : str = "t27/reel-beat";
pub const NAME : str = "Reel Beat";
pub const REPO : str = "t27";
; No SKILL.md exists for this card.
pub const SOURCE : str = "";
pub const SUMMARY_EN : str = "Cut a 15 second lesson reel on the beat clock: crossfade the open, main and close scenes at whole-frame cuts that never fall inside a spoken phrase, lay the captions and address on top, and refuse a voice that does not fit";
pub const COMMAND : str = "/reel-beat";
; Spec this skill stands on: the beat clock and the graph printer.
pub const SPECS : [1]str = ["specs/t27f/lesson_reel.t27"];
pub const TAGS : [4]str = ["social-visual", "reel", "beat", "t27f"];
pub const ENABLED : bool = true;
; Default budget; no SKILL.md declares a timeout.
pub const TIMEOUT_MIN : u16 = 30;

test basic_constants {
assert KIND == "skill";
assert ID == "t27/reel-beat";
assert NAME == "Reel Beat";
assert REPO == "t27";
assert SOURCE == "";
assert COMMAND == "/reel-beat";
assert ENABLED == true;
assert TIMEOUT_MIN == 30;
assert TAGS.len == 4;
assert TAGS[0] == "social-visual";
assert TAGS[2] == "beat";
assert SPECS.len == 1;
assert SPECS[0] == "specs/t27f/lesson_reel.t27";
}
Loading
Loading