Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d21f483
t27f: idea factory -- any idea in any style preset, every element a p…
gHashTag Oct 19, 2026
2c619fd
t27f skill: idea factory and the particle design system, preset sweep…
gHashTag Oct 19, 2026
5e07615
t27f: Russian ideas in the factory -- UTF-8 and a 5x7 Latin and Cyril…
gHashTag Oct 19, 2026
ba365d1
Merge master into claude/t27f-cyrillic; list five t27b-pass specs in …
gHashTag Oct 19, 2026
9ec9e29
t27f skill: a new spec's t27b ledger row comes from the arm64 run, ke…
gHashTag Oct 19, 2026
8efa4bc
Merge branch 'master' of github.com:gHashTag/t27 into claude/t27f-cyr…
gHashTag Oct 19, 2026
dae06a9
Ledger: glyph57 and petal_menu pass the t27b front end (Refs #10788)
gHashTag Oct 19, 2026
29f8511
t27f: idea factory picks its style preset from a hash of the idea, PR…
gHashTag Oct 19, 2026
7f33764
Ledger rows for mos_psp103 and re-sealed frontend, both inherited fro…
gHashTag Oct 19, 2026
549fe2c
Merge branch 'claude/t27f-cyrillic' into claude/t27f-preset-hash
gHashTag Oct 19, 2026
879ff99
t27f: idea factory batch -- five ideas cover all five presets, and th…
gHashTag Oct 19, 2026
d5dc2c9
Merge remote-tracking branch 'origin/master' into claude/t27f-cyrillic
gHashTag Oct 19, 2026
4f739c1
Ledger row for sar_switch, another master spec landed without one (Re…
gHashTag Oct 19, 2026
4d5b412
Merge branch 'claude/t27f-cyrillic' into claude/t27f-preset-hash
gHashTag Oct 19, 2026
0a5dad1
Merge branch 'claude/t27f-preset-hash' into claude/t27f-batch
gHashTag Oct 19, 2026
e9b55d6
t27f: idea factory pre-flight -- refuse an idea the font cannot draw …
gHashTag Oct 19, 2026
f8c3822
Merge remote-tracking branch 'origin/master' into claude/t27f-cyrillic
gHashTag Oct 19, 2026
fe4fd88
Ledger: sar_switch sorts after sar_comparator (Refs #10788)
gHashTag Oct 19, 2026
2b94b35
Merge remote-tracking branch 'origin/claude/t27f-cyrillic' into claud…
gHashTag Oct 19, 2026
56d6d78
Merge branch 'claude/t27f-preset-hash' into claude/t27f-batch
gHashTag Oct 19, 2026
7312b77
Merge branch 'claude/t27f-batch' into claude/t27f-preflight
gHashTag Oct 19, 2026
e1d344e
Merge remote-tracking branch 'origin/master' into claude/t27f-cyrillic
gHashTag Oct 19, 2026
b1312de
Merge branch 'claude/t27f-cyrillic' into claude/t27f-preset-hash
gHashTag Oct 19, 2026
863d00e
Merge branch 'claude/t27f-preset-hash' into claude/t27f-batch
gHashTag Oct 19, 2026
5ea0c54
Merge branch 'claude/t27f-batch' into claude/t27f-preflight
gHashTag Oct 19, 2026
ea41efb
t27b ledger: master's rows plus this lane's glyph57 row, so the branc…
gHashTag Oct 19, 2026
b07f7bf
t27b ledger: master's rows plus this lane's glyph57 row, so the branc…
gHashTag Oct 19, 2026
ef673a2
Merge master into claude/t27f-batch after #10806 landed (Refs #10815)
gHashTag Oct 19, 2026
893c929
t27f: re-save the idea_factory seal on the lab after the master merge…
gHashTag Oct 19, 2026
c7daeb9
Merge claude/t27f-batch into claude/t27f-preflight after #10806 lande…
gHashTag Oct 19, 2026
ba3bd65
t27f: re-save the idea_factory seal on the lab after the merge, 17/17…
gHashTag Oct 19, 2026
737d2cb
Merge origin/master into claude/t27f-preflight (#10816 landed)
gHashTag Oct 19, 2026
18f9209
t27f: re-seal idea_factory after the merge with master (Refs #10820)
gHashTag Oct 19, 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
10 changes: 10 additions & 0 deletions .claude/skills/t27f/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,16 @@ card's `PRESETS`; its lab check compares that list to `PRESET_NAMES` byte for by
Five parallel libx264 encoders on the lab failed with "Error while opening encoder", the
pipe closed, and md5 hashed a truncated stream: 3 of 5 read DIFF. Run md5 separately, then
encode one at a time. A DIFF that coincides with an encoder error is the harness, not the code.
- **Run `preflight(IDEA)` before you render an idea** (gHashTag/t27#10820). It returns `PF_EMPTY`
(no scene), `PF_GLYPH` (a character glyph57 would draw as `?`, such as phi, a euro sign, `{`, `~`
or a malformed byte; `first_missing` gives its byte offset), `PF_LONG` (the reel would run past
`MAX_S` = 60 s) or `PF_OK`. `main` writes **0 bytes** for a refused idea, so an empty output file
means the idea was refused, not that the render broke. Rewrite the idea instead: `PHI^2`, not the
Greek letter. **Check the guard on the binary, with a control:** the same refused idea built with
the guard line `sed`-ed out must write frames (144 for `A{B}`), or the 0 bytes prove nothing.
**A mutant can survive and still be equivalent:** reels grow in 72-frame steps, so `MAX_S` from
60 to 62 refuses exactly the same ideas. Say that in the report, and do not add a test that only
restates the constant.

**Keep it FPGA-shaped.** Use Q8 fixed point. The per-step and per-pixel paths use only add,
multiply, compare, table lookup (`kt` kernel ROM, `tt` tone ROM) and power-of-two division.
Expand Down
16 changes: 8 additions & 8 deletions .trinity/seals/t27f_idea_factory.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:bb1ef6081add1e9fa249288e6ebd223bc66be7f13d91210eed6dac48ed4a01aa",
"gen_hash_rust": "sha256:2420620e8b22b844cf0912ec4a21d7358a0045cec3508d8a14c6d8206f169ccb",
"gen_hash_verilog": "sha256:8f8cd1625c7f3d516bc5e597c4860b81f2a4860ce6d6f3cb4b06c2578568a49d",
"gen_hash_zig": "sha256:b4136a42488a6715a58f8a2e2e482871209c94a0009a78cab7a1f357db42c0d2",
"gen_hash_c": "sha256:b4cb42ec55deec10b3180769d04e36217f39dc786c7756387880cf26bc7f3f15",
"gen_hash_rust": "sha256:7632796cc4ee12e7d310e8115091e72248074a2e25ff02e8773ea5776ec6b2d1",
"gen_hash_verilog": "sha256:a1e8068e1df97e43a750658c626f76e47f52b7d62f249bc598af057a78a9e35f",
"gen_hash_zig": "sha256:6b2f7675bb3198c6045686c29eac989768cb5d3e0b00b14795be854be56a8c9f",
"module": "idea_factory",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-19T07:20:44Z",
"sealed_at": "2026-10-19T07:36:57Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:a68fbc93ea94b32e264ae91b82b149eb7530c3af4e2787c8b457a2eeec0c3482",
"spec_hash": "sha256:44432d34553ebeb531b415544066d45f5928975c5a98fb95aa7bd69267430a5a",
"spec_path": "specs/t27f/idea_factory.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 14,
"total": 14,
"passed": 17,
"total": 17,
"vacuous": 0
},
"toolchain": {
Expand Down
97 changes: 97 additions & 0 deletions specs/t27f/idea_factory.t27
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@
// A RUSSIAN IDEA is a u8 array of UTF-8 bytes (L3 keeps sources ASCII, and t27c keeps "\xNN" as
// four characters): RU_IDEA below, passed as RU_IDEA[0..80].
//
// PRE-FLIGHT: main draws nothing for an idea preflight refuses -- empty, a character the font
// lacks, or a reel longer than MAX_S.
//
// NOT CLAIMED: capitals only (lower case folds); no voice or music (lesson_reel mode 5 has those);
// the Verilog is emitted, not synthesised, and no board has drawn it.
// phi^2 + 1/phi^2 = 3 | TRINITY

module idea_factory;

use t27f::glyph57::glyph57_on;
use t27f::glyph57::glyph57_of;
use t27f::glyph57::QMARK;
use t27f::glyph57::cp_len;
use t27f::glyph57::cp_at;
use t27f::glyph57::cp_count;
Expand Down Expand Up @@ -304,6 +309,48 @@ pub fn pick_preset(t: []const u8, p: usize) -> usize {
return p;
}

// ---- pre-flight: an idea the factory cannot draw is refused before a frame (gHashTag/t27#10820) ----

// glyph57 draws a character it does not have as '?', so without this check an idea with a phi or
// a euro sign ships with the wrong letter on screen, and nothing notices.
pub const PF_OK : i32 = 0;
pub const PF_EMPTY : i32 = 1; // no scene: nothing but spaces and cuts
pub const PF_GLYPH : i32 = 2; // a character the font draws as '?'
pub const PF_LONG : i32 = 3; // the reel would run past MAX_S
pub const MAX_S : i32 = 60; // the factory's own cap on a reel, seconds

// The characters of t that the font draws as '?': what 0 = how many, 1 = the byte offset of the
// first (-1 when there is none). A real '?', a '|' cut and a malformed byte are decided by code
// point: '?' and '|' do not count, BAD does.
pub fn missing_walk(t: []const u8, what: i32) -> i32 {
const n = @as(i32, @intCast(t.len));
var i : i32 = 0;
var m : i32 = 0;
while (i < n) {
const c = cp_at(t, i);
if (c != 63 && c != 124 && glyph57_of(c) == QMARK) {
if (what == 1) { return i; }
m = m + 1;
}
i = i + cp_len(t, i);
}
if (what == 1) { return 0 - 1; }
return m;
}
pub fn missing_glyphs(t: []const u8) -> i32 {
return missing_walk(t, 0);
}
pub fn first_missing(t: []const u8) -> i32 {
return missing_walk(t, 1);
}
// Why the factory would refuse idea t, checked in this order; PF_OK when it would draw it.
pub fn preflight(t: []const u8) -> i32 {
if (scene_count(t) == 0) { return PF_EMPTY; }
if (missing_glyphs(t) > 0) { return PF_GLYPH; }
if (reel_frames(t) > MAX_S * FPS) { return PF_LONG; }
return PF_OK;
}

// ---- assign ----

// Where card particle m of mm sits on the border of the rectangle (x0, y0)-(x1, y1): what 0 = x,
Expand Down Expand Up @@ -361,6 +408,7 @@ pub fn main() -> void {
var accb : [1080]i32 = undefined;
var row : [3240]u8 = undefined;

if (preflight(IDEA) != PF_OK) { return; } // refused: not one frame, so no reel ships wrong
const p = pick_preset(IDEA, PRESET);
const rr = reach(p);
var d2 : i32 = 0;
Expand Down Expand Up @@ -721,3 +769,52 @@ test "a batch of five ideas covers all five presets under PRESET_AUTO" {
assert(preset_of("THE FPGA RUNS|THE SPEC") == P_PAPER);
assert(preset_of("SEALED|EVERY HASH") == P_RAIN);
}

// ---- pre-flight (gHashTag/t27#10820) ----
// UTF-8 bytes, since L3 keeps the source ASCII: phi (CF 86) " = 1"; "A " and the euro sign
// (E2 82 AC); "A", a stray continuation byte, "B".
pub const PF_PHI : [6]u8 = [207, 134, 32, 61, 32, 49];
pub const PF_EURO : [5]u8 = [65, 32, 226, 130, 172];
pub const PF_STRAY : [3]u8 = [65, 150, 66];

test "pre-flight counts the characters the font would draw as a question mark" {
assert(missing_glyphs("PARTICLES BUILD EVERY FRAME") == 0);
assert(missing_glyphs("WHY?") == 0); // a real '?' is drawn as itself
assert(missing_glyphs("A|B") == 0); // a cut is not drawn
assert(missing_glyphs("lower case folds") == 0);
assert(missing_glyphs(RU_IDEA[0..80]) == 0);
assert(missing_glyphs("A{B}") == 2);
assert(missing_glyphs("X~Y") == 1);
assert(missing_glyphs(PF_PHI[0..6]) == 1);
assert(missing_glyphs(PF_EURO[0..5]) == 1); // one code point, three bytes
assert(missing_glyphs(PF_STRAY[0..3]) == 1); // a malformed byte reads as BAD
assert(first_missing("WHY?") == 0 - 1);
assert(first_missing("A{B}") == 1);
assert(first_missing(PF_PHI[0..6]) == 0);
assert(first_missing(PF_EURO[0..5]) == 2);
assert(first_missing(PF_STRAY[0..3]) == 1);
}

test "pre-flight refuses an empty idea, a missing glyph and a reel past MAX_S, in that order" {
assert(preflight("") == PF_EMPTY);
assert(preflight(" | | ") == PF_EMPTY);
assert(preflight(PF_PHI[0..6]) == PF_GLYPH);
assert(preflight(PF_EURO[0..5]) == PF_GLYPH);
assert(preflight(PF_STRAY[0..3]) == PF_GLYPH);
// 24 scenes and the address: 25 * 72 = 1800 frames, exactly MAX_S * FPS.
assert(reel_frames("A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X") == 1800);
assert(preflight("A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X") == PF_OK);
assert(preflight("A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y") == PF_LONG);
assert(preflight("{|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y") == PF_GLYPH);
}

test "every idea this file renders passes pre-flight" {
assert(preflight(IDEA) == PF_OK);
assert(preflight(CTA) == PF_OK);
assert(preflight(RU_IDEA[0..80]) == PF_OK);
assert(preflight("SPEC IS THE SOURCE|EVERY VIEW DERIVED") == PF_OK);
assert(preflight("ONE LAW|MANY SURFACES") == PF_OK);
assert(preflight("THREE STATES|BEAT TWO") == PF_OK);
assert(preflight("THE FPGA RUNS|THE SPEC") == PF_OK);
assert(preflight("SEALED|EVERY HASH") == PF_OK);
}
Loading