diff --git a/.trinity/seals/lab_LabJobKind.json b/.trinity/seals/lab_LabJobKind.json index 8fbbd818a4..3d110c2005 100644 --- a/.trinity/seals/lab_LabJobKind.json +++ b/.trinity/seals/lab_LabJobKind.json @@ -1,5 +1,5 @@ { - "built_by": "t27c-bootstrap@0.5.2+c948f4df7", + "built_by": "t27c-bootstrap@0.5.2+0fba674ef", "closure": [ { "spec": "specs/hosting/credit.t27", @@ -47,7 +47,7 @@ }, { "spec": "specs/t27f/petal_menu.t27", - "spec_hash": "sha256:e1ced494087ebcbe9e5fe07803f187f1bafef21d7f22086297e7ace675407760" + "spec_hash": "sha256:9e90e3afe418dc36e5087b01566e97115d6e809e1e42bbec686e572ec19554b1" }, { "spec": "specs/trinet/node-work-credit.t27", @@ -67,16 +67,16 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:444db4020d3fc934a71d3ca266416323bb69e148c72832fd25f145eaeaac092b", + "gen_hash_c": "sha256:c7e6bdbb24d42e7fb1f6effee13e471f73c2d120d3378d629cf85ecc8edbcdd3", "gen_hash_rust": "sha256:2b570638d4d94dfd551e8bff976e58d48b2a4de2a9cbadd87ac8646bf5ee3b5b", "gen_hash_verilog": "sha256:364431e262b73b0f826730718deda22b885ec04015fa3a2e252e71c04e3a0d23", - "gen_hash_zig": "sha256:2ca1666f3c8fe9ec60dfd2edffb44b98521c44dd7ce6555908f92a97b9f88520", + "gen_hash_zig": "sha256:45ee6959f3a7b45542f8c30180224caa1ff0fafab76e4d8bf07ae3074177fd72", "module": "LabJobKind", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T13:59:45Z", + "sealed_at": "2026-10-20T07:25:30Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:ddcb04fd605b9b47e33fc6357167ef8035a6006d9031ad2c398d8c8230107118", + "spec_hash": "sha256:ae6064a2e267dcc06583f8a47efd71bb42e6951262f0aa84e5762f6eb9eab590", "spec_path": "specs/lab/job_kind.t27", "tests": { "failed": 0, diff --git a/.trinity/seals/t27os_T27osHomes.json b/.trinity/seals/t27os_T27osHomes.json new file mode 100644 index 0000000000..75e32fb4b9 --- /dev/null +++ b/.trinity/seals/t27os_T27osHomes.json @@ -0,0 +1,37 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+0fba674ef", + "closure": [ + { + "spec": "specs/t27f/petal_menu.t27", + "spec_hash": "sha256:9e90e3afe418dc36e5087b01566e97115d6e809e1e42bbec686e572ec19554b1" + }, + { + "spec": "specs/t27os/session_store.t27", + "spec_hash": "sha256:7d05d7dda14587458a77291c3d587e3f08516d8e678f0a4bd06dcaab7f6d1f2f" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:2d1003dad6a3db9287e48777b36dcfec0eccf4afbcbe7adf290bdc8194a0a11a", + "gen_hash_rust": "sha256:141ff74b276be22f1d8475755c4f39f646a806ab93663f27dc2002701cd004aa", + "gen_hash_verilog": "sha256:07ad7d52d1b53258d6552a7b7089b55f53430f34f593b9e98008f6a7ea81f3ff", + "gen_hash_zig": "sha256:e4ca80e7ba45434ec2575086c4a0d88fc41fae619c6b601f8e263415ba456ce6", + "module": "T27osHomes", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-20T07:25:36Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:b6e84467e629c9b21de72f520f18b48136447f3616d498604b4a0eca60ce74b0", + "spec_path": "specs/t27os/homes.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 26, + "total": 26, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/specs/lab/job_kind.t27 b/specs/lab/job_kind.t27 index 01c62beb3a..2d735e7941 100644 --- a/specs/lab/job_kind.t27 +++ b/specs/lab/job_kind.t27 @@ -872,7 +872,8 @@ test the_reward_card_belongs_to_the_depin_node_petal_of_the_home { assert PETAL_COUNT == 27; assert petal_ok(LJK_HOME_PETAL); assert ljk_world_is(WORLD_NAMES, LJK_HOME_PETAL, "DePIN NODE"); - assert ljk_world_is(WORLD_NAMES, 0, "CHAT"); + assert ljk_world_is(WORLD_NAMES, 2, "CHAT"); + assert ljk_world_is(WORLD_NAMES, 0, "LOGS"); assert ljk_world_is(WORLD_NAMES, 26, "ABOUT"); assert ljk_world_is(WORLD_NAMES, LJK_HOME_PETAL, "DePIN") == false; assert ljk_world_is(WORLD_NAMES, 27, "ABOUT") == false; diff --git a/specs/t27f/petal_menu.t27 b/specs/t27f/petal_menu.t27 index 88c9d0db09..63f7607461 100644 --- a/specs/t27f/petal_menu.t27 +++ b/specs/t27f/petal_menu.t27 @@ -113,8 +113,12 @@ pub const QY : [108]i64 = [ 272637, 248279, 287010, 349402, ]; -/// L2. The 27 worlds, in petal order (TriangleLogo.worlds, from sacred_worlds.zig:99-141). -pub const WORLD_NAMES : str = "CHAT,CODE,LOGS,DEBUG,REVIEW,TRANSLATE,VIBEE,VOICE,COMPOSE,FILES,EDITOR,BUILD,TEST,TERMINAL,GIT,DEPLOY,DePIN NODE,SETTINGS,DOCS,REELS,FEED,ROADMAP,BENCHMARKS,RESEARCH,FORMULAS,COMMUNITY,ABOUT"; +/// L2. The 27 worlds, in petal order. Owner, 2026-10-20: CHAT is the THIRD petal (index 2, Cmd+3; the pair +/// chat + commander lives there) and LOGS takes index 0; petals 19..25 are the seven homes t27a, t27b, t27c, +/// t27f, t27o, t27t, t27u (specs/t27os/homes.t27 maps a home to its petal and reads its names from here, so +/// this string is the ONE definition). Before: TriangleLogo.worlds, from sacred_worlds.zig:99-141, with CHAT +/// first and REELS .. COMMUNITY at 19..25. +pub const WORLD_NAMES : str = "LOGS,CODE,CHAT,DEBUG,REVIEW,TRANSLATE,VIBEE,VOICE,COMPOSE,FILES,EDITOR,BUILD,TEST,TERMINAL,GIT,DEPLOY,DePIN NODE,SETTINGS,DOCS,T27A,T27B,T27C,T27F,T27O,T27T,T27U,ABOUT"; pub const REALM_NAMES : str = "RAZUM,MATERIYA,DUKH"; /// L3. Colours as 0xAARRGGBB. Realm colours from sacred_worlds.zig: gold, cyan, purple. diff --git a/specs/t27os/glass_window.t27 b/specs/t27os/glass_window.t27 index 2f26aa3a22..2ecbc021af 100644 --- a/specs/t27os/glass_window.t27 +++ b/specs/t27os/glass_window.t27 @@ -308,6 +308,15 @@ use t27f::petal_menu::on_back; use t27f::petal_menu::menu_open; use t27f::petal_menu::card_x; use t27f::petal_menu::card_y; +use t27os::homes::PETAL_CHAT; +use t27os::homes::HOME_COUNT; +use t27os::homes::HOME_NAMES; +use t27os::homes::HOME_ROLES; +use t27os::homes::home_of_petal; +use t27os::homes::home_cards; +use t27os::homes::card_title; +use t27os::homes::card_line; +use t27os::homes::card_note; use t27os::doctor::ROOT_HOME; use t27os::doctor::ROOT_TMP; use t27os::doctor::ROOT_TMP_PATH; @@ -1981,6 +1990,95 @@ fn draw_cards(c: u64, kit: [48]u64, task: u32, a: f64, collapsed: bool, mx: f64, } } +/// H. The body of a home tab is the chat's card column with no sidebar: the same slots, the same honeycomb. +pub fn home_card_at(x: f64, y: f64, sw: f64, sh: f64, p: u32) -> u32 { + const k : u32 = card_at(x, y, sw, sh, WS_EXPANDED, true); + if (k < home_cards(home_of_petal(p))) { return k; } + return CARD_SLOTS; +} + +/// H. Text from a table constant, as bytes. +fn draw_str(c: u64, kit: [48]u64, s: []const u8, x: f64, base: f64, size: f64, weight: f64) -> f64 { + return draw_bytes(c, kit, s, s.len, x, base, size, weight, false); +} + +/// H. One line of a card, centred on kx and cut with an ellipsis where it would pass the cell's width. +fn draw_mid(c: u64, kit: [48]u64, s: []const u8, n: usize, kx: f64, base: f64, size: f64, weight: f64, room: f64) -> void { + if (n == 0) { return; } + const ns : u64 = cf_bytes(0, s, n, UTF8, false); + if (ns == 0) { return; } + const w : f64 = text_w(kit, ns, size, weight, false); + if (w <= room) { + draw_text(c, kit, ns, kx - 0.5 * w, base, size, weight, false); + } else { + draw_text_fit(c, kit, ns, kx - 0.5 * room, base, size, weight, false, kx + 0.5 * room); + } + cf_release(ns); +} + +/// H. The screen of a petal that is not the CHAT petal (t27 OS: black and white, shape and text only). The +/// title bar below is the chat's; the header names the petal and, for a home, the home and its role; the body +/// is the 3 x 3 honeycomb with the cards homes.t27 lists for that home (agents, widget counts, courses). A petal +/// that is no home shows the nine empty cells (petal_menu: every menu empty). +fn draw_home(c: u64, kit: [48]u64, p: u32, a: f64, mx: f64, my: f64, sw: f64, sh: f64) -> void { + const h : u32 = home_of_petal(p); + cg_fill_rgba(c, 0.0, 0.0, 0.0, STRICT_CONTENT * a); + cg_fill_rect(c, 0.0, WS_TOP, sw, sh - WS_TOP); + const base : f64 = WS_TOP + 0.5 * HEADER_H + 4.2; + cg_fill_rgba(c, 1.0, 1.0, 1.0, TEXT_WHITE * a); + const tw : f64 = draw_text(c, kit, world(kit, p), 14.0, base, 12.0, WEIGHT_SEMIBOLD, false); + if (h < HOME_COUNT) { + cg_fill_rgba(c, 1.0, 1.0, 1.0, MUTED_WHITE * a); + const nw : f64 = draw_str(c, kit, HOME_NAMES[h], 14.0 + tw + 12.0, base, 11.0, WEIGHT_MEDIUM); + cg_fill_rgba(c, 1.0, 1.0, 1.0, LABEL_WHITE * a); + const rx : f64 = 14.0 + tw + 12.0 + nw + 12.0; + const rn : usize = HOME_ROLES[h].len; + const rs : u64 = cf_bytes(0, HOME_ROLES[h], rn, UTF8, false); + if (rs != 0) { + draw_text_fit(c, kit, rs, rx, base, 10.0, 0.0, false, sw - 14.0); + cf_release(rs); + } + } + cg_fill_rgba(c, 1.0, 1.0, 1.0, LINE_WHITE * a); + cg_fill_rect(c, 0.0, WS_TOP + HEADER_H, sw, HAIR); + const l : f64 = body_left(sw, WS_EXPANDED, true); + const t : f64 = body_top(WS_EXPANDED); + const w : f64 = body_w(sw, WS_EXPANDED, true); + const bh : f64 = body_h(sw, sh, WS_EXPANDED, true); + const r : f64 = hex_r(w, bh); + const room : f64 = SQRT3 * r - 16.0; + var hk : u32 = CARD_SLOTS; + if (a >= 1.0) { hk = home_card_at(mx, my, sw, sh, p); } + var tb : [64]u8 = undefined; + var k : u32 = 0; + while (k < CARD_SLOTS) { + const ka : f64 = card_alpha(a, k); + var kf : f64 = STRICT_CARD_FILL; + if (k == hk) { kf = STRICT_HOVER_FILL; } + const kx : f64 = hex_c(k, 0, l, t, w, bh); + const ky : f64 = hex_c(k, 1, l, t, w, bh) + (1.0 - ka) * 6.0; + hex_path(c, kx, ky, r - 0.25); + cg_fill_rgba(c, 1.0, 1.0, 1.0, kf * ka); + cg_fill_path(c); + cg_line_width(c, cell_line(k)); + hex_path(c, kx, ky, r - 0.25); + cg_stroke_rgba(c, 1.0, 1.0, 1.0, cell_edge(k, hk, CARD_SLOTS) * ka); + cg_stroke_path(c); + if (h < HOME_COUNT and k < home_cards(h)) { + cg_fill_rgba(c, 1.0, 1.0, 1.0, TEXT_WHITE * ka); + const n1 : usize = card_title(h, k, tb[0..64]); + draw_mid(c, kit, tb[0..64], n1, kx, ky - 3.0, 11.0, WEIGHT_SEMIBOLD, room); + cg_fill_rgba(c, 1.0, 1.0, 1.0, MUTED_WHITE * ka); + const n2 : usize = card_line(h, k, tb[0..64]); + draw_mid(c, kit, tb[0..64], n2, kx, ky + 12.0, 9.5, 0.0, room); + cg_fill_rgba(c, 1.0, 1.0, 1.0, LABEL_WHITE * ka); + const n3 : usize = card_note(h, k, tb[0..64]); + draw_mid(c, kit, tb[0..64], n3, kx, ky + 25.0, 8.5, 0.0, room); + } + k = k + 1; + } +} + /// F. The frame's fill alpha: clear in a window (matte black glass, the blur and the layer tint show /// through), opaque black #000000 in full screen (no transparency there). pub fn backdrop_alpha(full: bool) -> f64 { @@ -2534,13 +2632,19 @@ fn draw(c: u64, kit: [48]u64, frame: u32, state: u32, hov: u32, ma: f64, shown: if (hov != PETAL_NONE) { draw_tooltip(c, kit, hov, ha, z, sw, sh); } } if (ma > 0.0) { - draw_chat(c, kit, shown, task, space, sp, ss, cur, ma, collapsed, mx, my, sw, sh, ws, dlabel, dp); - if (topen != PANE_NONE) { draw_term(c, kit, tst, tcells, tcur, ma, collapsed, sw, sh, ws); } + // the screen of petal `shown`: the chat workspace for the CHAT petal (index 2), a home tab for t27a .. + // t27u, an empty frame with the petal's name for every other petal + if (shown == PETAL_CHAT) { + draw_chat(c, kit, shown, task, space, sp, ss, cur, ma, collapsed, mx, my, sw, sh, ws, dlabel, dp); + if (topen != PANE_NONE) { draw_term(c, kit, tst, tcells, tcur, ma, collapsed, sw, sh, ws); } + } else { + draw_home(c, kit, shown, ma, mx, my, sw, sh); + } } if (qp) { draw_queen_panel(c, kit, sw, sh, mx, my, view_of(menu_open(state), shown), space, ss, cur, ring, n, now, sug, god); } if (dp and qp == false) { draw_doctor_panel(c, kit, dk, sw, sh, mx, my, darmed, doff, dgo, dlive); } if (kyp and qp == false and dp == false) { draw_keys_panel(c, kit, sw, sh, mx, my, kstored, kmask, kmlen, kst, kfail, kopen, now, kedit, kbuf, klen, karm, kbusy, kemask, kewhy, ktpick); } - if (ma > 0.0 and ws == WS_EXPANDED and collapsed == false) { + if (ma > 0.0 and shown == PETAL_CHAT and ws == WS_EXPANDED and collapsed == false) { // K. KEYS in the foot, left of DOCTOR var ka : f64 = LABEL_WHITE; if (kyp or on_keys_foot(mx, my, sh, ws, collapsed)) { ka = TEXT_WHITE; } @@ -3738,7 +3842,10 @@ pub fn main() -> void { var vy : i64 = @as(i64, @intFromFloat(virt(my, 1, z, sw, sh))); // H. the logo answers the mouse only at home, once the screen has gone. const home_live : bool = menu_open(state) == false and ma == 0.0 and my > AREA_TOP; - const screen : bool = menu_open(state) and ma >= 0.5; + // the CHAT petal's screen carries every chat control; a home tab (any other petal) carries only the + // title bar's back chevron and its cards + const screen : bool = menu_open(state) and ma >= 0.5 and shown == PETAL_CHAT; + const hscreen : bool = menu_open(state) and ma >= 0.5 and shown != PETAL_CHAT; now = unix_time(0); var sug : u32 = shown_suggestion(suggest_for(sessions_in(ss, space), cur != SESSION_NONE, screen and ktpick != TERM_NONE, edone, space), dismissed); var ev : u64 = msg_event(app, s_next, 0xffffffffffffffff, msg_f(date_cls, s_wait, fwait), mode, true); @@ -3875,7 +3982,7 @@ pub fn main() -> void { msg_set_frame(win, s_set_frame, vf[0] + vf[2] - PANEL_W, vf[1], PANEL_W, vf[3], true, true); } docked = docked == false; - } else if (screen and on_nav_back(mx, my, full)) { + } else if ((screen or hscreen) and on_nav_back(mx, my, full)) { state = on_back(state); ctl = C_HOME; } else if (screen and on_open_dash(mx, my, sw, ws)) { @@ -4413,6 +4520,7 @@ pub fn main() -> void { var hov : u32 = PETAL_NONE; if (home_live) { hov = hit_px(vx, vy, 960, 540); } var on_control : bool = on_home_button(mx, my, sw) or on_full_button(mx, my, sw) or on_dock_button(mx, my, sw); + if (hscreen and on_nav_back(mx, my, full)) { on_control = true; } if (screen and (on_nav_back(mx, my, full) or on_side_toggle(mx, my, ws, collapsed))) { on_control = true; } if (screen and (on_open_dash(mx, my, sw, ws) or on_close_dash(mx, my, ws, collapsed))) { on_control = true; } if (screen and ma >= 1.0 and card_at(mx, my, sw, sh, ws, collapsed) != CARD_SLOTS) { on_control = true; } @@ -4934,3 +5042,72 @@ test the_editor_view_shares_the_terminal_areas_key_space_with_the_chat_panes { assert view_key(true, true, 2) == VIEW_EDITOR; assert view_key(true, false, SESSION_NONE) == VIEW_EDITOR; } + +test chat_is_the_third_petal_and_cmd_3_opens_it { + assert PETAL_CHAT == 2; + assert petal_for_key(3, MOD_COMMAND) == PETAL_CHAT; + assert petal_for_key(1, MOD_COMMAND) == 0; + assert petal_for_key(4, MOD_COMMAND) == 3; + assert PETAL_CHAT != 0; +} + +test each_home_petal_draws_its_own_home_and_the_rest_draw_the_empty_frame { + var p : u32 = 19; + while (p <= 25) { + assert home_of_petal(p) == p - 19; + assert home_cards(home_of_petal(p)) >= 2; + p = p + 1; + } + assert home_of_petal(PETAL_CHAT) == 7; + assert home_of_petal(0) == HOME_COUNT; + assert home_of_petal(1) == HOME_COUNT; + assert home_of_petal(26) == HOME_COUNT; + assert home_of_petal(18) == HOME_COUNT; + assert home_cards(home_of_petal(26)) == 0; +} + +test a_card_in_a_home_tab_is_hit_where_it_is_drawn_and_only_for_cards_that_exist { + const l : f64 = body_left(W_PT, WS_EXPANDED, true); + const t : f64 = body_top(WS_EXPANDED); + const w : f64 = body_w(W_PT, WS_EXPANDED, true); + const bh : f64 = body_h(W_PT, H_PT, WS_EXPANDED, true); + var p : u32 = 19; + while (p <= 25) { + const n : u32 = home_cards(home_of_petal(p)); + var hits : u32 = 0; + var k : u32 = 0; + while (k < CARD_SLOTS) { + const got : u32 = home_card_at(hex_c(k, 0, l, t, w, bh), hex_c(k, 1, l, t, w, bh), W_PT, H_PT, p); + if (k < n) { assert got == k; hits = hits + 1; } + if (k >= n) { assert got == CARD_SLOTS; } + k = k + 1; + } + assert hits == n; + p = p + 1; + } + // a petal that is no home has no card to hit, and neither has the empty corner of the body + assert home_card_at(hex_c(0, 0, l, t, w, bh), hex_c(0, 1, l, t, w, bh), W_PT, H_PT, 26) == CARD_SLOTS; + assert home_card_at(1.0, 1.0, W_PT, H_PT, 21) == CARD_SLOTS; +} + +test the_home_tab_cards_do_not_overlap_and_hold_nine_slots { + const l : f64 = body_left(W_PT, WS_EXPANDED, true); + const t : f64 = body_top(WS_EXPANDED); + const w : f64 = body_w(W_PT, WS_EXPANDED, true); + const bh : f64 = body_h(W_PT, H_PT, WS_EXPANDED, true); + const r : f64 = hex_r(w, bh); + var a : u32 = 0; + while (a < CARD_SLOTS) { + var b : u32 = a + 1; + while (b < CARD_SLOTS) { + const dx : f64 = hex_c(a, 0, l, t, w, bh) - hex_c(b, 0, l, t, w, bh); + const dy : f64 = hex_c(a, 1, l, t, w, bh) - hex_c(b, 1, l, t, w, bh); + assert dx * dx + dy * dy > 4.0 * 0.75 * r * r; + b = b + 1; + } + a = a + 1; + } + assert CARD_SLOTS == 9; + // the title bar's back chevron answers on a home tab exactly as on the chat + assert on_nav_back(home_x(false) + 2.0, 0.5 * TITLE_H, false); +} diff --git a/specs/t27os/homes.t27 b/specs/t27os/homes.t27 new file mode 100644 index 0000000000..6fcb561c78 --- /dev/null +++ b/specs/t27os/homes.t27 @@ -0,0 +1,950 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/t27os/homes.t27 -- the HOMES of t27: which product each agent and each widget belongs to, and the +; tab T27 OS draws for each home. One registry; T27 OS reads its tabs from here. +; Owner, 2026-10-20: study all our agents and their homes -- t27a, t27b, t27c, t27u (the university with +; all its courses), t27t (our own model's training), and any other -- and link every widget to its home. +; Evidence, read on 2026-10-20 (nothing here is invented; what could not be established is named): +; - Homes with a law of their own: t27a specs/isa/t27a.t27; t27b cli/t27b/README.md (the native compiler, +; `t27b test|build|verilog`); t27c bootstrap/src/compiler.rs (the bootstrap compiler, sealed by +; bootstrap/stage0/FROZEN_HASH) with `t27c silicon|verdict|bench-agent`; t27f specs/t27f/frontend.t27; +; t27o specs/tools/t27o/rust_convert.t27 (AGENTS.md: converts foreign code into .t27); t27t +; specs/t27t/t27t.t27 (AGENTS.md "t27t: the t27 trainer", `tri t27t`); t27u gHashTag/trinity +; apps/website/specs/course/courses.t27 (7 courses of 27 lessons). +; - Not homes, and why: t27os is the host that draws the tabs (it owns no product another home lacks); +; t27mcp is the service half of T27F and TRI CLAW (specs/t27mcp/service.t27 decides by prefix which +; tools it serves; its `home_of` is about MCP tool names, not about this registry). +; - The eighth home, QUEEN: the shared home. The Queen (agent T, specs/queen, specs/t27os/queen.t27) is the +; orchestrator, and the agents that serve every home equally (governance, memory, metrics, scheduling, +; security) have no single product; they live with her rather than in a gap. +; - Letters are NOT homes: agent B is Beta (Build), not t27b; agent U is Upsilon (Universe levels), not the +; university t27u; agent I is the ISA, which is why it sits in t27a. +; - Widgets: the 195 ids of the t27 widget gallery (gHashTag/trinity apps/website/specs/widgets/gallery.t27), +; by index in gallery order. A widget belongs to the home whose product emits what it shows: bitstream, +; fasm, pins, JTAG, timing, Verilog and every `t27c-` page are t27c's; GoldenFloat and training pieces +; are t27t's; a lesson widget that exists only for a course (basics-*, golden-*) is t27u's; the Queen's +; loop and arena are the Queen's. A lesson may open any widget: that is COURSE_WIDGETS, a second link. +; - Petals: T27 OS has 27 petals in 3 realms of 9 (specs/t27f/petal_menu.t27). Owner: CHAT moves to the +; THIRD petal (index 2) and the pair chat + commander lives there; each home gets a petal tab. Default: +; CHAT and LOGS swap places, and the homes take petals 19..25 of the third realm in the order t27a, t27b, +; t27c, t27f, t27o, t27t, t27u. The Queen's tab is the CHAT petal. ABOUT stays at 26. Moving a home is one +; edit to HOME_PETAL and to WORLD_NAMES in petal_menu.t27 (the one definition of the names). The seven worlds REELS .. COMMUNITY give up their petals. +; Not established from evidence (so not guessed): what "commander" is, since the repository names no such +; component; whether the tri-fpga-* casts belong to t27c or to the `tri` CLI (filed under t27c because +; `tri fpga` drives `t27c silicon`); which widget a t27a, t27b, t27f or t27o page would be (the gallery has +; none: those four homes show 0 widgets, which is a measurement and not a gap in the table). +; ASCII only (L3), English only (LANG-EN). +; phi^2 + 1/phi^2 = 3 | TRINITY + +module T27osHomes; + +use t27f::petal_menu::WORLD_NAMES; +use t27f::petal_menu::PETAL_COUNT; +use t27f::petal_menu::PETAL_NONE; +use t27os::session_store::csv_field; +use t27os::session_store::put_bytes; +use t27os::session_store::put_dec; + +; --- L1. The homes --------------------------------------------------------------------------------------- + +pub const H_T27A : u32 = 0; +pub const H_T27B : u32 = 1; +pub const H_T27C : u32 = 2; +pub const H_T27F : u32 = 3; +pub const H_T27O : u32 = 4; +pub const H_T27T : u32 = 5; +pub const H_T27U : u32 = 6; +pub const H_QUEEN : u32 = 7; +pub const HOME_COUNT : u32 = 8; + +pub const HOME_IDS : [8]str = ["t27a", "t27b", "t27c", "t27f", "t27o", "t27t", "t27u", "queen"]; +pub const HOME_NAMES : [8]str = ["t27a assembler", "t27b native compiler", "t27c compiler", "t27f frontend", "t27o converter", "t27t trainer", "t27u university", "Queen"]; +pub const HOME_ROLES : [8]str = [ + "The TRI-27 assembler and disassembler: one field table, both directions.", + "t27's own native backend: AArch64 JIT test runner, objects and the Verilog path.", + "The bootstrap compiler and the sweep: parse, gen, seal, test, silicon, verdict.", + "T27F: everything visual t27 shows, from spec diagrams to the petal home.", + "Ouroboros: converts code in other languages into .t27.", + "Training our own model: what makes a run cheaper, measured not promised.", + "The university: 7 courses of 27 lessons, each lesson one working widget.", + "The orchestrator and the agents that serve every home.", +]; +pub const HOME_LAWS : [8]str = [ + "specs/isa/t27a.t27", + "cli/t27b/README.md", + "bootstrap/src/compiler.rs", + "specs/t27f/frontend.t27", + "specs/tools/t27o/rust_convert.t27", + "specs/t27t/t27t.t27", + "gHashTag/trinity apps/website/specs/course/courses.t27", + "specs/t27os/queen.t27", +]; +pub const HOME_RUNNERS : [8]str = [ + "t27c asm, t27c disasm", + "t27b test, t27b build, t27b verilog", + "./target/release/t27c, tri test, t27c silicon", + "T27F MCP tools, claw/t27f check, t27.ai/t27f/graph/", + "tri t27o [--oracle] [--check]", + "tri t27t; runner gHashTag/igla-coder-gpu train.py; specs/ml/train_hslm.t27 under t27c --backend ml", + "t27.ai learn/ pages and app #/", + "T27 OS Queen panel, GOD MODE, scheduled loops", +]; + +pub fn home_ok(h: u32) -> bool { + return h < HOME_COUNT; +} + +; --- L2. The 27 agent letters, each in exactly one home -------------------------------------------------- +; Index = ORDINAL - 1 of specs/agents/.t27. The note is the evidence for the placement. + +pub const AGENT_COUNT : u32 = 27; +pub const AGENT_LETTERS : [27]str = ["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", "Z", "Ti"]; +pub const AGENT_HOME : [27]u32 = [ + 7, 2, 2, 4, 7, 2, 3, 5, 0, + 7, 1, 2, 7, 5, 7, 7, 7, 2, + 7, 7, 7, 2, 7, 4, 7, 6, 7, +]; +pub const AGENT_NOTES : [27]str = [ + "A Architecture: SOUL.md and ADRs govern every home, so shared (DOMAIN Architecture / ADR / SOUL).", + "B Build: KEY_FILES src/tri/pipeline/, skill tri-pipeline; the pipeline runs t27c gen/test/seal.", + "C Compiler Core: KEY_FILES bootstrap/src/compiler.rs, specs/ar.", + "D De-Zigfication: docs/migration-map.md; foreign code eaten into .t27 is t27o's job (AGENTS.md).", + "E Experience: .trinity/experience, skill wrap-up; memory of all homes, shared.", + "F Formal Conformance: KEY_FILES conformance/FORMAT-SPEC-001.json; t27c conformance command.", + "G Graph: KEY_FILES architecture/graph_v2.json; the spec graph IR is t27f's (spec_graph.t27).", + "H HSLM: KEY_FILES specs/nn/hslm.t27; trained by specs/ml/train_hslm.t27, t27t's second runner.", + "I ISA: KEY_FILES specs/isa/registers.t27, the table t27a assembles with.", + "J Jobs: src/tri/dev_commands.zig, skills board-sync, wave-audit; task routing, shared.", + "K Kernel / FPGA MAC: KEY_FILES specs/fpga/mac.t27; FPGA specs reach Verilog by t27b verilog (AGENTS.md L4 line). Inferred.", + "L Language: docs TRI_SYNTAX_VNEXT.md; the syntax is what t27c parses.", + "M Metrics: .trinity/bench, skill measure-corpus; measures every home, shared.", + "N Numeric / GoldenFloat: specs/numeric; the formats t27t trains in and whose papers its check recomputes.", + "O Orchestration: skills phi-loop, tri; the phases every home runs through, shared.", + "P Physics: specs/math/sacred_physics.t27; no product consumes it, shared.", + "Q Queue / Scheduling: dev_commands.zig, skill god-mode; the Queen's scheduler, shared.", + "R Runtime / Bootstrap: KEY_FILES bootstrap/src/compiler.rs.", + "S Specs / Standardization: the whole specs/ tree, skill measure-corpus; shared.", + "T Tau, the TRINITY Queen: specs/queen/lotus.t27; 27 skills, the orchestrator herself.", + "U Universe levels: KEY_FILES t27/domains/; name collision with t27u, no course evidence, so shared.", + "V Verdict / Bench: src/tri/verdict.zig, skill tri-pipeline; t27c verdict and t27c frontier.", + "W Workflow / tri cell: src/tri/cell.zig, skills phi-loop, tri, implement-issue; shared.", + "X External bindings: KEY_FILES bindings/; interop with foreign code, next to t27o.", + "Y Yield / DePIN: deploy/contracts/, skills providers-steward, farm-garden; network, shared.", + "Z Zero-Touch UX / Docs: KEY_FILES docs/, skills blog-post, ux-wave; teaching material. Inferred.", + "Ti Security / Reserve: no KEY_FILES, no skills; reserve, shared.", +]; + +pub fn home_of_agent(ordinal: u32) -> u32 { + if (ordinal < 1 or ordinal > AGENT_COUNT) { + return HOME_COUNT; + } + return AGENT_HOME[ordinal - 1]; +} + +pub fn agents_in(h: u32) -> u32 { + var n : u32 = 0; + var k : u32 = 0; + while (k < AGENT_COUNT) { + if (AGENT_HOME[k] == h) { n = n + 1; } + k = k + 1; + } + return n; +} + +; --- L3. The named agents of .claude/agents -------------------------------------------------------------- + +pub const NAMED_COUNT : u32 = 12; +pub const NAMED_AGENTS : [12]str = [ + "agent-c-compiler", "agent-e-experience", "agent-l-lsp", "agent-v-verify", + "creator", "experience", "learner", "queen-browser-steward", + "review-warden", "t27c-steward", "trinity", "verifier", +]; +pub const NAMED_HOME : [12]u32 = [2, 7, 2, 2, 2, 7, 7, 7, 7, 2, 7, 7]; +pub const NAMED_NOTES : [12]str = [ + "Agent C compiles t27 specs, three-roads generation: t27c.", + "Agent E captures learnings of PHI LOOP cycles: shared memory.", + "Agent L validates LSP and type definitions of specs: the front end of t27c.", + "Agent V validates generated code against specs, L1-L7: t27c's conformance.", + "Creator generates code from specs: the gen stage of t27c.", + "Experience manages learning history: shared memory.", + "Learner researches patterns, builds the knowledge base: shared memory.", + "Keeps the BROWSER page and board loops turning, hands work to the Queen.", + "Keeps pull-request review moving, hands fixes to the Queen.", + "Steward of epic #6092 (silent-misread stack) in the t27c lab.", + "The Trinity Agent (Queen): AEL v2.0 orchestration.", + "Verifier validates correctness across homes; shared, with the Queen.", +]; + +pub fn home_of_named(i: u32) -> u32 { + if (i >= NAMED_COUNT) { + return HOME_COUNT; + } + return NAMED_HOME[i]; +} + +pub fn named_in(h: u32) -> u32 { + var n : u32 = 0; + var k : u32 = 0; + while (k < NAMED_COUNT) { + if (NAMED_HOME[k] == h) { n = n + 1; } + k = k + 1; + } + return n; +} + +; --- L4. Widgets to homes -------------------------------------------------------------------------------- +; The gallery has 195 widgets in one order. RUN_START[r] is the first index of run r and RUN_HOME[r] the +; home of every widget from there up to the next start. RUN_ANCHOR[r] is the id at that index, so a +; reader can find the run's start without the gallery open. + +pub const WIDGET_COUNT : u32 = 195; +pub const RUN_COUNT : u32 = 13; +pub const RUN_START : [13]u32 = [0, 12, 13, 29, 31, 123, 126, 180, 185, 186, 187, 190, 191]; +pub const RUN_HOME : [13]u32 = [2, 5, 2, 5, 2, 5, 6, 2, 7, 2, 7, 2, 7]; +pub const RUN_ANCHOR : [13]str = [ + "x7-board", "gf16-calc", "idcode", "t27-cell", "uart-bucket", "t27c-gft-nll", "basics-what-a-spec-is", + "t27-tri-vectors", "tri-selftest", "play-terminal-gates", "wars", "perf2-knockouts", "race-the-bee", +]; +pub const RUN_NOTES : [13]str = [ + "The FPGA flow, then the bench tools up to gf16-calc: bitstream, fasm, pins, JTAG, timing, Verilog -- what t27c silicon emits.", + "gf16-calc: a GoldenFloat calculator, t27t's format.", + "idcode to lut-treemap: bench readouts of the build.", + "t27-cell and t27-vs-nvfp4: number-format seats against FP4, the formats a trainer picks.", + "uart-bucket, the tri-fpga casts, every t27c- compiler page up to t27c-gft-signed-mac and the audits after the lessons.", + "t27c-gft-nll, -sgd-step, -xornet: loss, update and a net trained on XOR.", + "Lesson-only widgets: basics-* (27) and golden-* (27) exist for the two courses that own them.", + "t27-tri-* corpus audits up to t27-tri-lean.", + "tri-selftest: checks tri, railway, python3 and the loop directory -- the Queen's loop tools.", + "play-terminal-gates: the seven backends run on hello_world.", + "wars, tri-game-tick, tri-game-vault: the Queen's arena and loop.", + "perf2-knockouts: three performance bits, one knockout bitstream each -- a bitstream, so t27c.", + "race-the-bee, gatle, tri-blog-list, tri-game-selftest: the agent game shelf and the Queen's blog list.", +]; + +pub fn home_of_widget(i: u32) -> u32 { + if (i >= WIDGET_COUNT) { + return HOME_COUNT; + } + var r : u32 = RUN_COUNT - 1; + while (r > 0 and RUN_START[r] > i) { + r = r - 1; + } + return RUN_HOME[r]; +} + +pub fn widgets_in(h: u32) -> u32 { + var n : u32 = 0; + var i : u32 = 0; + while (i < WIDGET_COUNT) { + if (home_of_widget(i) == h) { n = n + 1; } + i = i + 1; + } + return n; +} + +; --- L5. Courses (t27u) ---------------------------------------------------------------------------------- +; Read from courses.t27 and the seven course specs on 2026-10-20. ROUTE is the app address (#/); +; SHARE is the share page under t27.ai: learn/ for the first course, learn// for the rest. + +pub const COURSE_COUNT : u32 = 7; +pub const LESSONS_PER_COURSE : u32 = 27; +pub const COURSE_IDS : [7]str = ["t27-basics", "course", "ai-numbers", "goldenfloat", "verify-hardware", "clocks-and-cdc", "buses-and-peripherals"]; +pub const COURSE_ROUTES : [7]str = ["t27-basics", "course", "ai-numbers", "goldenfloat", "verify-hardware", "clocks-and-cdc", "buses-and-peripherals"]; +pub const COURSE_SHARE : [7]str = ["learn/", "learn/course/", "learn/ai-numbers/", "learn/goldenfloat/", "learn/verify-hardware/", "learn/clocks-and-cdc/", "learn/buses-and-peripherals/"]; +pub const COURSE_SPECS : [7]str = [ + "specs/course/t27-basics.t27", "specs/course/course.t27", "specs/course/ai-numbers.t27", + "specs/course/goldenfloat.t27", "specs/course/verify-hardware.t27", "specs/course/clocks-and-cdc.t27", + "specs/course/buses-and-peripherals.t27", +]; +/// The gallery index of the widget each lesson opens, course by course, lesson by lesson (7 x 27). +pub const COURSE_WIDGETS : [189]u32 = [ + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 22, 13, 15, 12, 11, 24, 95, 19, 99, 101, 98, 96, 8, 25, 14, 20, 27, 28, 16, 26, 18, 1, 10, 21, 5, 0, 192, + 29, 30, 31, 103, 102, 104, 105, 106, 107, 108, 109, 110, 117, 118, 119, 120, 121, 122, 123, 124, 125, 111, 112, 113, 114, 115, 116, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 182, 32, 33, 97, 186, 190, 23, 34, 9, 180, 181, 7, 35, 36, 6, 17, 37, 183, 185, 38, 184, 2, 100, 40, 41, 39, 3, + 42, 43, 44, 45, 194, 191, 46, 47, 48, 49, 50, 61, 62, 51, 52, 53, 54, 188, 55, 58, 59, 56, 60, 4, 189, 57, 193, + 70, 71, 72, 76, 89, 82, 77, 69, 88, 78, 90, 83, 79, 73, 84, 68, 75, 85, 74, 80, 86, 92, 93, 63, 64, 65, 67, +]; +/// Returned for a widget no lesson opens. +pub const NO_COURSE : u32 = 7; + +pub fn course_widget(c: u32, lesson: u32) -> u32 { + if (c >= COURSE_COUNT or lesson >= LESSONS_PER_COURSE) { + return WIDGET_COUNT; + } + return COURSE_WIDGETS[c * LESSONS_PER_COURSE + lesson]; +} + +pub fn course_of_widget(i: u32) -> u32 { + var k : u32 = 0; + while (k < 189) { + if (COURSE_WIDGETS[k] == i) { return k / LESSONS_PER_COURSE; } + k = k + 1; + } + return NO_COURSE; +} + +pub fn widgets_without_course() -> u32 { + var n : u32 = 0; + var i : u32 = 0; + while (i < WIDGET_COUNT) { + if (course_of_widget(i) == NO_COURSE) { n = n + 1; } + i = i + 1; + } + return n; +} + +; --- L6. Petals ------------------------------------------------------------------------------------------ + +pub const PETAL_CHAT : u32 = 2; +pub const HOME_PETAL : [8]u32 = [19, 20, 21, 22, 23, 24, 25, 2]; +/// The 27 world names are WORLD_NAMES of t27f::petal_menu (the single definition): CHAT at index 2, the seven +/// product homes at 19..25 where REELS .. COMMUNITY stood, ABOUT at 26. The tests below pin it against HOME_PETAL. + +pub fn petal_of_home(h: u32) -> u32 { + if (home_ok(h) == false) { + return PETAL_NONE; + } + return HOME_PETAL[h]; +} + +pub fn home_of_petal(p: u32) -> u32 { + var h : u32 = 0; + while (h < HOME_COUNT) { + if (HOME_PETAL[h] == p) { return h; } + h = h + 1; + } + return HOME_COUNT; +} + +; --- L7. The cards of a home tab ------------------------------------------------------------------------- +; A home tab is a 3 x 3 grid of cards (petal_menu CARD_SLOTS). The order is fixed: the agents that live in +; the home (letter + name; four cards at most, the fourth reads "+N agents" when there are more), then one +; card with the home's widget total, then the widget groups (the runs of RUN_START that belong to the home; +; four at most, the last "+N groups" when there are more), then, for t27u only, the seven courses. A home +; with fewer than 9 cards leaves the rest empty. Counts are measured from the tables above, never typed. + +pub const HOME_CARD_SLOTS : u32 = 9; +pub const MAX_AGENT_CARDS : u32 = 4; +pub const MAX_GROUP_CARDS : u32 = 4; + +pub const CK_NONE : u32 = 0; +pub const CK_AGENT : u32 = 1; +pub const CK_AGENT_MORE : u32 = 2; +pub const CK_WIDGETS : u32 = 3; +pub const CK_GROUP : u32 = 4; +pub const CK_GROUP_MORE : u32 = 5; +pub const CK_COURSE : u32 = 6; + +pub fn runs_in(h: u32) -> u32 { + var n : u32 = 0; + var r : u32 = 0; + while (r < RUN_COUNT) { + if (RUN_HOME[r] == h) { n = n + 1; } + r = r + 1; + } + return n; +} + +/// The run index of the n-th run of home h, RUN_COUNT when there is none. +pub fn nth_run(h: u32, n: u32) -> u32 { + var seen : u32 = 0; + var r : u32 = 0; + while (r < RUN_COUNT) { + if (RUN_HOME[r] == h) { + if (seen == n) { return r; } + seen = seen + 1; + } + r = r + 1; + } + return RUN_COUNT; +} + +/// How many widgets run r holds. +pub fn run_size(r: u32) -> u32 { + if (r >= RUN_COUNT) { return 0; } + if (r + 1 == RUN_COUNT) { return WIDGET_COUNT - RUN_START[r]; } + return RUN_START[r + 1] - RUN_START[r]; +} + +/// The ordinal (1..27) of the n-th letter agent of home h, 0 when there is none. +pub fn nth_agent(h: u32, n: u32) -> u32 { + var seen : u32 = 0; + var k : u32 = 0; + while (k < AGENT_COUNT) { + if (AGENT_HOME[k] == h) { + if (seen == n) { return k + 1; } + seen = seen + 1; + } + k = k + 1; + } + return 0; +} + +pub fn agent_cards(h: u32) -> u32 { + const a : u32 = agents_in(h); + if (a > MAX_AGENT_CARDS) { return MAX_AGENT_CARDS; } + return a; +} + +pub fn group_cards(h: u32) -> u32 { + if (h == H_T27U) { return 0; } + const g : u32 = runs_in(h); + if (g > MAX_GROUP_CARDS) { return MAX_GROUP_CARDS; } + return g; +} + +pub fn home_cards(h: u32) -> u32 { + if (home_ok(h) == false) { return 0; } + var n : u32 = agent_cards(h) + 1 + group_cards(h); + if (h == H_T27U) { n = n + COURSE_COUNT; } + return n; +} + +pub fn card_kind(h: u32, k: u32) -> u32 { + if (k >= home_cards(h)) { return CK_NONE; } + const na : u32 = agent_cards(h); + if (k < na) { + if (agents_in(h) > MAX_AGENT_CARDS and k + 1 == na) { return CK_AGENT_MORE; } + return CK_AGENT; + } + if (k == na) { return CK_WIDGETS; } + const g : u32 = group_cards(h); + if (k < na + 1 + g) { + if (runs_in(h) > MAX_GROUP_CARDS and k == na + g) { return CK_GROUP_MORE; } + return CK_GROUP; + } + return CK_COURSE; +} + +/// What a card points at: the agent ordinal, the number of agents or groups folded into "+N", the widget +/// total, the run index, or the course index. +pub fn card_arg(h: u32, k: u32) -> u32 { + const kind : u32 = card_kind(h, k); + const na : u32 = agent_cards(h); + const g : u32 = group_cards(h); + if (kind == CK_AGENT) { return nth_agent(h, k); } + if (kind == CK_AGENT_MORE) { return agents_in(h) - (na - 1); } + if (kind == CK_WIDGETS) { return widgets_in(h); } + if (kind == CK_GROUP) { return nth_run(h, k - na - 1); } + if (kind == CK_GROUP_MORE) { return runs_in(h) - (g - 1); } + if (kind == CK_COURSE) { return k - na - 1 - g; } + return 0; +} + +/// "1 widget", "27 lessons": the count, a space, then the singular or the plural word. +fn put_counted(out: []u8, at: usize, n: u32, one: []const u8, many: []const u8) -> usize { + const c : usize = put_dec(out, at, @as(u64, n)); + const sp : usize = put_bytes(out, c, " "); + if (n == 1) { return put_bytes(out, sp, one); } + return put_bytes(out, sp, many); +} + +/// The name part of an AGENT_NOTES line: the text after " " up to the colon. +fn put_note_name(out: []u8, at: usize, note: []const u8) -> usize { + var i : usize = 0; + while (i < note.len and note[i] != 32) { i = i + 1; } + i = i + 1; + var n : usize = at; + while (i < note.len and note[i] != 58) { + out[n] = note[i]; + n = n + 1; + i = i + 1; + } + return n; +} + +/// The first line of card k of home h, as bytes in out; returns the length (0 for an empty slot). +pub fn card_title(h: u32, k: u32, out: []u8) -> usize { + const kind : u32 = card_kind(h, k); + const arg : u32 = card_arg(h, k); + if (kind == CK_AGENT) { + var n : usize = put_bytes(out, 0, AGENT_LETTERS[arg - 1]); + n = put_bytes(out, n, " "); + return put_note_name(out, n, AGENT_NOTES[arg - 1]); + } + if (kind == CK_AGENT_MORE) { + const c : usize = put_bytes(out, 0, "+"); + return put_counted(out, c, arg, "agent", "agents"); + } + if (kind == CK_WIDGETS) { return put_counted(out, 0, arg, "widget", "widgets"); } + if (kind == CK_GROUP) { return put_bytes(out, 0, RUN_ANCHOR[arg]); } + if (kind == CK_GROUP_MORE) { + const c : usize = put_bytes(out, 0, "+"); + return put_counted(out, c, arg, "group", "groups"); + } + if (kind == CK_COURSE) { return put_bytes(out, 0, COURSE_IDS[arg]); } + return 0; +} + +/// The second line of card k of home h. +pub fn card_line(h: u32, k: u32, out: []u8) -> usize { + const kind : u32 = card_kind(h, k); + const arg : u32 = card_arg(h, k); + if (kind == CK_AGENT) { + var n : usize = put_bytes(out, 0, "agent "); + n = put_dec(out, n, @as(u64, arg)); + n = put_bytes(out, n, " of "); + return put_dec(out, n, @as(u64, AGENT_COUNT)); + } + if (kind == CK_AGENT_MORE) { + var n : usize = 0; + var i : u32 = agent_cards(h) - 1; + while (i < agents_in(h)) { + if (i > agent_cards(h) - 1) { n = put_bytes(out, n, " "); } + n = put_bytes(out, n, AGENT_LETTERS[nth_agent(h, i) - 1]); + i = i + 1; + } + return n; + } + if (kind == CK_WIDGETS) { + if (arg == 0) { return put_bytes(out, 0, "none in the gallery"); } + const c : usize = put_bytes(out, 0, "in "); + return put_counted(out, c, runs_in(h), "group", "groups"); + } + if (kind == CK_GROUP) { return put_counted(out, 0, run_size(arg), "widget", "widgets"); } + if (kind == CK_GROUP_MORE) { + var total : u32 = 0; + var i : u32 = runs_in(h) - arg; + while (i < runs_in(h)) { + total = total + run_size(nth_run(h, i)); + i = i + 1; + } + return put_counted(out, 0, total, "widget", "widgets"); + } + if (kind == CK_COURSE) { return put_counted(out, 0, LESSONS_PER_COURSE, "lesson", "lessons"); } + return 0; +} + +/// The third line: only a course has one, its share page under t27.ai. +pub fn card_note(h: u32, k: u32, out: []u8) -> usize { + if (card_kind(h, k) == CK_COURSE) { + const n : usize = put_bytes(out, 0, "t27.ai/"); + return put_bytes(out, n, COURSE_SHARE[card_arg(h, k)]); + } + return 0; +} + + +; --- Tests ----------------------------------------------------------------------------------------------- + +test there_are_eight_homes_with_a_full_row_each { + assert HOME_COUNT == 8; + assert HOME_IDS[H_T27A] == "t27a"; + assert HOME_IDS[H_T27U] == "t27u"; + assert HOME_IDS[H_QUEEN] == "queen"; + assert HOME_NAMES[H_T27T] == "t27t trainer"; + assert HOME_LAWS[H_T27T] == "specs/t27t/t27t.t27"; + assert HOME_LAWS[H_T27A] == "specs/isa/t27a.t27"; + assert HOME_RUNNERS[H_T27O] == "tri t27o [--oracle] [--check]"; + assert HOME_ROLES[H_T27U] == "The university: 7 courses of 27 lessons, each lesson one working widget."; +} + +test every_letter_is_in_exactly_one_home_and_the_counts_add_up { + assert AGENT_COUNT == 27; + var total : u32 = 0; + var h : u32 = 0; + while (h < HOME_COUNT) { + total = total + agents_in(h); + h = h + 1; + } + assert total == 27; + var k : u32 = 0; + while (k < AGENT_COUNT) { + assert AGENT_HOME[k] < HOME_COUNT; + k = k + 1; + } +} + +test the_agent_counts_per_home_are_pinned { + assert agents_in(H_T27A) == 1; + assert agents_in(H_T27B) == 1; + assert agents_in(H_T27C) == 6; + assert agents_in(H_T27F) == 1; + assert agents_in(H_T27O) == 2; + assert agents_in(H_T27T) == 2; + assert agents_in(H_T27U) == 1; + assert agents_in(H_QUEEN) == 13; +} + +test the_letters_that_look_like_homes_are_not_confused { + assert AGENT_LETTERS[1] == "B"; + assert home_of_agent(2) == H_T27C; + assert AGENT_LETTERS[20] == "U"; + assert home_of_agent(21) == H_QUEEN; + assert home_of_agent(9) == H_T27A; + assert home_of_agent(11) == H_T27B; + assert home_of_agent(8) == H_T27T; + assert home_of_agent(26) == H_T27U; + assert home_of_agent(20) == H_QUEEN; + assert home_of_agent(27) == H_QUEEN; + assert AGENT_LETTERS[26] == "Ti"; +} + +test the_ordinals_outside_one_to_27_belong_to_no_home { + assert home_of_agent(0) == HOME_COUNT; + assert home_of_agent(28) == HOME_COUNT; +} + +test every_named_agent_has_a_home { + assert NAMED_COUNT == 12; + var total : u32 = 0; + var h : u32 = 0; + while (h < HOME_COUNT) { + total = total + named_in(h); + h = h + 1; + } + assert total == 12; + assert named_in(H_T27C) == 5; + assert named_in(H_QUEEN) == 7; + assert home_of_named(9) == H_T27C; + assert NAMED_AGENTS[9] == "t27c-steward"; + assert home_of_named(10) == H_QUEEN; + assert home_of_named(12) == HOME_COUNT; +} + +test the_runs_are_ordered_and_cover_the_gallery { + assert RUN_START[0] == 0; + var r : u32 = 1; + while (r < RUN_COUNT) { + assert RUN_START[r] > RUN_START[r - 1]; + assert RUN_HOME[r] != RUN_HOME[r - 1]; + r = r + 1; + } + assert RUN_START[RUN_COUNT - 1] < WIDGET_COUNT; +} + +test every_widget_has_exactly_one_home { + var i : u32 = 0; + while (i < WIDGET_COUNT) { + assert home_of_widget(i) < HOME_COUNT; + i = i + 1; + } + var total : u32 = 0; + var h : u32 = 0; + while (h < HOME_COUNT) { + total = total + widgets_in(h); + h = h + 1; + } + assert total == 195; +} + +test the_widget_counts_per_home_are_pinned_and_recomputed { + assert widgets_in(H_T27A) == 0; + assert widgets_in(H_T27B) == 0; + assert widgets_in(H_T27C) == 127; + assert widgets_in(H_T27F) == 0; + assert widgets_in(H_T27O) == 0; + assert widgets_in(H_T27T) == 6; + assert widgets_in(H_T27U) == 54; + assert widgets_in(H_QUEEN) == 8; +} + +test the_anchor_indices_land_in_the_homes_the_evidence_gives { + assert RUN_ANCHOR[0] == "x7-board"; + assert home_of_widget(0) == H_T27C; + assert home_of_widget(1) == H_T27C; + assert home_of_widget(5) == H_T27C; + assert home_of_widget(11) == H_T27C; + assert home_of_widget(12) == H_T27T; + assert home_of_widget(13) == H_T27C; + assert home_of_widget(28) == H_T27C; + assert home_of_widget(29) == H_T27T; + assert home_of_widget(30) == H_T27T; + assert home_of_widget(31) == H_T27C; + assert home_of_widget(122) == H_T27C; + assert home_of_widget(123) == H_T27T; + assert home_of_widget(125) == H_T27T; + assert home_of_widget(126) == H_T27U; + assert home_of_widget(179) == H_T27U; + assert home_of_widget(180) == H_T27C; + assert home_of_widget(184) == H_T27C; + assert home_of_widget(185) == H_QUEEN; + assert home_of_widget(186) == H_T27C; + assert home_of_widget(187) == H_QUEEN; + assert home_of_widget(189) == H_QUEEN; + assert home_of_widget(190) == H_T27C; + assert home_of_widget(192) == H_QUEEN; + assert home_of_widget(194) == H_QUEEN; +} + +test negative_control_an_index_past_the_gallery_has_no_home { + assert home_of_widget(195) == HOME_COUNT; + assert home_of_widget(4294967295) == HOME_COUNT; + assert home_of_widget(194) != HOME_COUNT; + assert widgets_in(HOME_COUNT) == 0; + assert home_ok(HOME_COUNT) == false; +} + +test the_seven_courses_have_twenty_seven_lessons_each { + assert COURSE_COUNT == 7; + assert LESSONS_PER_COURSE == 27; + assert COURSE_COUNT * LESSONS_PER_COURSE == 189; + assert COURSE_IDS[0] == "t27-basics"; + assert COURSE_IDS[3] == "goldenfloat"; + assert COURSE_IDS[6] == "buses-and-peripherals"; + assert COURSE_SHARE[0] == "learn/"; + assert COURSE_SHARE[4] == "learn/verify-hardware/"; + assert COURSE_ROUTES[1] == "course"; + assert COURSE_SPECS[2] == "specs/course/ai-numbers.t27"; +} + +test the_lesson_widgets_are_in_the_gallery_and_never_repeat { + var k : u32 = 0; + while (k < 189) { + assert COURSE_WIDGETS[k] < WIDGET_COUNT; + var j : u32 = k + 1; + while (j < 189) { + assert COURSE_WIDGETS[k] != COURSE_WIDGETS[j]; + j = j + 1; + } + k = k + 1; + } +} + +test the_courses_that_own_their_lesson_widgets_are_in_t27u { + var l : u32 = 0; + while (l < LESSONS_PER_COURSE) { + assert home_of_widget(course_widget(0, l)) == H_T27U; + assert home_of_widget(course_widget(3, l)) == H_T27U; + assert course_of_widget(course_widget(0, l)) == 0; + assert course_of_widget(course_widget(3, l)) == 3; + l = l + 1; + } + assert course_widget(0, 0) == 126; + assert course_widget(3, 26) == 179; + assert course_widget(2, 0) == 29; +} + +test six_widgets_are_opened_by_no_lesson { + assert widgets_without_course() == 6; + assert course_of_widget(187) == NO_COURSE; + assert course_of_widget(94) == NO_COURSE; + assert course_of_widget(66) == NO_COURSE; + assert course_of_widget(0) == 1; + assert course_of_widget(192) == 1; + assert course_widget(7, 0) == WIDGET_COUNT; + assert course_widget(0, 27) == WIDGET_COUNT; +} + +test chat_is_the_third_petal_and_the_homes_take_nineteen_to_twenty_five { + assert PETAL_CHAT == 2; + assert petal_of_home(H_T27A) == 19; + assert petal_of_home(H_T27B) == 20; + assert petal_of_home(H_T27C) == 21; + assert petal_of_home(H_T27F) == 22; + assert petal_of_home(H_T27O) == 23; + assert petal_of_home(H_T27T) == 24; + assert petal_of_home(H_T27U) == 25; + assert petal_of_home(H_QUEEN) == PETAL_CHAT; + assert petal_of_home(HOME_COUNT) == PETAL_NONE; +} + +test home_of_petal_inverts_petal_of_home_and_the_rest_have_no_home { + var h : u32 = 0; + while (h < HOME_COUNT) { + assert home_of_petal(petal_of_home(h)) == h; + h = h + 1; + } + var n : u32 = 0; + var p : u32 = 0; + while (p < PETAL_COUNT) { + if (home_of_petal(p) < HOME_COUNT) { n = n + 1; } + p = p + 1; + } + assert n == 8; + assert home_of_petal(26) == HOME_COUNT; + assert home_of_petal(0) == HOME_COUNT; + assert home_of_petal(18) == HOME_COUNT; + assert home_of_petal(27) == HOME_COUNT; +} + +test the_product_homes_sit_in_the_third_realm_and_about_stays_last { + var h : u32 = 0; + while (h < H_QUEEN) { + assert HOME_PETAL[h] >= 19; + assert HOME_PETAL[h] <= 25; + assert HOME_PETAL[h] / 9 == 2; + h = h + 1; + } + assert home_of_petal(26) != H_T27U; +} + +/// Is field `petal` of the comma-separated list `names` exactly `word`? (the tests' reader of WORLD_NAMES) +fn world_is(names: []const u8, petal: u32, word: []const u8) -> bool { + var b : [32]u8 = undefined; + const n : usize = csv_field(names, petal, b[0..32]); + if (n != word.len) { return false; } + var i : usize = 0; + while (i < n) { + if (b[i] != word[i]) { return false; } + i = i + 1; + } + return true; +} + +test the_world_names_of_petal_menu_put_chat_third_and_name_every_home { + assert world_is(WORLD_NAMES, PETAL_CHAT, "CHAT"); + assert world_is(WORLD_NAMES, 0, "LOGS"); + assert world_is(WORLD_NAMES, 19, "T27A"); + assert world_is(WORLD_NAMES, 20, "T27B"); + assert world_is(WORLD_NAMES, 21, "T27C"); + assert world_is(WORLD_NAMES, 22, "T27F"); + assert world_is(WORLD_NAMES, 23, "T27O"); + assert world_is(WORLD_NAMES, 24, "T27T"); + assert world_is(WORLD_NAMES, 25, "T27U"); + assert world_is(WORLD_NAMES, 26, "ABOUT"); + assert world_is(WORLD_NAMES, 16, "DePIN NODE"); + assert world_is(WORLD_NAMES, 1, "CODE"); + assert world_is(WORLD_NAMES, 18, "DOCS"); + assert world_is(WORLD_NAMES, 27, "ABOUT") == false; + assert world_is(WORLD_NAMES, 2, "LOGS") == false; + assert world_is(WORLD_NAMES, 0, "CHAT") == false; + assert world_is(WORLD_NAMES, 19, "REELS") == false; +} + +fn same(out: []const u8, n: usize, want: []const u8) -> bool { + if (n != want.len) { return false; } + var i : usize = 0; + while (i < n) { + if (out[i] != want[i]) { return false; } + i = i + 1; + } + return true; +} + +test every_home_has_a_card_count_that_fits_nine_slots { + var h : u32 = 0; + while (h < HOME_COUNT) { + assert home_cards(h) >= 1; + assert home_cards(h) <= HOME_CARD_SLOTS; + h = h + 1; + } + assert home_cards(H_T27A) == 2; + assert home_cards(H_T27B) == 2; + assert home_cards(H_T27C) == 9; + assert home_cards(H_T27F) == 2; + assert home_cards(H_T27O) == 3; + assert home_cards(H_T27T) == 6; + assert home_cards(H_T27U) == 9; + assert home_cards(H_QUEEN) == 8; + assert home_cards(HOME_COUNT) == 0; + assert card_kind(H_T27A, 2) == CK_NONE; + assert card_kind(H_T27U, 9) == CK_NONE; +} + +test the_card_kinds_follow_the_fixed_order { + assert card_kind(H_T27A, 0) == CK_AGENT; + assert card_kind(H_T27A, 1) == CK_WIDGETS; + assert card_kind(H_T27C, 0) == CK_AGENT; + assert card_kind(H_T27C, 2) == CK_AGENT; + assert card_kind(H_T27C, 3) == CK_AGENT_MORE; + assert card_kind(H_T27C, 4) == CK_WIDGETS; + assert card_kind(H_T27C, 5) == CK_GROUP; + assert card_kind(H_T27C, 7) == CK_GROUP; + assert card_kind(H_T27C, 8) == CK_GROUP_MORE; + assert card_kind(H_T27U, 0) == CK_AGENT; + assert card_kind(H_T27U, 1) == CK_WIDGETS; + assert card_kind(H_T27U, 2) == CK_COURSE; + assert card_kind(H_T27U, 8) == CK_COURSE; + assert card_kind(H_QUEEN, 3) == CK_AGENT_MORE; + assert card_kind(H_QUEEN, 5) == CK_GROUP; + assert card_kind(H_QUEEN, 7) == CK_GROUP; +} + +test the_run_walk_covers_the_gallery { + var total : u32 = 0; + var r : u32 = 0; + while (r < RUN_COUNT) { + total = total + run_size(r); + r = r + 1; + } + assert total == WIDGET_COUNT; + assert run_size(RUN_COUNT) == 0; + assert runs_in(H_T27C) == 6; + assert runs_in(H_T27T) == 3; + assert runs_in(H_T27U) == 1; + assert runs_in(H_QUEEN) == 3; + assert runs_in(H_T27A) == 0; + assert nth_run(H_T27C, 0) == 0; + assert nth_run(H_T27C, 5) == 11; + assert nth_run(H_T27C, 6) == RUN_COUNT; + assert nth_run(H_T27U, 0) == 6; + var h : u32 = 0; + while (h < HOME_COUNT) { + var w : u32 = 0; + var n : u32 = 0; + while (n < runs_in(h)) { + w = w + run_size(nth_run(h, n)); + n = n + 1; + } + assert w == widgets_in(h); + h = h + 1; + } +} + +test the_agent_cards_name_the_letter_and_the_name { + var b : [64]u8 = undefined; + var n : usize = card_title(H_T27B, 0, b[0..64]); + assert same(b[0..64], n, "K Kernel / FPGA MAC"); + n = card_line(H_T27B, 0, b[0..64]); + assert same(b[0..64], n, "agent 11 of 27"); + n = card_title(H_T27A, 0, b[0..64]); + assert same(b[0..64], n, "I ISA"); + n = card_title(H_T27U, 0, b[0..64]); + assert same(b[0..64], n, "Z Zero-Touch UX / Docs"); + n = card_title(H_T27C, 3, b[0..64]); + assert same(b[0..64], n, "+3 agents"); + n = card_line(H_T27C, 3, b[0..64]); + assert same(b[0..64], n, "L R V"); + n = card_title(H_QUEEN, 3, b[0..64]); + assert same(b[0..64], n, "+10 agents"); +} + +test the_widget_cards_say_the_real_counts_including_zero { + var b : [64]u8 = undefined; + var n : usize = card_title(H_T27A, 1, b[0..64]); + assert same(b[0..64], n, "0 widgets"); + n = card_line(H_T27A, 1, b[0..64]); + assert same(b[0..64], n, "none in the gallery"); + n = card_title(H_T27C, 4, b[0..64]); + assert same(b[0..64], n, "127 widgets"); + n = card_line(H_T27C, 4, b[0..64]); + assert same(b[0..64], n, "in 6 groups"); + n = card_title(H_T27C, 5, b[0..64]); + assert same(b[0..64], n, "x7-board"); + n = card_line(H_T27C, 5, b[0..64]); + assert same(b[0..64], n, "12 widgets"); + n = card_title(H_T27C, 8, b[0..64]); + assert same(b[0..64], n, "+3 groups"); + n = card_title(H_T27T, 2, b[0..64]); + assert same(b[0..64], n, "6 widgets"); + n = card_title(H_T27U, 1, b[0..64]); + assert same(b[0..64], n, "54 widgets"); + n = card_line(H_T27U, 1, b[0..64]); + assert same(b[0..64], n, "in 1 group"); +} + +test the_course_cards_give_id_lessons_and_share_path { + var b : [64]u8 = undefined; + var n : usize = card_title(H_T27U, 2, b[0..64]); + assert same(b[0..64], n, "t27-basics"); + n = card_line(H_T27U, 2, b[0..64]); + assert same(b[0..64], n, "27 lessons"); + n = card_note(H_T27U, 2, b[0..64]); + assert same(b[0..64], n, "t27.ai/learn/"); + n = card_title(H_T27U, 8, b[0..64]); + assert same(b[0..64], n, "buses-and-peripherals"); + n = card_note(H_T27U, 8, b[0..64]); + assert same(b[0..64], n, "t27.ai/learn/buses-and-peripherals/"); + n = card_note(H_T27U, 1, b[0..64]); + assert n == 0; +} + +test negative_control_an_empty_slot_has_no_text { + var b : [64]u8 = undefined; + assert card_title(H_T27A, 5, b[0..64]) == 0; + assert card_line(H_T27A, 5, b[0..64]) == 0; + assert card_note(H_T27A, 5, b[0..64]) == 0; + assert card_title(HOME_COUNT, 0, b[0..64]) == 0; +}