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
6 changes: 3 additions & 3 deletions .claude/skills/t27f/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ the reels. Describing T27F as #1677 is the error this section exists to stop (ow
| Spec -> graph IR schema (the core) | `specs/t27f/spec_graph.t27` (t27#10020) | spec with tests: node / edge kinds, labels, spans, `validate_graph`, line -> node mapping, node -> ui_plan widget, one hand-read fixture (`specs/tutorial/08_modules_and_arrays.t27`) |
| Graph view: layout of a spec graph | `specs/t27f/spec_graph_view.t27` (t27#10034) | spec with tests: one column per node kind, size / colour / dash / row order from the inference label, UNKNOWN nodes alone in a band, every edge with a source line, node -> widget through `node_widget`; numbers only, laid out on the hand-read fixture; drawn by the MCP tool `t27f_graph` (gHashTag/t27-mcp#9) |
| Graph view for big specs: wrap, fold, expand, hit | `specs/t27f/spec_graph_fold.t27` (t27#10218) | spec with tests: a kind column taller than N = 12 rows wraps into sub-columns so the canvas aspect is near phi (identical to the view when nothing wraps); a fold to one box per kind with the exact count and one arc per pair of kinds with the exact edge count, each labelled by its weakest member; `expand_graph` = one kind plus its direct neighbours, copied verbatim with an origin map; `wrap_hit` / `wrap_hit_line_start` / `wrap_hit_line_end` take a click to a node and its lines; numbers only, the view's colours are not touched |
| Spec -> graph IR exporter | `specs/t27f/spec_graph_export.t27` (t27#10162) | spec with tests: `export_graph(path, src)` is a deterministic byte scanner over the source, `export_verdict` is `validate_graph` of the result. One node per top-level declaration (a leading `pub ` is allowed), spans to the matching closing brace, every node and edge EXACT or (unclassifiable) OPAQUE + UNKNOWN, CALLS / USES / CONSTRAINS edges with the line of the use, refusal past 192 nodes or 254 edges. On the tutorial 08 snapshot it equals the hand-read fixture on all 14 declaration nodes and all 9 EXACT edges; it does not emit the fixture's `std.testing` UNKNOWN node, nor its 7 DERIVED edges (4 data flow, 3 tested_by), because a scanner never derives; the derive pass below adds them. Limits: textual matching without scope or type resolution, no block comments, no struct/enum bodies, no cross-spec `use` resolution, capacity 192 nodes and 254 edges (about 98 percent of specs in specs/ fit; the rest are refused), takes a `[]const u8` slice rather than `[65536]u8` plus a length. Not wired to a host: the MCP tool does not call it. AST/IR audit is trinity#1650, pilot choice #1651 |
| Graph IR derive pass | `specs/t27f/spec_graph_derive.t27` (t27#10191) | spec with tests: `derive_graph(g, src)` appends what the exporter must not claim and changes nothing it holds. R1 DERIVED `tested_by` = every EXACT CALLS edge from a test, invariant or bench to a fn read backwards (no line). R2 DERIVED `data_flow` = a const or `use` that an EXACT USES edge ties to a fn and that is named inside one of the fn's return statements (from `return` to the next `;`), on the line of the first such name. R3 UNKNOWN opaque node = an undeclared qualified call target of three or more segments (`std.testing.expectEqual(` names `std.testing`), spanning the lines that call through it. Exporter plus derive equal the tutorial 08 fixture as sets: 15 of 15 nodes, 16 of 16 edges, no special case. Limits: names matched as text; call results and flow through locals are not derived; R3 needs 3 or more segments (two cannot be told from a method call); a graph that does not validate, or a source of another line count, comes back unchanged; past 192 nodes or 254 edges the result is refused with the too-many verdict. Not wired to a host: the MCP tool does not call it |
| Spec -> graph IR exporter | `specs/t27f/spec_graph_export.t27` (t27#10162) | spec with tests: `export_graph(path, src)` is a deterministic byte scanner over the source, `export_verdict` is `validate_graph` of the result. One node per top-level declaration (a leading `pub ` is allowed), spans to the matching closing brace, every node and edge EXACT or (unclassifiable) OPAQUE + UNKNOWN, CALLS / USES / CONSTRAINS edges with the line of the use, refusal past 1024 nodes or 4096 edges. On the tutorial 08 snapshot it equals the hand-read fixture on all 14 declaration nodes and all 9 EXACT edges; it does not emit the fixture's `std.testing` UNKNOWN node, nor its 7 DERIVED edges (4 data flow, 3 tested_by), because a scanner never derives; the derive pass below adds them. Limits: textual matching without scope or type resolution, no block comments, no struct/enum bodies, no cross-spec `use` resolution, capacity 1024 nodes and 4096 edges, u16 indices (all 2225 specs in specs/ fit, largest 775 nodes and 3194 edges after derive; anything past the cap is refused, never truncated), takes a `[]const u8` slice rather than `[65536]u8` plus a length. AST/IR audit is trinity#1650, pilot choice #1651 |
| Graph IR derive pass | `specs/t27f/spec_graph_derive.t27` (t27#10191) | spec with tests: `derive_graph(g, src)` appends what the exporter must not claim and changes nothing it holds. R1 DERIVED `tested_by` = every EXACT CALLS edge from a test, invariant or bench to a fn read backwards (no line). R2 DERIVED `data_flow` = a const or `use` that an EXACT USES edge ties to a fn and that is named inside one of the fn's return statements (from `return` to the next `;`), on the line of the first such name. R3 UNKNOWN opaque node = an undeclared qualified call target of three or more segments (`std.testing.expectEqual(` names `std.testing`), spanning the lines that call through it. Exporter plus derive equal the tutorial 08 fixture as sets: 15 of 15 nodes, 16 of 16 edges, no special case. Limits: names matched as text; call results and flow through locals are not derived; R3 needs 3 or more segments (two cannot be told from a method call); a graph that does not validate, or a source of another line count, comes back unchanged; past 1024 nodes or 4096 edges the result is refused with the too-many verdict. Not wired to a host: the MCP tool does not call it |
| UI plan IR + trusted widget catalog | `specs/t27f/ui_plan.t27` (t27#9962) | spec with tests; no renderer draws a plan yet |
| Honeycomb focus+context geometry | `specs/t27f/reel.t27` | spec; stills drawn by the MCP tool `t27f_view` |
| Renderer in the Trinity UI | gHashTag/trinity | **missing**; no T27F widget is live |
Expand All @@ -88,7 +88,7 @@ is not progress on the core.
4. Say plainly that this is a manual reading unless a graph came out of `export_graph`
(`specs/t27f/spec_graph_export.t27`), with `derive_graph` (`specs/t27f/spec_graph_derive.t27`)
on top. The exporter reads declarations, spans and exact CALLS / USES / CONSTRAINS edges only,
within 192 nodes and 254 edges; the derive pass adds DERIVED tested_by and data_flow edges
within 1024 nodes and 4096 edges; the derive pass adds DERIVED tested_by and data_flow edges
and UNKNOWN opaque nodes for undeclared qualified call targets, by its three stated rules.
Anything the rules do not reach (SUGGESTED explanations, call-result flow, anything larger)
is still a manual reading, and no host runs either pass yet. Name that gap rather than hide it.
Expand Down
92 changes: 43 additions & 49 deletions specs/t27f/spec_graph.t27
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,23 @@ pub const EDGE_FROM_KINDS : [5]u32 = [60, 61, 4, 16, 198];
pub const EDGE_TO_KINDS : [5]u32 = [4, 66, 56, 6, 188];

/// Capacity. Line 0 means "no source line": a span with line_start 0 is no span.
/// Measured 2026-10-18 on the Railway lab, over the 2204 specs/**/*.t27 files of gHashTag/t27
/// master (t27#10173 tree): top-level declarations per spec (nodes) p50 20, p90 66, p95 96,
/// p98 183, p99 436, max 678. Call/use/constrain edges, read through the exporter on an 86-spec
/// stratified sample: p50 at most 12, p95 at most 128 (82 of 86 specs), 85 of 86 at most 192, the
/// largest between 193 and 256. The caps cover the node p98 and the edge p95. Node and edge
/// endpoints are u8 and NO_INDEX is 255, so both caps must stay below 255; MAX_EDGES is the
/// largest edge count that does, MAX_NODES is the 192 that covers p98 under the same
/// limit. A larger graph is refused, never truncated (validate_graph).
pub const MAX_NODES : u32 = 192;
pub const MAX_EDGES : u32 = 254;
pub const MEASURED_NODES_P95 : u32 = 96;
pub const MEASURED_EDGES_P95 : u32 = 128;
/// Measured 2026-10-18 on the Railway lab, over all 2225 specs/**/*.t27 files of gHashTag/t27
/// master 538df0750, through export_graph plus derive_graph (the graph a host draws): nodes per
/// spec p50 20, p90 65, p95 93, p98 191, p99 444, p999 720, max 775 (specs/igla/coder/eval.t27);
/// edges p50 18, p90 118, p95 211, p98 371, p99 531, p999 1573, max 3194 (specs/tools/t27o/
/// rust_convert.t27, then t27core.t27 at 3020). Every one of the 2225 specs exports and derives to a
/// graph that validates. The caps are the next power of two above the maxima, about 30 percent of
/// headroom: 1024 over 775 and 4096 over 3194. Node and edge endpoints are u16 and NO_INDEX is
/// 65535, so a cap may grow to 65534 without a type change. A larger graph is refused, never
/// truncated (validate_graph).
pub const MAX_NODES : u32 = 1024;
pub const MAX_EDGES : u32 = 4096;
pub const MEASURED_NODES_P95 : u32 = 93;
pub const MEASURED_EDGES_P95 : u32 = 211;
pub const MEASURED_NODES_MAX : u32 = 775;
pub const MEASURED_EDGES_MAX : u32 = 3194;
pub const NO_LINE : u32 = 0;
pub const NO_INDEX : u32 = 255;
pub const NO_INDEX : u32 = 65535;

/// Validation verdicts. validate_graph checks graph-level rules in this order, then each node in
/// index order, then each edge in index order, and returns the first rule broken.
Expand Down Expand Up @@ -153,17 +156,17 @@ pub struct SpecGraph {
spec_path: str,
line_count: u32,
node_count: u32,
node_kind: [192]u8,
node_label: [192]u8,
node_name: [192]str,
line_start: [192]u32,
line_end: [192]u32,
node_kind: [1024]u8,
node_label: [1024]u8,
node_name: [1024]str,
line_start: [1024]u32,
line_end: [1024]u32,
edge_count: u32,
edge_kind: [254]u8,
edge_from: [254]u8,
edge_to: [254]u8,
edge_label: [254]u8,
edge_line: [254]u32,
edge_kind: [4096]u8,
edge_from: [4096]u16,
edge_to: [4096]u16,
edge_label: [4096]u8,
edge_line: [4096]u32,
}

/// One node read out of a graph: the record a node inspector shows.
Expand Down Expand Up @@ -513,23 +516,10 @@ pub fn all_bindings_trusted() -> bool {
pub fn blank_graph() -> SpecGraph {
return SpecGraph {
ir_version: GRAPH_IR_VERSION, spec_path: "", line_count: 0, node_count: 0,
node_kind: [0; 192], node_label: [0; 192],
node_name: [
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
],
line_start: [0; 192], line_end: [0; 192],
edge_count: 0, edge_kind: [0; 254], edge_from: [0; 254], edge_to: [0; 254], edge_label: [0; 254], edge_line: [0; 254]
node_kind: [0; 1024], node_label: [0; 1024],
node_name: ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
line_start: [0; 1024], line_end: [0; 1024],
edge_count: 0, edge_kind: [0; 4096], edge_from: [0; 4096], edge_to: [0; 4096], edge_label: [0; 4096], edge_line: [0; 4096]
};
}

Expand All @@ -550,7 +540,7 @@ pub fn with_node(g: SpecGraph, kind: u8, label: u8, name: str, s: u32, e: u32) -
}

/// g with one more edge, under the same capacity rule as with_node.
pub fn with_edge(g: SpecGraph, kind: u8, f: u8, t: u8, label: u8, line: u32) -> SpecGraph {
pub fn with_edge(g: SpecGraph, kind: u8, f: u16, t: u16, label: u8, line: u32) -> SpecGraph {
var h : SpecGraph = g;
if (h.edge_count < MAX_EDGES) {
let k : usize = h.edge_count as usize;
Expand Down Expand Up @@ -761,28 +751,29 @@ test capacity_is_refused_not_truncated {
}

test a_graph_exactly_at_capacity_is_valid {
// 16 fns and 16 tests after the fixture's 15 nodes, then consts up to MAX_NODES; every
// test calls every fn, so the CALLS edges are distinct (a repeated edge is a duplicate)
// 64 fns and 64 tests after the fixture's 15 nodes, then consts up to MAX_NODES; every
// test calls every fn, so the CALLS edges are distinct (a repeated edge is a duplicate) and
// 64 x 64 of them reach MAX_EDGES from the fixture's 16
var g : SpecGraph = tutorial_08_graph();
var a : u32 = 0;
while (a < 16) {
while (a < 64) {
g = with_node(g, NODE_FN, LABEL_EXACT, "f", 1, 1);
a = a + 1;
}
a = 0;
while (a < 16) {
while (a < 64) {
g = with_node(g, NODE_TEST, LABEL_EXACT, "t", 1, 1);
a = a + 1;
}
while (g.node_count < MAX_NODES) {
g = with_node(g, NODE_CONST, LABEL_EXACT, "extra", 1, 1);
}
var t : u32 = 0;
while (t < 16) {
while (t < 64) {
var f : u32 = 0;
while (f < 16) {
while (f < 64) {
if (g.edge_count < MAX_EDGES) {
g = with_edge(g, EDGE_CALLS, (31 + t) as u8, (15 + f) as u8, LABEL_EXACT, 1);
g = with_edge(g, EDGE_CALLS, (79 + t) as u16, (15 + f) as u16, LABEL_EXACT, 1);
}
f = f + 1;
}
Expand All @@ -795,7 +786,7 @@ test a_graph_exactly_at_capacity_is_valid {
assert g.node_kind[MAX_NODES as usize - 1] == NODE_CONST;
assert g.edge_kind[MAX_EDGES as usize - 1] == EDGE_CALLS;
// negative control: one more edge or node makes the same graph refused
assert validate_graph(with_edge(g, EDGE_CALLS, 31, 15, LABEL_EXACT, 1)) == ERR_TOO_MANY_EDGES;
assert validate_graph(with_edge(g, EDGE_CALLS, 79, 15, LABEL_EXACT, 1)) == ERR_TOO_MANY_EDGES;
assert validate_graph(with_node(g, NODE_CONST, LABEL_EXACT, "extra", 1, 1)) == ERR_TOO_MANY_NODES;
}

Expand Down Expand Up @@ -1067,6 +1058,9 @@ invariant reserved_codes_stay_outside {
assert MAX_EDGES < NO_INDEX;
assert MAX_NODES >= MEASURED_NODES_P95;
assert MAX_EDGES >= MEASURED_EDGES_P95;
// every spec of the corpus fits, with room: the caps clear the measured maxima by a quarter
assert MAX_NODES * 4 >= MEASURED_NODES_MAX * 5;
assert MAX_EDGES * 4 >= MEASURED_EDGES_MAX * 5;
assert (NODE_KINDS as u32) < 32;
assert ERR_DUPLICATE_EDGE + 1 == VERDICT_KINDS;
}
Loading
Loading