Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions specs/OWNERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
| `ui/` | **T-Queen** | The site's viewport contract as a `.t27` spec (`ui/viewport.t27`, `KIND = "viewport"`): tiers by CSS viewport width, panes per tier, the 44 px touch minimum, the six-size QA matrix and the Queen rail capacity read from `Queen.css`; the site (gHashTag/trinity) generates `viewport.generated.ts` / `.css` from a vendored copy and evaluates the spec's own test blocks; `ui/queen_evidence.t27` (`module ui_queen_evidence`): the evidence constants of the Queen's FFN and Ternary Check panel, from which the site generates `queenEvidence.generated.ts`; canonical here since S10, byte-identical to the copy the site carried alone |
| `catalog/` | **T-Queen** | How the site's spec catalog and the Queen map find the repositories that write `.t27` (`catalog/discovery.t27`, `KIND = "discovery"`): the GitHub owners scanned, the exclusions, what makes a candidate qualify (a declaration in the compiler's AST, not a file extension) and the limits; the site (gHashTag/trinity) vendors a byte-identical copy and `scripts/discover-t27-worlds.mjs` reads it through the compiler wasm before touching GitHub |
| `t27f/` | **T-Queen** | T27F, the visual spec of a .t27 file (gHashTag/trinity#1648, RFC #1649; definition in `.claude/skills/t27f/SKILL.md`): `t27f/spec_graph.t27` (module `t27f_spec_graph`): the core graph IR a spec becomes -- node and edge kinds, inference labels, source spans, `validate_graph`, source mapping both ways and the node -> ui_plan widget binding, with one hand-read fixture; `t27f/spec_graph_view.t27` (module `t27f_spec_graph_view`): the graph view, a deterministic layout of a spec graph -- one column per node kind, style and row order from the inference label, UNKNOWN nodes drawn alone, every edge with a source line; numbers only, a host draws them; `t27f/ui_plan.t27` (module `t27f_ui_plan`): the renderer-neutral UI plan IR and the trusted widget catalog. Applications built on it: the topic-to-value map of gHashTag/trinity#1677 (`t27f/topic_engine.t27`, module `T27fTopicEngine`): the 3 x 9 = 27 lesson shape, intake and gap rules, keyword routing to one domain, the Bee dispatch cap, the evidence and approval gates, and progression that is never money; driven by the Claude Code skill `.claude/skills/t27f/SKILL.md` through the t27 MCP server; `t27f/reel.t27` (module `T27fReel`): the video format of a topic map and the still views of a Claude Code answer -- honeycomb geometry, focus+context camera, scene catalog, per-target limits, gap styling, the posting gate and the channel per host; numbers only, a host draws them; `t27f/deck.t27` (module `T27fDeck`): the default look of a reel, every scene a presentation slide -- layouts per scene, regions, type sizes, glass card, palette kept off the status colours, contrast and card fades |
| `t27mcp/` | **T-Queen** | The decisions of the t27 MCP server (gHashTag/t27-mcp) as `.t27` specs, compiled to wasm that the server runs: `t27mcp/check_hints.t27` (module `T27mcpCheckHints`): which short "write this instead" hint a compiler message gets for foreign syntax (`in` as a name, `as u3`, `x++`, `a ? b : c`, `match`), and the line number a message names; the server's JavaScript is I/O glue and holds no rule |
| `trinity/` | **T-Queen** + **A-Architect** | The project manifest of gHashTag/trinity as `.t27` specs (`trinity/project.t27`, `KIND = "project"`; `trinity/capabilities/<id>.t27`, `KIND = "capability"`): the pinned consumer revision, profiles, dispositions and evidence tags, dialect and build counts, dependency pins and the work packages of gHashTag/trinity#988; each card names its owner, canonical spec, implementation and generated paths, build targets, acceptance command and evidence; `tools/trinity_manifest.py` derives the inventory from a clean pinned checkout and holds the cards to it; `trinity/compiler_matrix.t27` (`KIND = "compiler-matrix"`) names the pinned compiler, the backends, the stages and the features of the executable subset with their fixtures under `bootstrap/tests/fixtures/trinity_matrix/`, and `tools/trinity_compiler_matrix.py` measures them; `trinity/build_graph.t27` (`KIND = "build-graph"`) declares the consumer's toolchain, pins, profiles, generated-file boundaries and receipt policy, and `tools/trinity_build_receipt.py` derives the graph, writes the receipts and judges drift; the consumer vendors a byte-identical copy and generates its manifest from it (S12). Shared contracts stay in their own subtrees (`numeric/`, `vsa/`, `queen/`, `tools/`, `docs/`, `ui/`) and are cited, never copied |

Each subtree with substantial churn should keep a local **`OWNERS.md`** (see below).
Expand Down
205 changes: 205 additions & 0 deletions specs/t27mcp/check_hints.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: Apache-2.0
; specs/t27mcp/check_hints.t27 -- t27_check of the t27 MCP server (gHashTag/t27-mcp): what to tell a model
; that wrote foreign syntax. The compiler's messages are exact but written for compiler authors; this spec
; maps the ones a model really triggers to a short "write this instead", and reads the line number out of a
; message. See #10073 (epic #10066).
; --
; Compiled to wasm (`t27c gen-c`, then `zig cc -target wasm32-wasi`), the server calls hint_code() and line_of()
; with the compiler's message and nothing else; `t27c gen-js` supplies HINTS. The server's JavaScript holds no
; rule. What is NOT restated here: the parser and the typechecker (bindings/wasm-explorer runs them as wasm
; and produces the messages), and the worked examples (t27mcp/examples.t27).
; --
; The rules:
; - A message is matched by the text the compiler really emits (each test quotes one), in a fixed order;
; the first rule that matches wins. A message no rule knows gets H_NONE: the server prints the compiler's
; own words and invents nothing. A clean run has no message, so it gets H_NONE too.
; - Constructs that parse and typecheck (`let`, `&&`, `||`, Zig-style signatures) get no hint: the compiler
; accepts them, and a hint would be a claim about taste, not about an error.
; - line_of() is the number after the first `line ` in a message; failing that, the number after a final `:`
; (the typechecker's warnings end `... is declared:2`); failing that, 0.
; phi^2 + 1/phi^2 = 3 | TRINITY

module T27mcpCheckHints;

/// hint_code() answers; HINTS[code] is the text to show.
pub const H_NONE : u32 = 0;
pub const H_KEYWORD_IN : u32 = 1;
pub const H_CAST : u32 = 2;
pub const H_MATCH : u32 = 3;
pub const H_INCREMENT : u32 = 4;
pub const H_TERNARY : u32 = 5;
pub const H_TOP_LEVEL : u32 = 6;
pub const H_BRACE : u32 = 7;
pub const H_WIDTH : u32 = 8;
pub const H_UNKNOWN_TYPE : u32 = 9;
pub const H_COUNT : u32 = 10;

pub const HINTS : [10]str = [
"",
"`in` is a reserved keyword, so it cannot name a port, parameter or variable. Rename it (din, data_in) everywhere it appears.",
"Casts reach only bool u8 i8 u16 i16 u32 i32 u64 i64 usize f32 f64: there is no `as u3`. Keep a wider type and mask the value, e.g. `x & 7`.",
"t27 has no `match`. Write `return switch (x) { 0 => a, else => b };`: the arms are expressions and `else =>` closes the switch.",
"An expression ended early at `;`. t27 has no `x++` or `x--`: write `x = x + 1;`. Otherwise an operand is missing before the `;`.",
"t27 has no `c ? a : b`. Declare `var r : T = b;`, then `if (c) { r = a; }`, and use r.",
"A top-level item was not recognised. `impl`, `class`, `trait` and `#include` do not exist: write `pub fn name(args) -> T { .. }` and pass the value as a parameter.",
"A `{` where an expression was expected. A switch arm is `pattern => expr,` with no block, and the switch is returned: `return switch (x) { 0 => a, else => b };`.",
"Types of different widths do not convert on their own. Convert with `as u8` (or another type from the cast list: bool u8 i8 u16 i16 u32 i32 u64 i64 usize f32 f64), or declare the wider type.",
"Unknown type. Scalars are bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64; text is `string` (`str` in a const table); an array is `[N]T`."
];

/// find() answers when the needle does not occur.
pub const NO_POS : u32 = 4294967295;

/// Byte offset of the first occurrence of `needle` in `hay`, or NO_POS.
pub fn find(hay: string, needle: string) -> u32 {
var hn : u32 = (hay.len as u32);
var nn : u32 = (needle.len as u32);
if (nn > hn) { return NO_POS; }
var i : u32 = 0;
while (i <= hn - nn) {
var j : u32 = 0;
var same : u32 = 1;
while (j < nn) {
if ((hay[i + j] as u32) != (needle[j] as u32)) { same = 0; }
j = j + 1;
}
if (same == 1) { return i; }
i = i + 1;
}
return NO_POS;
}

/// 1 when `needle` occurs in `hay`.
pub fn has(hay: string, needle: string) -> u32 {
if (find(hay, needle) == NO_POS) { return 0; }
return 1;
}

/// The hint code for one compiler message (parse error, typecheck error or warning).
pub fn hint_code(msg: string) -> u32 {
if (has(msg, "KwIn (") == 1) { return H_KEYWORD_IN; }
if (has(msg, "unknown cast target type") == 1) { return H_CAST; }
if (has(msg, "`match` block") == 1) { return H_MATCH; }
if (has(msg, "Unexpected token in expression: Semicolon") == 1) { return H_INCREMENT; }
if (has(msg, "after expression statement: Colon") == 1) { return H_TERNARY; }
if (has(msg, "at module level") == 1) {
if (has(msg, "after expression statement") == 1) { return H_TOP_LEVEL; }
}
if (has(msg, "Unexpected token in expression: LBrace") == 1) { return H_BRACE; }
if (has(msg, "where") == 1) {
if (has(msg, "is declared") == 1) { return H_WIDTH; }
}
if (has(msg, "unknown type `") == 1) { return H_UNKNOWN_TYPE; }
return H_NONE;
}

/// The typechecker's warnings end in `:N` (`... is declared:2`): N, or 0 when the message does not.
pub fn trailing_line(msg: string) -> u32 {
var len : u32 = (msg.len as u32);
var k : u32 = len;
var n : u32 = 0;
var scale : u32 = 1;
while (k > 0) {
var c : u32 = (msg[k - 1] as u32);
if (c < 48) { break; }
if (c > 57) { break; }
n = n + (c - 48) * scale;
scale = scale * 10;
k = k - 1;
}
if (k == len) { return 0; }
if (k == 0) { return 0; }
if ((msg[k - 1] as u32) != 58) { return 0; }
return n;
}

/// The line number a message names (`near line 2`, `at line 3`), or 0.
pub fn line_of(msg: string) -> u32 {
var p : u32 = find(msg, "line ");
if (p == NO_POS) { return trailing_line(msg); }
var k : u32 = p + 5;
var n : u32 = 0;
var len : u32 = (msg.len as u32);
while (k < len) {
var c : u32 = (msg[k] as u32);
if (c < 48) { return n; }
if (c > 57) { return n; }
n = n * 10 + (c - 48);
k = k + 1;
}
return n;
}

test "find: first offset, and NO_POS when absent" {
assert find("abcabc", "ca") == 2;
assert find("abc", "abcd") == NO_POS;
assert find("abc", "x") == NO_POS;
}

test "line_of: the number after the first `line `" {
assert line_of("parse error in fn 'f' near line 2: unknown cast target type `u3`") == 2;
assert line_of("function 'g' expects 1 args, got 2 at line 13") == 13;
assert line_of("warning: returns U16 where U8 is declared:2") == 2;
assert line_of("warning: returns U16 where U8 is declared:117") == 117;
assert line_of("warning: unknown type `String` in parameter `a` of `f`") == 0;
assert line_of("12345") == 0;
assert line_of("") == 0;
}

test "keyword in: the message the parser really emits for an `in` port" {
assert hint_code("parse error in fn 'f' near line 2: Unexpected token in expression: KwIn ('in') at line 2:33") == H_KEYWORD_IN;
}

test "cast: `as u3` and `as u12` are not on the whitelist" {
assert hint_code("parse error in fn 'f' near line 2: unknown cast target type `u3`; expected one of [bool, u8]") == H_CAST;
assert hint_code("parse error in fn 'f' near line 2: unknown cast target type `u12`; expected one of [bool, u8]") == H_CAST;
}

test "match: a typecheck error, not a parse error" {
assert hint_code("`match` block at line 2 in fn `f` is Rust; t27 has no `match`") == H_MATCH;
}

test "increment: `x++;` ends the expression at the semicolon" {
assert hint_code("parse error in fn 'f' near line 2: Unexpected token in expression: Semicolon (';') at line 2:38") == H_INCREMENT;
}

test "ternary: a C conditional leaves a Colon behind" {
assert hint_code("parse error in fn 'f' near line 2: unexpected token after expression statement: Colon") == H_TERNARY;
}

test "top level: impl and friends" {
assert hint_code("parse error at module level near line 3: unexpected token after expression statement: Ident") == H_TOP_LEVEL;
}

test "brace: a Zig-style switch arm block" {
assert hint_code("parse error in fn 'f' near line 3: Unexpected token in expression: LBrace ('{') at line 3:23") == H_BRACE;
}

test "width: the typechecker's warning" {
assert hint_code("warning: returns U16 where U8 is declared:2") == H_WIDTH;
}

test "unknown type: String is not a t27 type" {
assert hint_code("warning: unknown type `String` in parameter `a` of `f`") == H_UNKNOWN_TYPE;
}

test "negative control: a clean run and unrelated messages get no hint" {
assert hint_code("") == H_NONE;
assert hint_code("function 'g' expects 1 args, got 2 at line 3") == H_NONE;
assert hint_code("unterminated string literal near line 4") == H_NONE;
assert hint_code("Unexpected token in expression: Plus") == H_NONE;
}

test "every hint code has the text its rule promises, and H_NONE has none" {
assert HINTS[H_NONE] == "";
assert has(HINTS[H_KEYWORD_IN], "Rename") == 1;
assert has(HINTS[H_CAST], "x & 7") == 1;
assert has(HINTS[H_MATCH], "switch") == 1;
assert has(HINTS[H_INCREMENT], "x = x + 1") == 1;
assert has(HINTS[H_TERNARY], "if (c)") == 1;
assert has(HINTS[H_TOP_LEVEL], "pub fn") == 1;
assert has(HINTS[H_BRACE], "return switch") == 1;
assert has(HINTS[H_WIDTH], "as u8") == 1;
assert has(HINTS[H_UNKNOWN_TYPE], "string") == 1;
assert has(HINTS[H_UNKNOWN_TYPE], "no such text") == 0;
}
Loading