Skip to content

feat(t27f): t27f_outline and t27f_graph {path} from the T27F exporter - #13

Merged
gHashTag merged 2 commits into
masterfrom
t27f/outline
Oct 18, 2026
Merged

gHashTag merged 2 commits into
masterfrom
t27f/outline

Conversation

@gHashTag

@gHashTag gHashTag commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

What

Claude Code can now read a .t27 spec through its T27F graph instead of the whole file.

  • New tool t27f_outline({path}): compact text, one line per node (kind, name, lines a-b, label), one per edge (kind, from -> to, line), plus a short derived section "fn X is tested by: ...".
  • t27f_graph accepts {path} as well as {fixture} and draws the exported graph with the existing spec_graph_view layout.
  • Both are exact only (no derived edges in the graph) and capped at 192 nodes / 254 edges (the SpecGraph size after spec(t27f): raise SpecGraph capacity to 192 nodes and 254 edges (Closes #10180) t27#10181; the brief's 20/24 is obsolete). A larger spec returns the verdict ("refused, too many nodes: N nodes found, the graph holds at most 192. No graph is drawn or truncated."), never a truncated graph.

The logic is t27

The output wording and every decision are in a new spec, specs/t27f/spec_graph_outline.t27 (gHashTag/t27#10202, issue #10200; 8 tests, 0 vacuous, on the Railway lab): the text templates (OUTLINE_TEXT, REFUSAL_TEXT, with {} marks), which refusal text a verdict takes (refusal_index), which facts print (shows_in_outline, exact only), how a span prints (span_is_range) and the tested-by view (tested_by_count, tested_by_of). The spec carries its own small copy of the tested_by rule until the derive spec (gHashTag/t27#10198) lands.

How it is built

Glue disclosure (I/O only, no spec logic)

  • wasm/export.zig: 46 -> 138 lines, so ~92 lines of glue: a 2 MiB source buffer + 512-byte path buffer with gx_set_len / gx_set_path_len, gx_load() (runs the exporter, copies the graph into the view and outline modules' identical-shaped types field by field), scalar getters from a comptime loop over the SpecGraph fields, and wrappers for the view and outline fns. No spec function is named and no number is held.
  • Strings: node names come back as gx_node_name_ptr(i) / gx_node_name_len(i), a pointer and length into the source buffer.
  • graph.mjs (57 lines): host I/O only. It writes bytes in, loops over nodes and edges and fills the spec's {} templates left to right. It chooses no wording, no filter and no refusal text. Its leftover: name tables for kinds/labels/verdicts are read from the generated constants of spec_graph_view.mjs (gen-js does not emit them).
  • server.mjs: tool registration, path validation (*.t27, no leading /, no ..), SVG text fitting.

Verification (fresh node server.mjs, JSON-RPC tools/call, re-run after moving the logic into t27; output unchanged)

  • t27f_outline specs/tutorial/08_modules_and_arrays.t27: 14 nodes, 9 edges (3 tested-by lines).
  • t27f_graph on the same path: 14 nodes, 9 edges, 1600x380 SVG.
  • Large spec that fits: specs/compiler/core/t27core_ddc.t27 (about 1.36 MB, 4165 lines): 41 nodes, 14 edges.
  • Refused: specs/numeric/gf16.t27: "refused, too many nodes: 436 nodes found, the graph holds at most 192. No graph is drawn or truncated." (also t27core.t27, 307 nodes). t27f_graph returns the same verdict as an error. ../etc/passwd.t27 is rejected by path validation.

Order of merge: gHashTag/t27#10202 first (provenance of the outline spec), then this PR. Auto-merge is disabled on this repository, so a human merges.

package.json files lists graph.mjs, wasm/spec_graph_view.mjs, wasm/spec_graph_export.mjs and wasm/spec_graph_outline.mjs.

🤖 Generated with Claude Code

Read a .t27 spec through its exact-only T27F graph instead of the whole file.
Generated spec_graph_export.zig/.mjs on the Railway lab from gHashTag/t27
master 290ff17df; export.zig gains ~80 lines of buffer/getter glue, graph.mjs
is 50 lines of host I/O. Refuses (never truncates) over 192 nodes / 254 edges.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tri-claw

tri-claw Bot commented Oct 18, 2026

Copy link
Copy Markdown

Summary by t27-bees

This PR adds two new tools (t27f_outline and t27f_graph {path}) that read a .t27 spec through the T27F exact-only graph exporter (spec_graph_export.t27), with a hard cap of 192 nodes / 254 edges. The exporter runs in wasm/t27f.wasm; host glue in graph.mjs handles I/O and the "tested by" derivation.

Worth a look

  • wasm/export.zig:70 — The field-by-field copy inline for (@​typeInfo(V.SpecGraph).@"struct".fields) |f| @​field(vg, f.name) = @​field(eg, f.name); assumes spec_graph_export.zig and spec_graph_view.zig have identical SpecGraph field names, order, and types. Both are generated from separate .t27 specs; if those specs diverge, this copies wrong data or misses fields silently.
  • wasm/export.zig:64 — The 2 MiB source buffer ([2 << 20]u8) and 512-byte path buffer are hard limits. A spec larger than 2,097,152 bytes or a path longer than 512 bytes will fail at gx_set_len/gx_set_path_len with a generic "buffer holds N" error. The largest current spec is ~1.36 MB, but this is a fixed ceiling.
  • graph.mjs:24–26 — gx_set_len returns false when n > src_buf.len (strictly greater), so a spec of exactly 2,097,152 bytes would be rejected even though the buffer is that size. Off-by-one; unlikely to matter in practice but inconsistent with the capacity check message.
  • server.mjs:182–187 — Path validation uses /^[A-Za-z0-9_\-./]+\.t27$/ plus a .. segment check. It allows ./ and ... segments (e.g., specs/./../foo.t27 passes the regex but is caught by includes('..'); specs/.../foo.t27 passes both). Not an exploit with the current corpus layout, but the regex is permissive.
  • server.mjs:210–212 — The getter map g uses view_-prefixed names for most view fields but line_start/line_end without prefix for node line spans. This relies on the exporter's SpecGraph having those exact field names (it does), but the asymmetry is fragile if the view module's generated getters change.

Read: 11 files, +1644 -45, the whole diff up to 60000 characters (the rest was not read). Head 72ecc84. Model: openai-compatible/nvidia/nemotron-3-ultra-550b-a55b. Commands: @t27-bees review | summary | help | pause | resume. Free for every repository.

… graph.mjs is I/O only

The outline text templates, the refusal choice, the exact-only filter, the span
rule and the tested-by view are t27 (gHashTag/t27#10202, issue #10200), generated
to wasm on the lab. graph.mjs now only moves bytes and fills the spec's {} marks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@gHashTag
gHashTag marked this pull request as ready for review October 18, 2026 04:14
@gHashTag
gHashTag merged commit 95a9de5 into master Oct 18, 2026
2 checks passed
@gHashTag
gHashTag deleted the t27f/outline branch October 18, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant