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
2 changes: 1 addition & 1 deletion specs/OWNERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +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 |
| `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; `t27mcp/examples.t27` (module `T27mcpExamples`): which short slice of the published corpus, pinned to one commit, answers a question about clocked or combinational modules, `fn`, `test` blocks and the reset styles; `t27mcp/spec_brief.t27` (module `T27mcpSpecBrief`): which manifest fields a short spec row keeps and why a failing spec fails (a bit set of reasons plus their text templates); 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
253 changes: 253 additions & 0 deletions specs/t27mcp/examples.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// SPDX-License-Identifier: Apache-2.0
; specs/t27mcp/examples.t27 -- t27_examples of the t27 MCP server (gHashTag/t27-mcp): which short slice of the
; published corpus answers a question a model asks while it writes .t27. See #10081 (epic #10066).
; --
; A model that learns the idioms from failed compiles pays one round trip each: a clocked module has no port
; list, combinational logic is `fn on_comb`, the reset is implicit. The corpus already shows each of them in a
; small tested spec, but t27_spec returns the whole file. This spec holds the pointers; the server fetches
; the file at the pinned commit, cuts the lines and prints them.
; --
; The rules:
; - topic_code() maps the words of a question (already lower case) to one topic, in a fixed order, the first
; rule that matches wins. A question no rule knows gets T_NONE, and the server then lists the topics and
; invents no example. `async` contains `sync`, so the async rule comes first.
; - pick(topic, k) is the row of the k-th example of a topic, or NO_ROW. A topic has one to three rows.
; - A row is path + from..to lines of the pinned file, an optional module line (a slice that starts below the
; `module` line is not a file) and an optional `use` line and footer line (a braced module needs its closing
; brace); the slice is at most MAX_LINES lines. 0 means "no such line".
; - PIN is one commit of this repository. A pointer cannot drift when a file changes on master.
; Every row was run with `t27c test-report` on the lab at PIN: parse and typecheck clean and, where the slice
; has tests, all passing with 0 vacuous; `why` says "no tests" for the one slice that has none.
; phi^2 + 1/phi^2 = 3 | TRINITY

module T27mcpExamples;

/// The commit of gHashTag/t27 every row points into.
pub const PIN : str = "a18d7ee14a9994ef9dc84dac10e1a2944367d4c5";

pub const T_NONE : u32 = 0;
pub const T_CLOCKED : u32 = 1;
pub const T_COMB : u32 = 2;
pub const T_FN : u32 = 3;
pub const T_TEST : u32 = 4;
pub const T_RESET_ASYNC : u32 = 5;
pub const T_RESET_SYNC : u32 = 6;
pub const T_RESET_NONE : u32 = 7;
pub const T_COUNT : u32 = 8;

pub const TOPICS : [8]str = [
"",
"clocked",
"comb",
"fn",
"test",
"reset_async",
"reset_sync",
"reset_none"
];

pub const NO_ROW : u32 = 4294967295;
pub const NO_POS : u32 = 4294967295;
pub const MAX_LINES : u32 = 40;
pub const ROWS : u32 = 10;

pub const EX_TOPIC : [10]u32 = [1, 1, 2, 2, 3, 4, 4, 5, 6, 7];
pub const EX_PATH : [10]str = [
"specs/ternary/clocked_counter.t27",
"specs/fpga/ro_race.t27",
"specs/verified/poc/a/slot.t27",
"specs/ternary/gft_relu.t27",
"specs/ternary/ternary_mac.t27",
"specs/port/trinity/fpga/openxc7-synth/uart_echo.t27",
"specs/verified/poc/a/slot.t27",
"specs/ternary/clocked_counter.t27",
"specs/fpga/reset_sync.t27",
"specs/fpga/ro_race.t27"
];
pub const EX_FROM : [10]u32 = [25, 16, 8, 10, 1, 6, 8, 1, 21, 16];
pub const EX_TO : [10]u32 = [37, 46, 18, 24, 26, 35, 18, 37, 58, 46];
pub const EX_MODULE : [10]u32 = [1, 2, 0, 1, 0, 0, 0, 0, 17, 2];
pub const EX_USE : [10]u32 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
pub const EX_FOOTER : [10]u32 = [0, 0, 0, 0, 0, 0, 0, 0, 257, 0];
pub const EX_WHY : [10]str = [
"Smallest clocked module: a module-level var is the register, fn on_clock() is the clock edge, the test steps it. No port list, no always, no clk.",
"Two registers, a const bound, a branch in on_clock, and tests that set registers by assignment before stepping the clock.",
"Smallest combinational module: pub fn on_comb(x: u8) -> u8 and a test with an assert statement.",
"A real on_comb with an if-chain and four assert_eq tests; the params are the inputs and the return value is the output.",
"Module header, pub fns with typed params and an arrow return, a loop, and four assert_eq tests.",
"Tests inside a braced module, written assert(cond); the assert_eq and assert x == y forms are in the other examples.",
"The test form `test \"name\" { assert x == y; }` with a quoted name.",
"The default reset needs no code. The header says on_clock lowers to an async active-low reset: each var takes its declared init on !rst_n, and en gates the body.",
"ResetKind (sync, async_assert_sync_release, async_assert_async_release) and a shift-register model of the release path. A sync reset is ordinary logic, not a module switch. No tests in this slice.",
"The header states that nothing here is reset: the counters start from their var initialisers."
];

