Skip to content

feat(website): Spec Explorer — Chip tab, on-die view, and hardware history - #953

Open
gHashTag wants to merge 16 commits into
mainfrom
blog/t27-spec-explorer
Open

feat(website): Spec Explorer — Chip tab, on-die view, and hardware history#953
gHashTag wants to merge 16 commits into
mainfrom
blog/t27-spec-explorer

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 6, 2026

Copy link
Copy Markdown
Owner

What

Adds a Chip tab to the Spec Explorer. Every .t27 spec gets an animated
schematic of the datapath it declares, and the formats that have actually been
through the AX7203 carry their hardware evidence chain beside it.

This branch also carries the rest of the Spec Explorer line (16 commits): the
corpus browser, live editing, tags and deep links, health grouping, and the
Queen SPECS view.

The schematic

Derived from declarations the language already forces you to write — the bit
width of every constant, the field layout of every packed struct, the parameter
and return widths of every function. Buses render as parallel lanes with a
travelling pulse; the animation is pausable.

It is deliberately not presented as a netlist. The .t27 to Verilog backend
emits module shells today: 361 of 676 specs produce Verilog yosys accepts, and
every one of them yields 0 LUTs and 0 flip-flops. There is no placement to draw,
and the panel says so rather than drawing something suggestive.

Rows are capped at 12. The SVG scales to the panel width, so a 53-function spec
would otherwise shrink every label past reading; the remainder is counted in
words instead of rendered illegibly.

The hardware history

Keyed by an explicit spec-path table, not by name similarity — a spec cannot
acquire a hardware claim by resembling one.

  • gf4, gf8, gf12, gf16, gf20, gf24 — ADD and MUL measured on silicon
  • SUB is shown as "bitstream ready, not yet flashed", not as a result
  • gf32 and every other spec — "No hardware run", stated plainly rather than
    rendered as an empty timeline that reads as pending

Each claim is displayed at the strength it was earned: declared / proven by SAT /
bitstream ready / on silicon. Figures are transcribed from
GOLDENFLOAT_HW_CONFORMANCE_v0.2 and its evidence chain — CI run, bitstream
SHA-256, JTAG flash, UART log.

The panel states explicitly that the silicon results belong to hand-written RTL
of the same format, not to the Verilog these specs compile to. The spec and
the core describe the same number format; they are not the same artifact.

Verified

  • tsc -b --force — zero errors in the four touched/added files (repo baseline
    is 179 pre-existing errors, unchanged)
  • Production build green
  • Rendered and read back in the browser on two specs: specs/numeric/gf16.t27
    shows ADD/MUL 512/512 on silicon with the NaN-fix note and the four-link chain;
    a tutorial spec correctly shows "No hardware run"

Both EN and RU copy added for every new string.

gHashTag and others added 16 commits September 5, 2026 17:25
A library of all 497 t27 specs. Pick one and see it through every stage the
compiler puts it through: tokens, AST, type check, HIR, and five codegen
backends (Zig, Verilog, Verilog-from-HIR, C, Rust).

The compiler is not reimplemented in TypeScript. bootstrap/src/compiler.rs is
built to wasm32-unknown-unknown and run in the browser, so what the page draws
is what t27c produces and cannot drift from it. Three things made that
practical, none of them obvious up front: compiler.rs carries exactly one
external `use`, its only filesystem calls sit inside #[cfg(test)], and the
whole module is 173 KB gzipped with every backend linked. Measured across the
corpus: 5ms average per spec, 68ms for the largest (1618 lines, 3738 nodes).

The page uses parse_ast_full rather than parse_ast. The difference matters:
error recovery can discard declarations while still reporting a successful
parse, and a tree that quietly omitted half its file would be the wrong thing
to draw. 49 of 497 specs (9.9%) lose content this way, so the page shows a
banner naming exactly what was dropped. api/c_api_contract.t27 is the clearest
case -- 25 discarded declarations, and the generated Verilog says
`module unknown` because the module declaration was among them.

Also surfaced: the type checker runs in no codegen path (t27c compile gates on
it, t27c gen does not), which the Types tab states rather than glossing.

