diff --git a/.claude/skills/t27f/SKILL.md b/.claude/skills/t27f/SKILL.md index f3862bda01..f52737b3b6 100644 --- a/.claude/skills/t27f/SKILL.md +++ b/.claude/skills/t27f/SKILL.md @@ -194,6 +194,29 @@ approved)`. Keep drafts, verified results and published items under separate headings (`layer_of`). If the journey is saved, save it as a `.t27` data module, not JSON. Saving follows the user's instruction. +## Application: content factory, beats (t27#10584) + +`specs/t27f/reel_beats.t27` plans every reel of course 0 as three beats: open, main and close. +Main is `arche(t)` from `reel_factory.t27`. Open is the best other construct, with a generic +fallback. Close is a rotating generic. No two reels share the ordered pair (open, main), and +neighbouring reels differ in every beat. + +Before you change a uniqueness rule, measure what it can draw from: + +- **Count the pool before you promise uniqueness.** 21 of 27 course-0 lessons have content only + for graph and tests among the constructs. This is measured, not a detection bug: course 0's + snippets have no `while` or `if`. Six archetypes make only 15 unordered pairs for 27 reels, + so an unordered rule fails 4 of 7 tests. Ordered pairs (30) hold. Print the content matrix + (lesson by archetype `content()`) first. +- **A generated printer needs a test block.** `t27c gen` emits `const std = @import("std")` + only when the module has a test. Without one, `std.debug.print` fails in zig with "use of + undeclared identifier 'std'". Give a lab-only printer one trivial `test`. +- **Keep scratch printers on the lab.** Copy them into `specs/` for `t27c gen`, then `rm` them + in the same command. Never commit them. +- **Write PR bodies with the Write tool.** The Mac hook matches words in a + shell heredoc, such as a PR body that mentions tests. Write the body with the Write tool, then + pass `--body-file`. + ## What this skill does not claim The graph IR is a schema (`specs/t27f/spec_graph.t27`) and `specs/t27f/spec_graph_export.t27` turns source into it, but only exact declarations, spans and edges within the IR's capacity, with `specs/t27f/spec_graph_derive.t27` adding the derived and unknown part by stated rules, and no host calls either yet; no renderer draws a T27F view in the Trinity UI, no diff --git a/.trinity/seals/t27f_reel_beats.json b/.trinity/seals/t27f_reel_beats.json new file mode 100644 index 0000000000..8d003251d3 --- /dev/null +++ b/.trinity/seals/t27f_reel_beats.json @@ -0,0 +1,37 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+9448ab4be", + "closure": [ + { + "spec": "specs/t27f/reel_factory.t27", + "spec_hash": "sha256:96099d764ba86fa3ef2c29d81c3838ce255bfd3b3abbd37b27cb86e4bca91012" + }, + { + "spec": "specs/t27f/reel_topics.t27", + "spec_hash": "sha256:7c1762cde96524791711594d23f880fbad1f497a499277710f1fd61d52d50157" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:d83963d6f15f810d29f87edeb1c98d40928612cc2fcc48cedcf3e9557df14e7e", + "gen_hash_rust": "sha256:041b2748f1e1199063b73fd00970dcc6db10602ce7f1ad934d879d86c0e13920", + "gen_hash_verilog": "sha256:4869ae2493cdc3f9f8c82dca42898a8a2366aef9a61ad190567626b8ee2e1fe3", + "gen_hash_zig": "sha256:d96bd594710b380e505389559632939ec5500b21b593bdd40d1fd3b7525e4d51", + "module": "reel_beats", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T18:01:36Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:5b30be8b6d549ec9c0fee0132877f143cd3af9d04e51568eaa51f2ac50a30d0c", + "spec_path": "specs/t27f/reel_beats.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 7, + "total": 7, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/docs/reports/t27b_expectations.json b/docs/reports/t27b_expectations.json index 6e4b977d5f..01e7d890f5 100644 --- a/docs/reports/t27b_expectations.json +++ b/docs/reports/t27b_expectations.json @@ -1447,6 +1447,7 @@ {"path": "specs/t27f/pbr_sphere.t27", "t27b": "pass"}, {"path": "specs/t27f/quest.t27", "t27b": "pass"}, {"path": "specs/t27f/reel.t27", "t27b": "pass"}, + {"path": "specs/t27f/reel_beats.t27", "t27b": "pass"}, {"path": "specs/t27f/reel_factory.t27", "t27b": "pass"}, {"path": "specs/t27f/reel_gf16_nmse.t27", "t27b": "pass"}, {"path": "specs/t27f/reel_marketing.t27", "t27b": "pass"}, diff --git a/specs/t27f/reel_beats.t27 b/specs/t27f/reel_beats.t27 new file mode 100644 index 0000000000..08f0dc7450 --- /dev/null +++ b/specs/t27f/reel_beats.t27 @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: Apache-2.0 +// specs/t27f/reel_beats.t27 -- every reel of a course as three beats, open, main and close, so +// that no two reels show the same scenes. Issue gHashTag/t27#10584, epic gHashTag/t27#10560. +// +// WHY: reel_factory.t27 gives each lesson one scene archetype, at most ARCH_CAP = 4 times a +// course, so up to four reels share their only scene and differ in accent, motion and layout +// alone. Its own NOT CLAIMED block says the signature test is weak by itself. Tools that fill one +// fixed structure with an item's text produce exactly the sameness the owner saw; the ones that +// look varied let the data choose the scene list (Motion Canvas and Revideo take a list of scenes +// per project). Here the data chooses three. +// +// THE BEATS, planned for the whole course at once, in course order: +// main the factory's scene, arche(t). Nothing here changes it. +// open the scene of the item's next construct: of the archetypes the item has content for, +// other than main, the best by reel_factory's term-frequency score, where any construct +// ranks above any generic scene. It may not repeat the previous reel's open, and the ordered +// pair (open, main) may not be one an earlier reel already has. +// close a generic scene (code, title, task or map), rotating from t mod GENERICS, other than +// main and open, and not the previous reel's close. +// A beat the plan cannot place reads ARCHS, which the tests refuse. +// +// The tests hold over every lesson of course 0: every beat placed; three distinct scenes a reel; +// no two reels open the same way into the same main, so no two show the same sequence; neighbours +// differ in every position; every beat shows content its item has, and every close is generic. +// +// NOT CLAIMED: +// - Not that an unordered pair of scenes is unique. Measured on course 0: 21 of 27 lessons have +// content for graph and tests alone among the constructs, so their scenes come from six archetypes, +// which make 15 unordered pairs for 27 reels. Ordered pairs (open, main) number 30, and the rule +// holds on those. For the same reason open falls back to a generic scene when main took the only +// construct the item has left, or the pair rule refused it. +// - The plan arrays are sized for course 0 (COURSE = 27, checked against TOPICS by a test). A +// course of another length needs the size changed with it. +// - Beat lengths in frames, and the transitions between beats, are not here. The renderers are R3 +// (#10571) and the reel that reads this plan is R4. +// - "Shows content the item has" is the factory's content() count being positive. It does not say +// the open is interesting; a lesson whose only second construct is one test block opens on it. +// phi^2 + 1/phi^2 = 3 | TRINITY + +module reel_beats; + +use t27f::reel_topics::TOPICS; +use t27f::reel_factory::A_CODE; +use t27f::reel_factory::A_TITLE; +use t27f::reel_factory::A_TASK; +use t27f::reel_factory::A_MAP; +use t27f::reel_factory::ARCHS; +use t27f::reel_factory::generic; +use t27f::reel_factory::content; +use t27f::reel_factory::course_content; +use t27f::reel_factory::score_with; +use t27f::reel_factory::arche; + +pub const COURSE : usize = 27; +pub const BEATS : usize = 3; +pub const GENERICS : usize = 4; +// The generic archetypes in close rotation order: code, title, task, map. +pub const GENERIC_ARCH : [4]usize = [0, 9, 10, 11]; + +// ---- the pair rule ---- + +// Whether a reel before t already opens on a into the main m. +pub fn pair_used(mn: [27]usize, op: [27]usize, t: usize, m: usize, a: usize) -> bool { + var s : usize = 0; + while (s < t) { + if (mn[s] == m && op[s] == a) { + return true; + } + s = s + 1; + } + return false; +} + +// How open ranks archetype a for lesson t: 0 when a is generic, otherwise one more than its +// factory score, so that any construct beats any generic scene. +pub fn open_rank(t: usize, a: usize, total: u32) -> u32 { + if (generic(a)) { + return 0; + } + return score_with(t, a, total) + 1; +} + +// ---- plan ---- + +// The beats of every lesson: op the open, mn the main, cl the close. +pub fn plan_beats(op: *[27]usize, mn: *[27]usize, cl: *[27]usize) -> void { + var total : [12]u32 = undefined; + var a : usize = 0; + while (a < ARCHS) { + total[a] = course_content(a); + a = a + 1; + } + var t : usize = 0; + while (t < COURSE) { + mn[t] = arche(t); + op[t] = ARCHS; + cl[t] = ARCHS; + t = t + 1; + } + t = 0; + while (t < COURSE) { + var best : usize = ARCHS; + var best_rank : u32 = 0; + a = 0; + while (a < ARCHS) { + var ok = a != mn[t] && content(t, a) > 0; + if (ok && t > 0 && a == op[t - 1]) { + ok = false; + } + if (ok && pair_used(mn.*, op.*, t, mn[t], a)) { + ok = false; + } + if (ok) { + const r = open_rank(t, a, total[a]); + if (best == ARCHS || r > best_rank) { + best = a; + best_rank = r; + } + } + a = a + 1; + } + op[t] = best; + var k : usize = 0; + while (k < GENERICS && cl[t] == ARCHS) { + const g = GENERIC_ARCH[(t + k) % GENERICS]; + var free = g != mn[t] && g != op[t] && content(t, g) > 0; + if (free && t > 0 && g == cl[t - 1]) { + free = false; + } + if (free) { + cl[t] = g; + } + k = k + 1; + } + t = t + 1; + } +} + +// ---- tests ---- + +test "the plan is sized for this course and its generic list is the factory's" { + assert(COURSE == TOPICS); + assert(GENERIC_ARCH[0] == A_CODE); + assert(GENERIC_ARCH[1] == A_TITLE); + assert(GENERIC_ARCH[2] == A_TASK); + assert(GENERIC_ARCH[3] == A_MAP); + var k : usize = 0; + while (k < GENERICS) { + assert(generic(GENERIC_ARCH[k])); + k = k + 1; + } +} + +test "the pair rule sees an earlier pair in its order and nothing at or after t" { + var mn : [27]usize = undefined; + var op : [27]usize = undefined; + var t : usize = 0; + while (t < 27) { + mn[t] = 0; + op[t] = 9; + t = t + 1; + } + mn[3] = 4; + op[3] = 2; + assert(pair_used(mn, op, 4, 4, 2)); + assert(pair_used(mn, op, 4, 2, 4) == false); + assert(pair_used(mn, op, 3, 4, 2) == false); + assert(pair_used(mn, op, 27, 4, 5) == false); + assert(open_rank(0, A_TITLE, 5) == 0); +} + +test "every beat of every reel is placed" { + var op : [27]usize = undefined; + var mn : [27]usize = undefined; + var cl : [27]usize = undefined; + plan_beats(&op, &mn, &cl); + var t : usize = 0; + while (t < COURSE) { + assert(op[t] < ARCHS); + assert(mn[t] < ARCHS); + assert(cl[t] < ARCHS); + t = t + 1; + } +} + +test "every reel shows three different scenes" { + var op : [27]usize = undefined; + var mn : [27]usize = undefined; + var cl : [27]usize = undefined; + plan_beats(&op, &mn, &cl); + var t : usize = 0; + while (t < COURSE) { + assert(op[t] != mn[t]); + assert(cl[t] != mn[t]); + assert(cl[t] != op[t]); + t = t + 1; + } +} + +test "no two reels open the same way into the same main, so no two show the same sequence" { + var op : [27]usize = undefined; + var mn : [27]usize = undefined; + var cl : [27]usize = undefined; + plan_beats(&op, &mn, &cl); + var s : usize = 0; + while (s < COURSE) { + var t : usize = s + 1; + while (t < COURSE) { + assert((mn[s] == mn[t] && op[s] == op[t]) == false); + assert((op[s] == op[t] && mn[s] == mn[t] && cl[s] == cl[t]) == false); + t = t + 1; + } + s = s + 1; + } +} + +test "two neighbouring reels differ in every beat" { + var op : [27]usize = undefined; + var mn : [27]usize = undefined; + var cl : [27]usize = undefined; + plan_beats(&op, &mn, &cl); + var t : usize = 1; + while (t < COURSE) { + assert(op[t] != op[t - 1]); + assert(mn[t] != mn[t - 1]); + assert(cl[t] != cl[t - 1]); + t = t + 1; + } +} + +test "every beat shows content its item has, and every close is generic" { + var op : [27]usize = undefined; + var mn : [27]usize = undefined; + var cl : [27]usize = undefined; + plan_beats(&op, &mn, &cl); + var t : usize = 0; + while (t < COURSE) { + assert(content(t, op[t]) > 0); + assert(content(t, mn[t]) > 0); + assert(content(t, cl[t]) > 0); + assert(generic(cl[t])); + t = t + 1; + } +}