/// 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 topic a lower-case question is about, or T_NONE.
pub fn topic_code(q: string) -> u32 {
if (has(q, "reset") == 1) {
if (has(q, "async") == 1) { return T_RESET_ASYNC; }
if (has(q, "no reset") == 1) { return T_RESET_NONE; }
if (has(q, "without") == 1) { return T_RESET_NONE; }
if (has(q, "none") == 1) { return T_RESET_NONE; }
if (has(q, "sync") == 1) { return T_RESET_SYNC; }
return T_RESET_ASYNC;
}
if (has(q, "comb") == 1) { return T_COMB; }
if (has(q, "test") == 1) { return T_TEST; }
if (has(q, "assert") == 1) { return T_TEST; }
if (has(q, "clock") == 1) { return T_CLOCKED; }
if (has(q, "sequential") == 1) { return T_CLOCKED; }
if (has(q, "register") == 1) { return T_CLOCKED; }
if (has(q, "counter") == 1) { return T_CLOCKED; }
if (has(q, "fn") == 1) { return T_FN; }
if (has(q, "func") == 1) { return T_FN; }
return T_NONE;
}

/// The row of the k-th example of `topic`, or NO_ROW.
pub fn pick(topic: u32, k: u32) -> u32 {
var i : u32 = 0;
var seen : u32 = 0;
while (i < ROWS) {
if (EX_TOPIC[i] == topic) {
if (seen == k) { return i; }
seen = seen + 1;
}
i = i + 1;
}
return NO_ROW;
}

/// Lines the rebuilt slice has: from..to plus the module, use and footer lines that are present.
pub fn row_lines(i: u32) -> u32 {
var n : u32 = EX_TO[i] + 1 - EX_FROM[i];
if (EX_MODULE[i] != 0) { n = n + 1; }
if (EX_USE[i] != 0) { n = n + 1; }
if (EX_FOOTER[i] != 0) { n = n + 1; }
return n;
}

/// 1 when a row can be rebuilt: a topic of the table, a non-empty range, at most MAX_LINES lines.
pub fn row_ok(topic: u32, from: u32, to: u32, lines: u32) -> u32 {
if (topic == T_NONE) { return 0; }
if (topic >= T_COUNT) { return 0; }
if (from == 0) { return 0; }
if (to < from) { return 0; }
if (lines > MAX_LINES) { return 0; }
return 1;
}

/// How many rows of the table fail row_ok.
pub fn bad_rows() -> u32 {
var i : u32 = 0;
var bad : u32 = 0;
while (i < ROWS) {
bad = bad + 1 - row_ok(EX_TOPIC[i], EX_FROM[i], EX_TO[i], row_lines(i));
i = i + 1;
}
return bad;
}

/// How many rows a topic has.
pub fn rows_of(topic: u32) -> u32 {
var i : u32 = 0;
var n : u32 = 0;
while (i < ROWS) {
if (EX_TOPIC[i] == topic) { n = n + 1; }
i = i + 1;
}
return n;
}

test "topic_code: the words of a question" {
assert topic_code("clocked counter") == T_CLOCKED;
assert topic_code("how do i write a register") == T_CLOCKED;
assert topic_code("combinational logic") == T_COMB;
assert topic_code("test block") == T_TEST;
assert topic_code("assert") == T_TEST;
assert topic_code("fn") == T_FN;
assert topic_code("function with a loop") == T_FN;
}

test "topic_code: async is not taken for sync, and a bare reset is the default one" {
assert topic_code("async reset") == T_RESET_ASYNC;
assert topic_code("sync reset") == T_RESET_SYNC;
assert topic_code("reset") == T_RESET_ASYNC;
assert topic_code("no reset") == T_RESET_NONE;
assert topic_code("a module without reset") == T_RESET_NONE;
}

test "negative control: a question no rule knows gets no topic and no row" {
assert topic_code("") == T_NONE;
assert topic_code("axi4 burst") == T_NONE;
assert pick(T_NONE, 0) == NO_ROW;
}

test "pick: the k-th row of a topic, in table order, and NO_ROW past the end" {
assert pick(T_CLOCKED, 0) == 0;
assert pick(T_CLOCKED, 1) == 1;
assert pick(T_CLOCKED, 2) == NO_ROW;
assert pick(T_FN, 0) == 4;
assert pick(T_FN, 1) == NO_ROW;
assert pick(T_RESET_SYNC, 0) == 8;
assert pick(T_COUNT, 0) == NO_ROW;
}

test "every topic has one to three rows" {
assert rows_of(T_CLOCKED) == 2;
assert rows_of(T_COMB) == 2;
assert rows_of(T_FN) == 1;
assert rows_of(T_TEST) == 2;
assert rows_of(T_RESET_ASYNC) == 1;
assert rows_of(T_RESET_SYNC) == 1;
assert rows_of(T_RESET_NONE) == 1;
assert rows_of(T_NONE) == 0;
}

test "every row of the table can be rebuilt" {
assert bad_rows() == 0;
assert row_lines(0) == 14;
assert row_lines(8) == 40;
}

test "negative control: row_ok refuses the rows it must" {
assert row_ok(T_CLOCKED, 25, 37, 14) == 1;
assert row_ok(T_NONE, 25, 37, 14) == 0;
assert row_ok(T_COUNT, 25, 37, 14) == 0;
assert row_ok(T_CLOCKED, 0, 37, 14) == 0;
assert row_ok(T_CLOCKED, 37, 25, 14) == 0;
assert row_ok(T_CLOCKED, 1, 41, 41) == 0;
assert row_ok(T_CLOCKED, 1, 40, 40) == 1;
}

test "the pin and the topic names" {
assert has(PIN, "a18d7ee1") == 1;
assert PIN.len == 40;
assert has(TOPICS[T_RESET_SYNC], "sync") == 1;
assert TOPICS[T_NONE] == "";
}
101 changes: 101 additions & 0 deletions specs/t27mcp/spec_brief.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: Apache-2.0
; specs/t27mcp/spec_brief.t27 -- the brief spec row and the "why does it fail" decision of the t27 MCP server
; (gHashTag/t27-mcp). See the epic #10066.
; --
; t27_specs, t27_spec and t27_grep answer with a spec row. The whole manifest record is about 860 bytes (AST node
; kinds, emitted bytes per backend, tags, a summary), and a model that only wants "does it compile, and if not
; why" pays for all of it on every row. This spec holds the two decisions that were JavaScript in the server:
; - BRIEF_FIELDS / ONE_FIELDS: which manifest fields a brief row keeps. The server copies those keys and nothing
; else; `fields: true` on the tool returns the whole record instead.
; - why_mask(): `health: "fail"` covers unrelated events. A spec that never parsed reports loss 0, tcErrors 0 and
; no failed backend, not because it is clean but because nothing downstream of the parser ran. why_mask turns
; the observable counters into a bit set; WHY_TEXT holds one template per bit, `{}` marks the number or list
; the server fills in. A spec that is not "fail" gets no reasons at all.
; The mask adds no claim of its own: it reads the counters the manifest already records.
; phi^2 + 1/phi^2 = 3 | TRINITY