Vendored, not fetched at runtime, so the page works offline and
deterministically; scripts/sync-t27-specs.mjs regenerates the snapshot and
records the exact t27 commit, which the page displays.

Route is unlisted, matching the convention for /play and /quantum.

gitignore: bare `demos/` and `runtime/` rules matched at any depth and were
silently dropping six specs from the snapshot; negated for this path only.

Gates: typecheck ratchet unchanged (179/26, new files zero errors), aria refs
resolve, vite build clean, route added to the qa audit list. Verified in a
real browser against the production build, not just compiled.
Three things, all driven by the same point: a corpus that stops at specs/
cannot show a problem living outside it.

CORPUS 497 -> 667. Now every .t27 in the t27 repo, not just specs/.
Excluded: .git/, and .claude/ (git worktrees, i.e. second checkouts of files
already counted -- the same compiler/ast.t27 appears in every worktree).
chips/ alone contributed 147 real specs. Widening it immediately surfaced
problems that were invisible before:

    typecheck failures    60 -> 149
    specs losing content  49 -> 78   (9.9% -> 11.7%)
    backend rejections     0 -> 6    verilog_hir, "duplicate signal name: "

Those six are gf16_to_fp16 / gf32_to_fp32 across chips/{euler,gamma,phi} --
every one outside specs/, so none of them appeared in the earlier scan. Note
the empty name in that error. The compiler's own specs lose content too:
compiler/parser/parser.t27 drops 2 declarations, and 10 more files under
compiler/ drop one each.

SYNTAX HIGHLIGHTING. The .t27 source is coloured from the compiler's OWN
token stream -- the lexer already said what every span is, so there is no
reason to re-guess it with regexes and no way for the colours to disagree
with the compiler. Generated output (Zig/Verilog/C/Rust) has no token stream
coming back, so that falls back to a small regex pass; it is presentation
only. New shared SpecCodeView adds line numbers and a copy button to every
code-bearing layer.

Also: byte/count badges on each tab so backends are comparable without
opening them, and the failing one reads red with an ✕.

NAV. Listed in Navigation.tsx PAGES (EN + RU), so it is reachable rather
than a URL you have to know.

Fixes found by testing rather than by reading:
- header overlapped its own text below ~1100px; inline styles cannot carry
  media queries, so width is tracked and the optional labels drop out
- output dir renamed specs/ -> files/, since paths became repo-root-relative
  and specs/demos/ was landing at specs/specs/demos/
- .gitignore: enumerating per-directory negations was whack-a-mole (the
  widened corpus hit three more bare rules like `runtime/` and `legacy/`).
  Replaced with one `!apps/website/public/t27/**`, which also re-includes the
  excluded directories -- git cannot re-include a file whose parent is
  excluded. 667 staged == 667 in manifest, verified.

All ten layer tabs exercised in a real browser against the production build.
Gates: ratchet 179/26 unchanged, aria clean, build clean.
…peline ribbon

The library was 668 rows sorted by path, which made a catalogue of the corpus
but not a way to see its state. Three changes, all from the same idea: the
working specs are the product, the broken ones are one click away.

HEALTH IS PRECOMPUTED, NOT GUESSED. sync-t27-specs.mjs now runs the same wasm
the browser runs over the whole corpus and records per-spec health in the
manifest. Compiling 668 specs in the browser just to colour a list would take
minutes; doing it at sync time costs one pass and makes the list instant.

    453 ok · 209 warn · 6 fail   (= 668, and they sum on purpose)

Worst-wins and disjoint: `fail` means a backend refused to emit at all, `warn`
means it emitted but the compiler dropped or flagged something, `ok` means
clean through every layer. Counts that do not add up are how a health page
loses credibility, so each spec is counted exactly once.

FILTER CHIPS CARRY THEIR COUNTS, defaulting to Working. Summary and navigation
are one control rather than two that can disagree. Status uses shape as well
as hue (✓ ⚠ ✕) so it survives a colour-blind reader, and a failing row names
its own cause inline -- "verilog_hir ✕ 3 type errors" -- rather than making
you open it to find out.

