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
30 changes: 29 additions & 1 deletion .claude/skills/t27f/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ the diagram core in detail, then the other surfaces and the topic-map applicatio
| 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 |
| Dependencies view: the specs around one spec | `specs/t27f/spec_deps.t27` (t27#10680) | spec with tests: reads `t27c use-edges --specs-dir specs`; layer 0 is the focus, +k the specs k imports away (right), -k the specs k importers away (left), each spec once in its shortest-path layer; every edge-list line between two drawn specs is one `exact` edge, a repeated line drawn once; `cycle=1` when the focus imports itself or is imported by a spec in its own import closure; a walk past the caller's room or depth is refused whole (`refused\tfull` / `refused\tdeep`), never cut. A `use` line t27c does not resolve is not in the edge list, so `deps_unknown` (t27#10702) reads the focus source and writes each such line as `unknown\tpath`, mirroring `use_resolve.rs` (whole path, then one fallback level) without looking for a file; on master f33f269d5 that was 23 lines in 13 specs, byte for byte the `t27c gen` warnings. `deps_why a b` (t27#10705) writes one shortest chain of edge-list lines from a to b, each `exact`, a tie taken in edge-list order, `none` when a does not reach b, refused whole when it does not fit. Limits: the view flags only the focus, and `deps_cycles` (t27#10693) lists every spec on a cycle, each once, refused whole (`cap + 1`) if anything does not fit; a spec on a cycle through the focus is drawn on both sides; no host draws it yet. Corpus on master e607228f7: 806 specs, 2461 edge lines, nothing refused at depth 32, deepest walks 6, one real cycle (igla race `backend` <-> `rtl`), which `deps_cycles` lists exactly on master f7efbc232 and lists as empty with #10689 |
| Dependencies view: the specs around one spec | `specs/t27f/spec_deps.t27` (t27#10680) | spec with tests: reads `t27c use-edges --specs-dir specs`; layer 0 is the focus, +k the specs k imports away (right), -k the specs k importers away (left), each spec once in its shortest-path layer; every edge-list line between two drawn specs is one `exact` edge, a repeated line drawn once; `cycle=1` when the focus imports itself or is imported by a spec in its own import closure; a walk past the caller's room or depth is refused whole (`refused\tfull` / `refused\tdeep`), never cut. A `use` line t27c does not resolve is not in the edge list, so `deps_unknown` (t27#10702) reads the focus source and writes each such line as `unknown\tpath`, mirroring `use_resolve.rs` (whole path, then one fallback level) without looking for a file; on master f33f269d5 that was 23 lines in 13 specs, byte for byte the `t27c gen` warnings. `deps_why a b` (t27#10705) writes one shortest chain of edge-list lines from a to b, each `exact`, a tie taken in edge-list order, `none` when a does not reach b, refused whole when it does not fit; `specs/t27f/why_card.t27` (t27#10716) draws that chain as a 1080x1350 social card, one exact box per spec, and draws nothing for a chain that is not one. Limits: the view flags only the focus, and `deps_cycles` (t27#10693) lists every spec on a cycle, each once, refused whole (`cap + 1`) if anything does not fit; a spec on a cycle through the focus is drawn on both sides; no host draws it yet. Corpus on master e607228f7: 806 specs, 2461 edge lines, nothing refused at depth 32, deepest walks 6, one real cycle (igla race `backend` <-> `rtl`), which `deps_cycles` lists exactly on master f7efbc232 and lists as empty with #10689 |
| 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 |
Expand Down Expand Up @@ -406,6 +406,34 @@ tail). A `sed s#@EDGES@#...#` with 2466 lines fails with "Argument list too long
no outgoing edge, such as `verified/reuse`, checks only itself, so `> 1` fails for it; that is
the probe, not the function.

### Draw the chain as a card (t27#10716)

`specs/t27f/why_card.t27` turns the chain into an ffmpeg graph. Its main reads the edge list on
stdin, plus `WHY_A`, `WHY_B` and `WHY_FONT`. Skill card: `t27/why-card`. On the lab:

1. `t27c gen specs/t27f/why_card.t27 > why_card.zig`, then compile it with zig and `-lc`.
2. `t27c use-edges --specs-dir specs | WHY_A=... WHY_B=... WHY_FONT=/root/tf/fonts/jbm-all-800.ttf ./why_card 2> g.txt`.
3. Render with `ffmpeg -nostdin -threads 2 -filter_threads 1 -filter_complex_script g.txt -map '[v]' -frames:v 1 card.png`.
4. Check pixels, never by eye. Crop one pixel (`crop=1:1:x:y`) to rgb24 and compare: box fill
at (106, top+6), stroke at (101, top+48), fill at both inner edges (110 and 970) so a name
never reaches the stroke, backdrop in each gap, the green connector at x=540. Count
`ff ff ff` in each box's text band so every name is drawn, with 0 below the column. Add one
deliberately wrong expectation that must MISS.

On 8aec8e259 (2474 edge lines): from `why_card` 20 chains drew, up to 7 boxes (to `verified/device_dna`, 6 hops),
and all 62 pixel checks held.

Traps:

- **Do not import a print module for one constant.** `use t27f::lesson_reel::FONT` pulls
lesson_reel's whole scene tree into the build, and zig stops on `FLOOR_KEY` undeclared. Pass
the value in (`WHY_FONT`) instead of copying it.
- **`t27c gen` drops an open slice end** (t27#6250): `buf[n..].ptr` becomes `buf[n].ptr`, and
zig says "type 'u8' does not support field access". Write `buf[n..cap].ptr`.
- **A count check can hide behind a later check.** `k != hops` mutated to `k > hops` survived
until a fixture had fewer edges than `hops=` *and* still ended at B. Give every refusal a
fixture that only that refusal catches.

## What this skill does not claim

The graph IR is a schema (`specs/t27f/spec_graph.t27`) and `specs/t27f/spec_graph_export.t27` turns source into it, but only exact declarations, spans and edges within the IR's capacity, with `specs/t27f/spec_graph_derive.t27` adding the derived and unknown part by stated rules, and no host calls either yet; no renderer draws a T27F view in the Trinity UI, no
Expand Down
28 changes: 28 additions & 0 deletions .trinity/seals/skills_skill_t27_why_card.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"built_by": "t27c-bootstrap@0.5.2+9448ab4be",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:3e2cb9c5de2ae2c30b6e20883edcd206910c33c77de68d94040bf860d73635ad",
"gen_hash_rust": "sha256:5781a115f9a47ae898623d893de3925ac3cbea9958d7b14f5f5a27aedb735240",
"gen_hash_verilog": "sha256:b2abd22fabe5a482094b79436f85ce56578c8727c910536ce8cfbfbe3f7a240b",
"gen_hash_zig": "sha256:02488bd498760018d2e80d5d7da2719ffa4cd8a031f2033b1945b8cc321a58d7",
"module": "skill_t27_why_card",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T23:40:56Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:7a8a7d317b6876db1c66fc3a2ebb03cde5d21a931feea30e36d80926d26682e5",
"spec_path": "specs/skills/t27-why-card.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 1,
"total": 1,
"vacuous": 0
},
"toolchain": {
"test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10",
"zig": "0.16.0"
}
}
49 changes: 49 additions & 0 deletions .trinity/seals/t27f_t27f_why_card.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"built_by": "t27c-bootstrap@0.5.2+9448ab4be",
"closure": [
{
"spec": "specs/ci/affected.t27",
"spec_hash": "sha256:38a7bdc245f1974f0d5f1a55a45966decd70098afdb89e882f568da3650ee9f9"
},
{
"spec": "specs/claw/t27f_gate.t27",
"spec_hash": "sha256:856be049f9ef3de20cce2f7d18ca3b78a5f4a8ad660dddd8f2cf1aa97bc61ee7"
},
{
"spec": "specs/policy/text_lines.t27",
"spec_hash": "sha256:13c90895a4a44c760cce6b3b65f72f937298633062f040f8489486108ab0c2d1"
},
{
"spec": "specs/t27f/spec_deps.t27",
"spec_hash": "sha256:c6e6c03537dd7238ce7cceb937dfe307294ab59d4946de95f7d96e40333cb070"
},
{
"spec": "specs/t27f/spec_graph_view.t27",
"spec_hash": "sha256:d4c318162bf52405208d4016d0505c8037cd648b58feb3ad5240a2695c6730fe"
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:8be8c42a1cdddd18fcb9c024e3f3567b9cfc1d01bcf36f3b982698bd6b339678",
"gen_hash_rust": "sha256:b2ec6788661eec77f37b5a37a99a2c6d9d63f9187233e23de67963c8f33cfdbb",
"gen_hash_verilog": "sha256:8e8a23e07c446ddcc960f96865756bd2804f9a0f0001364b41c5b810d1a8add3",
"gen_hash_zig": "sha256:b1db9ea3c5fcf9f466564a67281d6de3616306101f19d76a3561eef484d65eeb",
"module": "t27f_why_card",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T23:40:53Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:01381c902fe4b5702dc26505a0d081faa0895bfaf60ef51a529cfef894a8ae68",
"spec_path": "specs/t27f/why_card.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 10,
"total": 10,
"vacuous": 0
},
"toolchain": {
"test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10",
"zig": "0.16.0"
}
}
49 changes: 49 additions & 0 deletions specs/skills/t27-why-card.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache-2.0
; specs/skills/t27-why-card.t27 -- skill t27/why-card
; Source of truth for the skill card on t27.ai (#/skills?skill=t27/why-card).
; How TRI CLAW answers "why does spec A import spec B" with a picture: pipe
; `t27c use-edges` into why_card with WHY_A, WHY_B and WHY_FONT set, and render the
; graph it prints with ffmpeg as a 1080x1350 card, one box per spec of the shortest
; use chain, each hop an exact edge. The law lives once, in specs/t27f/why_card.t27
; (card_* functions and their tests), and the chain in specs/t27f/spec_deps.t27
; (deps_why); this card points at them and copies none. Posting a card is the owner's
; call. There is no SKILL.md behind it yet, so the card is spec-only (see "Witness
; labels" in specs/skills/README.md). Issues gHashTag/t27#10705 and
; gHashTag/t27#10716, epic gHashTag/trinity#1648.
; The JSON the site serves is generated from this file by the real compiler;
; nothing below is parsed with a regex.
; phi^2 + 1/phi^2 = 3 | TRINITY

module skill_t27_why_card;

pub const KIND : str = "skill";
pub const ID : str = "t27/why-card";
pub const NAME : str = "Why Card";
pub const REPO : str = "t27";
; No SKILL.md exists for this card.
pub const SOURCE : str = "";
pub const SUMMARY_EN : str = "Draw why one spec imports another as a social card: the shortest use chain t27c resolved, one box per spec, every edge exact, and no card at all when the chain does not exist or does not fit";
pub const COMMAND : str = "/why-card";
; Specs this skill stands on: the card layout and the chain it draws.
pub const SPECS : [2]str = ["specs/t27f/why_card.t27", "specs/t27f/spec_deps.t27"];
pub const TAGS : [4]str = ["social-visual", "t27f", "deps", "card"];
pub const ENABLED : bool = true;
; Default budget; no SKILL.md declares a timeout.
pub const TIMEOUT_MIN : u16 = 30;

test basic_constants {
assert KIND == "skill";
assert ID == "t27/why-card";
assert NAME == "Why Card";
assert REPO == "t27";
assert SOURCE == "";
assert COMMAND == "/why-card";
assert ENABLED == true;
assert TIMEOUT_MIN == 30;
assert TAGS.len == 4;
assert TAGS[0] == "social-visual";
assert TAGS[2] == "deps";
assert SPECS.len == 2;
assert SPECS[0] == "specs/t27f/why_card.t27";
assert SPECS[1] == "specs/t27f/spec_deps.t27";
}
Loading
Loading