module T27mcpSpecBrief;

pub const BRIEF_FIELDS : [8]str = [
"path",
"repo",
"name",
"health",
"lines",
"loss",
"tcErrors",
"failedBackends"
];

/// t27_spec keeps the brief fields and what a reader asks next about one file.
pub const ONE_FIELDS : [14]str = [
"path",
"repo",
"name",
"health",
"lines",
"loss",
"tcErrors",
"failedBackends",
"category",
"bytes",
"tokens",
"nodes",
"summary",
"tags"
];

pub const W_NOT_PARSED : u32 = 1;
pub const W_BACKEND : u32 = 2;
pub const W_TYPECHECK : u32 = 4;
pub const W_LOSS : u32 = 8;
pub const W_UNKNOWN : u32 = 16;

/// One template per bit, in bit order. `{}` is the count or the backend list.
pub const WHY_TEXT : [5]str = [
"not-parsed: the manifest records no AST, so no backend ran",
"backend: {} returned nothing",
"typecheck: {} error(s)",
"round-trip loss: {}",
"unknown: marked fail with no counter to explain it"
];

/// The reasons a spec fails, as a bit set. `is_fail` is 1 for health "fail"; the rest are the manifest counters
/// (nodes of the AST, number of failed backends, type-check errors, round-trip loss).
pub fn why_mask(is_fail: u32, nodes: u32, nfailed: u32, tc: u32, loss: u32) -> u32 {
if (is_fail == 0) { return 0; }
var m : u32 = 0;
if (nodes == 0) { m = m + W_NOT_PARSED; }
if (nfailed > 0) { m = m + W_BACKEND; }
if (tc > 0) { m = m + W_TYPECHECK; }
if (loss > 0) { m = m + W_LOSS; }
if (m == 0) { m = W_UNKNOWN; }
return m;
}

test "why_mask: a spec that is not failing has no reasons" {
assert why_mask(0, 0, 0, 0, 0) == 0;
assert why_mask(0, 120, 3, 2, 1) == 0;
}

test "why_mask: a spec that never parsed is not-parsed, not unexplained" {
assert why_mask(1, 0, 0, 0, 0) == W_NOT_PARSED;
}

test "why_mask: each counter sets its own bit, and bits add" {
assert why_mask(1, 50, 2, 0, 0) == W_BACKEND;
assert why_mask(1, 50, 0, 3, 0) == W_TYPECHECK;
assert why_mask(1, 50, 0, 0, 7) == W_LOSS;
assert why_mask(1, 50, 1, 2, 0) == W_BACKEND + W_TYPECHECK;
assert why_mask(1, 0, 1, 0, 1) == W_NOT_PARSED + W_BACKEND + W_LOSS;
}

test "negative control: a fail with no counter is W_UNKNOWN alone, never silent" {
assert why_mask(1, 50, 0, 0, 0) == W_UNKNOWN;
assert why_mask(1, 50, 0, 0, 0) != 0;
}

test "why_mask: all four counters at once leave W_UNKNOWN out" {
assert why_mask(1, 0, 2, 3, 4) == W_NOT_PARSED + W_BACKEND + W_TYPECHECK + W_LOSS;
assert why_mask(1, 0, 2, 3, 4) < W_UNKNOWN;
}
Loading