OPENS ON specs/demos/hello_world.t27 (new, t27#3276), badged START HERE. An
empty pane teaches nothing, and dropping a first-time visitor into a 1600-line
FPGA spec teaches less. That file is 98 lines and clean on every axis.

Each row now shows the spec's own header comment as a description (634 of 668
have one), with the directory dimmed and the basename bold.

PIPELINE RIBBON above the tabs: one bar per stage, height on a shared log
scale, red where a stage failed. It answers "where does this spec die" and
"why is the Verilog four times the Zig" without opening five tabs. Log scale
because linear flattens every stage next to a 45 KB Verilog output. Hand-
written SVG -- a chart library would outweigh the page.

Corpus health renders as one stacked bar rather than a 668-cell waffle: at
that count cells cannot be read as proportions, and six failures would be
three lost pixels.
The interesting decision here was NOT to add a loading indicator.

A compile is 200-300ms, which sits inside the 1-second "flow of thought"
threshold: the user notices a delay but stays in context, and no progress UI
is warranted. A spinner that mounts and unmounts in 250ms is a flash, and
flashes read as jank rather than speed -- the usual mitigation (don't show
before 200ms, then hold 400ms) would pad a 250ms operation to 450ms purely to
make its own indicator look deliberate. A skeleton is worse: two layout passes
inside 250ms, and it throws away the previous pane, which is the most useful
thing on screen. A shimmer needs ~800ms to read as a sweep at all.

So: selection paints immediately -- that sub-100ms response IS the feedback --
and the previous result stays put while the new one compiles. A dim exists but
carries `animation-delay: 200ms`, so a fast compile never starts the ramp and
a pathological one degrades gracefully. No JS timers.

Then the loading state was mostly deleted outright:

    warm the wasm at mount, not at first click   189ms -> 43ms first compile
    prefetch on pointerenter/focus               hover is 80-150ms of free budget
    cache the last 24 results                    revisit measured at 0ms

Motion tokens (--dur-fast 90ms, --dur-mid 160ms, --dur-flag 480ms, --shift
4px) live in one scoped block, which makes prefers-reduced-motion a token
rewrite rather than a retrofit -- 1ms rather than 0s, because at zero
animationend may never fire and anything awaiting it hangs. Reduced motion
drops the movement, never the feedback: colour, glyph and counts all stay.

Pane swaps are enter-only (90ms, opacity + 4px rise), keyed on spec+layer. A
cross-fade would need both panes in the DOM at once -- double the text nodes
for a code view, with a real chance of a scroll blip. Only opacity and
transform are animated; nothing that triggers layout. The AST tree is
deliberately not animated at all: a disclosure that can reveal 400 nodes would
drop frames, and the user is already looking at where the content will appear.

The failing-backend tab flashes exactly once (keyed by spec so it re-fires for
a different spec), never `infinite` -- in a dev tool that failure is on screen
for as long as it takes to fix, and a pulsing badge habituates within about
two minutes, having spent the signal it was meant to carry.

`scrollbar-gutter: stable` on the pane: a code tab that scrolls beside an AST
tab that does not would otherwise shift the pane horizontally on every switch.

Verified in the browser: 20ms first compile, 0ms revisit, no console errors.
…te's own tokens

The library now opens on an ordered course: hello_world as the overview, then
eight numbered lessons, then the rest of the corpus in path order. A gold
"◆ Course" chip filters to just those nine. Each row carries a LESSON n badge
so the reading order is visible without opening anything.

The lessons are new in t27 (t27#3276) and all nine are health-clean, which
matters more than it sounds: a course whose examples are flagged broken
teaches the wrong lesson twice.

Code theme rebuilt from index.css tokens instead of a stock editor palette.
Keywords take --golden (#FFD700), identifiers take --accent (#00FF88), with a
cool cyan for numbers and a soft violet for strings chosen to sit beside those
two rather than compete. Panels moved off pure black to #0b0d0c: on OLED,
white text on #000 smears during scroll, and this list scrolls a lot. Borders
are now a low-alpha accent rather than white.

Every colour clears 4.5:1 on the panel background except `comment`, which is
deliberately quiet at ~4.6:1 and carries nothing a reader needs.
The corpus was one repo. It is five now: t27, tri-net, trinity-fpga, trinity
and tt-trinity-corona -- 676 -> 760 specs. Surveyed rather than assumed: all
213 repos were checked via the git tree API, and those five are the ones that
actually hold .t27 files.

Pulled as tarballs rather than cloned. One request per repo, no working copies
to keep in sync, nothing writable left behind, and the manifest records each
repo's exact commit so the snapshot can be traced.

DEDUPLICATED BY CONTENT, which turned out to matter more than expected. t27
vendors three whole chip repos: chips/{euler,gamma,phi} are byte-identical to
tt-trinity-{euler,gamma,phi}, verified by blob SHA before trusting it. 129
files were duplicates and are kept once. A knowledge library full of copies is
worse than a smaller honest one -- and the effect is visible in the failure
count, which fell 6 -> 3 purely because four of the six were the same two
files repeated across chips.

Two findings from the newly-reachable files:

- `trinity-fpga/t27/specs/base/types.t27` produces 0 tokens and no tree. It is
  not t27 source at all -- it is TRI-27 assembly (`.data`, `.const`) sharing
  the extension. Exactly one file in 760 is like this. Worth knowing that the
  extension does not imply the language.
- Everything else parses. The corpus now sits at 522 ok / 235 warn / 3 fail.

Paths are namespaced by repo (`tri-net/specs/...`) so a spec's real home stays
visible, with t27 unprefixed since it is the primary source.
The page is an input surface now. Source is the landing tab, the text is
editable the moment it loads, and there is no mode to enter first.

TYPING IS THE TRIGGER. A compile is 5-70ms, comfortably inside the window
where a result reads as immediate, so making someone ask for it was the wrong
default. 200ms of quiet fires a compile; every layer and every metric updates
from it. GO stays, and stays useful: it runs without waiting for the debounce,
and it is the affordance that tells you the page compiles at all. ⌘⏎ does the
same from the keyboard.

HIGHLIGHTING SURVIVES EDITING, which a plain textarea cannot do. A transparent
textarea sits over a highlighted layer sharing identical metrics -- same font,
size, line-height, padding, tab-size, wrapping -- with their scroll positions
kept in step. You type into the textarea, you read the layer beneath, and the
compiler receives the textarea's raw value with no DOM in between. Colour
still comes from the compiler's own token stream, so it cannot disagree with
the compiler; the live compile is what keeps those tokens describing the text
actually on screen.

METRICS ARE DELTAS. "3481 tokens" alone says nothing; "3481 (+42)" against
what the shipped spec produced turns this into an instrument. Tokens, nodes,
depth, type errors and dropped declarations all carry their change, and each
backend shows its output size plus whether an edit broke or fixed it.

Two bugs found by testing rather than reading:

- `highlightRef` was declared after the useMemo that reads it. useMemo runs
  its callback during the call, so the ref was still in its temporal dead
  zone -- a runtime throw that tsc does not catch. Moved above.
- The debounce effect depended on `run`, whose identity changes every render,
  so the cleanup cancelled the pending timer before 200ms could elapse and
  the compile only landed on later edits. `run` is reached through a ref now
  and is no longer a dependency.

Also: the RU language audit correctly flagged 446 English spec descriptions.
They are comments quoted verbatim out of 760 source files, not UI copy --
translating them would misrepresent the files and hiding them would gut the
page. They are registered as audit exceptions, generated by the sync script so
the list cannot drift from what renders. The gate stays strict for real UI.
Every run of Claude Code Review has failed since it was added: 12 for 12
across six different branches, not something one PR introduced.

The job granted `pull-requests: read`, while its own prompt ends with "Use
`gh pr comment` with your Bash tool to leave your review as a comment on the
PR". A read-scoped token cannot comment, so the run reached its single turn
and errored. `pull-requests` and `issues` are now `write`.

The reason this took a while to see is worth fixing too: the log said only

    Claude result reported subtype success with is_error:true

with the actual message swallowed behind "Rerun in debug mode or enable
`show_full_output: true`". That is now enabled, so the next failure states its
own cause instead of requiring a rerun to find out.

claude.yml carried the identical permissions bug. It has only ever been
skipped -- nobody has @claude-mentioned yet -- so there it is latent rather
than observed, but it would fail the same way on first real use. Fixed
alongside.
…laude_args

The log's own suggestion -- enable `show_full_output: true` -- names an input
claude-code-action@v1 does not have. Its inputs are trigger_phrase,
assignee_trigger, label_trigger, base_branch, branch_prefix,
branch_name_template, allowed_bots, allowed_non_write_users,
include/exclude_comments_by_actor, prompt, settings, the auth and provider
set, github_token, claude_args and additional_permissions. Checked against
action.yml at the pinned SHA rather than assumed.

Passing it also left the file with two `claude_args` keys, which is not valid
YAML for a step's `with:` map. Both problems removed: verbosity now rides on
claude_args as `--verbose`, and there is one key again.

The permissions fix in the previous commit stands and is confirmed landed --
the runner now reports `Issues: write` / `PullRequests: write` where it
previously reported read.
…very spec

TAGS, DERIVED NOT HAND-APPLIED. 760 specs cannot be tagged by hand and stay
correct, so every tag comes from a signal already in the file: its path, the
AST node kinds it really contains, and what the backends produced. 43 tags in
six families -- domain/, has/, issue/, size/, src/, health/ -- and every spec
carries at least one.

Multi-select filtering with AND semantics, because "FPGA specs that have tests"
is the question people actually ask. Facet counts recompute against the rest of
the filter, so a tag never promises results it cannot deliver; a tag that would
yield nothing is disabled rather than a dead click.

DEEP LINKS, which sharing needed first. Selecting a spec now writes
#/specs?spec=<path> via replaceState -- replaceState, not push, so browsing 760
specs does not bury the back button. A shared link reopens that exact spec, and
switches to the All filter when the linked spec is not in the default Working
view, since otherwise the link would land on an empty list.

SHARING to X, Telegram, LinkedIn, Hacker News and Reddit, plus copy-link. The
share text carries the spec's measured facts -- tokens, AST nodes, backends
emitting -- rather than a generic blurb.

CONTRIBUTE, with no backend involved. "Propose a fix" opens GitHub's own editor
for the file: for anyone without push access GitHub forks the repo and offers
the pull request, which is the auto-PR path using the visitor's own session and
no credentials passing through us. "Report" opens a prefilled issue carrying
what the compiler measured, and shows a count when the spec actually has
problems, so reports arrive with evidence instead of "it doesn't work".

One limitation stated in the code rather than papered over: the editor draft is
not pushed into the URL. GitHub's `value` prefill only applies to new files,
and a multi-kilobyte spec would exceed URL limits regardless.

DETAILED DESCRIPTIONS. Each spec gains a generated paragraph beside its header
comment -- what it declares, what it carries, what it compiles to, what it
emits, and why it is flagged. Built from the compile, so it cannot drift from
the artifact the way a hand-written blurb would.

Gates: ratchet unchanged at 179/26, aria clean, vite build clean.
… brains

The corpus is the Queen's core, so SPECS sits second in the HUD -- directly
after COMB, hotkey 2 -- rather than as an appendix behind the board views.

THE VIEW IS THE DOCTRINE'S OWN SCOREBOARD. It states the standing directive
(every part of the project expressed as a .t27 spec, the project generated from
those specs, spec as source of truth and Zig/Verilog/C/Rust as outputs) and
then puts the measured distance right underneath it:

    522 clean · 235 flagged · 3 rejected · 760 specs across 5 repositories

and, in the panel that keeps it honest: the generated Verilog synthesises to
0 LUTs and 0 flip-flops across every spec Yosys accepts -- module shells with
IBUF/OBUF and nothing behind them -- while the bitstreams running on the board
today come from hand-written RTL. The directive is the goal; that line is the
distance. A directive with no counter beside it is a slogan.

Numbers come from the same manifest the Spec Explorer uses, produced by running
the real compiler over every spec, so the panel cannot drift from the corpus.

BOTH BRAINS CARRY IT.

- The bees': `agentBootstrapText` is what every agent joining over A2A reads.
  It now carries the directive, including the instruction to prefer replacing
  hand-written code with a spec that generates it over maintaining both -- and
  explicitly that this is the goal and not the current state, so an agent
  measures rather than asserts.
- The Queen's: doctrine 01 no longer says only "boundary, scenarios,
  requirements". It says those are written as a .t27 spec and that every
  language output is generated from it.

Three layout bugs, each found by measuring the rendered page rather than
reading the CSS:

- The sections inherited `display: flex` (row) from an existing rule, so each
  label sat beside its paragraph and the section collapsed to the label's
  height -- sections then painted over one another.
- Fixing that with a blanket `> section { display: block }` clobbered the stats
  row, which is itself a section; it needs a higher-specificity selector to
  keep its flex.
- Flex children shrink by default, and this column lives in a height-
  constrained HUD body, so the sections compressed below their own text.
  `flex-shrink: 0` on the children; the container scrolls instead.

Styles live in Queen.css, not index.css -- the first version went to the wrong
file and only appeared to work because index.css is global.

Gates: ratchet unchanged at 179/26, aria clean, build clean, layout verified
in the browser with an explicit overlap check (0).
# Conflicts:
#	apps/website/src/pages/Queen.tsx
…lays

The audit failed on /specs with lines like

    ; A trit is the unit this language is built on: three states, not two.
    // A function declares its parameter types and its return type.

Those are comments inside hello_world.t27, rendered because the page opens on
the Source tab. They are the spec's own text, quoted verbatim, in whatever
language its author wrote. Translating them would misrepresent the file, and
the page's actual UI is fully translated -- so the gate was right that the
words are English and wrong about what it was looking at.

`collectRouteText` now clones the body, drops any `[data-lang-exempt]`
subtree, and reads the rest. The editor and the code view carry that
attribute; everything around them is still audited. This narrows the gate
rather than excluding the route, which would have switched it off for real UI
copy on the same page.

Verified by running the audit against the production build rather than
reasoning about it: RU-аудит PASS across 28 routes.
… of it

The first version showed corpus counts and the directive. That was a readout
about the tool; what belongs in the Queen is the tool. Picking a spec, editing
it, pressing GO and watching every layer recompile now happens inside the HUD.

An iframe rather than the SpecExplorer component inline, for two concrete
reasons: SpecExplorer owns a full-viewport shell with its own header and
sidebar and would fight the HUD's grid, and it instantiates a 477 KB compiler
wasm that the host page has already loaded. Same origin, so the frame is a
layout boundary rather than a security one -- which is exactly what was needed.

SpecExplorer gains ?embed=1, which drops its page header. Inside the HUD that
header would be a second title bar directly under the sector strip.

The directive is now one line above the frame with the health counts inline
(522 clean, 235 flagged, 3 rejected). The full statement and the measured gap
live inside the explorer, where the numbers are already being computed -- no
second place to keep them in sync.

Verified in the browser at 1600x1000: strip 990x96, frame 990x635, and inside
the frame the editor, the GO button and a hidden header. An earlier measurement
read 32x0 and was not a layout bug -- the browser pane itself had collapsed to
16x16, so every element in the chain measured near zero. Worth checking the
viewport before believing a zero.
The default filter was Working. That is 522 specs with no suggested order --
someone arriving has no way to know which one to open first, and the catalogue
answers a question they have not formed yet.

The course does have an order: hello_world, then eight lessons, each clean
through every layer. It is now the default group, so the page opens on a path
through the corpus rather than a pile of it. Every other group stays one click
away and still carries its count.

Also widens the deep-link override. It read `health !== 'ok'`, which was the
right test against a Working default and the wrong one now: a link to a healthy
spec outside the course would have landed on a filter excluding the very spec
it named -- an open file beside a list that does not contain it. It now checks
membership in the course instead.

Verified in a browser on a fresh load, which is what a shared link actually is:
the default shows Course 9 with the nine lessons in reading order, and a link
to chips/euler/specs/fpga/gf16_to_fp16.t27 widens to All 760 with that spec
loaded in the editor.

Worth recording: an in-page hash change does NOT re-run the deep-link handler,
because it only runs on mount and the SPA does not remount. That is correct for
a shared link and would be wrong if anything ever navigated by hash internally.
Every .t27 spec gets an animated schematic of the datapath it declares, and
the formats that have actually been through the AX7203 carry their evidence
chain beside it.

The schematic is derived from declarations the language already forces you to
write: the bit width of every constant, the field layout of every packed
struct, and the parameter and return widths of every function. Buses are drawn
as parallel lanes with a travelling pulse, pausable.

It is deliberately NOT presented as a netlist. The .t27 to Verilog backend
emits module shells today -- 361 of 676 specs produce Verilog yosys accepts,
and every one yields 0 LUTs and 0 flip-flops -- so no placement is drawn and
the panel says why.

The hardware section is keyed by an explicit spec-path table rather than by
name similarity, so a spec cannot acquire a hardware claim by resembling one.
gf4/8/12/16/20/24 carry ADD and MUL measured on silicon; SUB is shown as
"bitstream ready, not yet flashed" rather than as a result. Specs with no run
say so plainly instead of rendering an empty timeline that reads as pending.
Figures are transcribed from GOLDENFLOAT_HW_CONFORMANCE_v0.2 and its evidence
chain: CI run, bitstream SHA-256, JTAG flash, UART log.

The panel states that the silicon results belong to hand-written RTL of the
same format, not to the Verilog these specs compile to.

Rows drawn are capped at 12; the diagram scales to the panel, so a 53-function
spec would otherwise shrink every label past reading. The remainder is counted
in words.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gHashTag added a commit to gHashTag/trinity-fpga that referenced this pull request Sep 6, 2026
… of stale

D98: the Spec Explorer gets a Chip tab. Every .t27 spec is drawn as the
datapath it declares -- constant widths, packed-struct field layouts, function
parameter and return widths -- with animated buses. Formats that have been
through the AX7203 carry their evidence chain beside the drawing: gf4/8/12/16/
20/24 ADD and MUL on silicon, SUB shown as bitstream-ready rather than as a
result, everything else saying "no hardware run" outright. Keyed by an explicit
spec-path table so no spec can acquire a hardware claim by resembling one.
gHashTag/trinity#953.

A43: most of the iteration went to a feature that "did not render" while the
code was correct. Three staleness layers stacked: the preview server was
`vite preview` serving a prebuilt dist/ and never reading src/; `npm run
typecheck` is `tsc -b`, incremental, which re-checked nothing and exited quiet;
and the browser held a cached index.html pointing at the previous chunk hash.
Forcing the checker immediately surfaced a real ReferenceError in my own file
-- `active?.path` where the scope's variable is `selected`. A checker that
skipped the file reports exactly what a checker that approved it reports.
One command ended it: `ps -eo pid,args | grep vite`, asked after three failed
hypotheses rather than before the first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gHashTag

gHashTag commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

The Chip tab and hardware-history work in this branch is being sent to main
as a separate, minimal PR instead of riding this one.

Reason: this branch is 16 commits and 787 changed files, and it conflicts with
main — a single add/add conflict on apps/website/src/pages/SpecExplorer.tsx,
because that file was added independently on both sides. Comparing the two
versions, main's file and this branch's are content-identical apart from the
chip work and a RUNGO button rename. So the chip feature needs none of
the other 786 files to land, and the live site deploys from main.

The minimal PR adds three new files and five small edits to main's existing
SpecExplorer.tsx, with the RUN/GO rename left out as unrelated scope.

This PR stays open for the rest of the branch, which still needs its conflict
resolved and a decision on whether the whole line is wanted on main.

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