Skip to content

fix(specs): three ported specs were Zig and Python, and the gate reported the oldest problem instead - #4562

Merged
gHashTag merged 3 commits into
masterfrom
fix/link-relay-t27-port
Sep 21, 2026
Merged

gHashTag merged 3 commits into
masterfrom
fix/link-relay-t27-port

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Three specs under specs/port/tools/ were not t27 and had never compiled on any backend. All three landed on 2026-09-21 from the same porting campaign, and the gate that exists to catch exactly this reported a different, older problem every time it ran.

Closes #4559. Closes #4560.

The specs

link_relay.t27 (#4533) run_conformance_vvp.t27 (#4553) check_fix_carries_source.t27 (#4556)
was Zig transliterated Python Zig with let, 565 lines against a 284-line source
tell ?struct{...}, 23 @intCast(x as T), 14 try std.testing.expect 51 let, 4 try, + string concatenation 52 std.*, 30 catch, 27 x += 1, 25 '\n' as u8, 6 error.*
now 0 errors, 0 warnings, 4 backends exit 0 same same

Each was rewritten against a neighbour that actually generates, not patched until the parser stopped complaining.

link_relay.t27 — the optional is gone rather than papered over: DrWord { ok, value } keeps the distinction the Python made with None, so a caller that ignores ok reads a zero instead of a word the hardware never sent. The two IR loops, spelled four times between xfer and request, are one chain_ir(idx, chain, sel). One of its seven dead tests asserted 0x1F3DF where the arithmetic gives 0x1F39F; a test that cannot compile cannot be wrong out loud. The rewrite's constants were checked against an independent computation of the same loop.

run_conformance_vvp.t27 — the transliteration did not only fail to parse, it changed what the tool decides:

  • The emitted testbench declares integer fails = 0 and increments fails, and the one line that reports success reads if (folds == 0). folds is declared nowhere, so the pass line is unreachable.
  • vvp_path is bound to the compiled output (line 76), then rebound to the simulator binary (line 94), then passed twice: vvp vvp. The Python's literal "vvp" became a which lookup that overwrote the variable it was meant to sit beside.

Neither is a parse error. A port that only fixed the syntax would have kept both. The rewrite keeps the decidable part — the prescaler encoding, the two-condition pass rule, T117 (absent is not failed), T67 (zero executed cases is a failure however clean the run) — and stubs the OS.

check_fix_carries_source.t27 — states the verdict lattice, the inversion in is_prose, and the rule that a guard which cannot read its own definition reports could-not-run rather than passing. It deliberately does not restate SOURCE_SCOPES or PROSE_EXT: those have one definition in cli/tri/src/hooks.rs, and the Python parses it rather than copying it — "two readers with two copies of a list is how a rule drifts into two rules." A third copy here would be the defect that tool exists to catch, so every predicate takes the answer to a lookup rather than the table.

The gate

tools/check_specs_generate.py had four return 1s in a row, so the first class it found was the only class it reported — and fixed, the mildest, was checked three lines before new, the most serious.

specs/ml/optimizer/adamw.t27 was repaired by #4540 and its ledger line was left behind. From then on every run printed "1 spec(s) in the baseline now generate" and returned before scanning for anything newly broken. All three specs above landed inside that window.

  • All four classes now report; the exit code is taken once at the end.
  • The stale-ledger branch had no self-check case at all — the only return 1 in main() that nothing ever executed, and the one that caused this. It has one now.
  • Plus a case that plants a stale line and a new break in the same tree. Every existing planted world triggers exactly one class, so all of them passed against the broken ordering.

The ordering case was measured against the defect rather than asserted: restoring the return 1 makes a stale line does not hide a new break report CONTROL FAILED, while the two branch cases correctly survive it.

python3 tools/check_specs_generate.py now exits 0 — 1027 specs, 952 generate, 75 known-broken.

Границы

Found while doing this, filed rather than folded in:

  • A call to a builtin that does not exist typechecks clean, generates, and reaches the C verbatim -- 41 specs classed Working do it #4561 — a call to a builtin that does not exist typechecks clean, generates, and reaches the C verbatim. 360 calls to 69 phantom names across 55 specs; 41 of them pass gen today and are counted as Working; 35 of those emit the invented name into their generated C. Arity and argument types are unchecked for the 28 real builtins too. This is why check_fix_carries_source.t27 could not simply call @startsWith and @split — three shipped specs do, and that string library does not exist.
  • undefined lowers to {0};, which is not valid C. read_user1.t27, classed Working, fails cc with 6 errors for the same reason. A backend defect across every spec that stubs a body, and not introduced here.
  • gen-c exits 0 on specs every backend rejects, which is why the gate has to shell out four times.
  • The porting campaign itself: 15 specs under specs/port/, of which 8 never compiled — the 5 still in the baseline plus these 3.
  • emit-bitexact triggers on pull_request only, so master accumulates reds that first surface on an unrelated branch.

@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-21 15:10:12 UTC

Summary

Status Count
Total Open PRs 25
PRs with Failing Checks 22
PRs with All Checks Green 3
READY 2
FAILING 22
PENDING 0
NO CHECKS YET 0

These columns do not partition: 2 + 22 + 0 + 0 = 24, and there are 25 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=403499176a5d != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-21 15:27:33 UTC

Summary

Status Count
Total Open PRs 25
PRs with Failing Checks 22
PRs with All Checks Green 3
READY 2
FAILING 22
PENDING 0
NO CHECKS YET 0

These columns do not partition: 2 + 22 + 0 + 0 = 24, and there are 25 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=403499176a5d != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

specs/port/tools/jtag/link_relay.t27 landed in #4533 today with Zig bodies
in a file named .t27: ?struct{} at line 104, 23 @intcast(x as T), 14 try
std.testing.expect, m.tms(&[...]), while (i < n) : (i += 1), .{ .role = r },
null/.unwrap()/.or(0). It failed on gen-c, gen-rust, gen-verilog and gen.

Rewritten against read_user1.t27 -- its neighbour in the same directory and
the same JTAG/MPSSE domain, and a spec that does generate. t27c check now
reports 0 errors and 0 warnings and all four backends exit 0.

The optional is kept rather than dropped: DrWord { ok, value } preserves the
distinction the Python drew with None, so a caller that ignores ok reads a
zero instead of a word the hardware never sent. The chain-IR loop, written
out four times between xfer and request, is one chain_ir(idx, chain, sel).

One of the seven dead tests asserted 0x1F3DF where the arithmetic gives
0x1F39F. A test that cannot compile cannot be wrong out loud.

adamw.t27 was fixed by #4540 and left in the baseline. check_specs_generate.py
short-circuits on "baseline now generates" and never reaches "newly does not
generate", so every run stopped at adamw and never looked at link_relay. The
stale line is pruned here; that is what made this visible.

Fixes #4559
…ed the oldest problem instead

Fixes #4560

specs/port/tools/run_conformance_vvp.t27 and check_fix_carries_source.t27
landed on 2026-09-21 and have never compiled on any backend. The first is
transliterated Python, the second 565 lines of Zig with `let` against a 284
line source.

The transliteration also changed the verdict. The emitted testbench
increments `fails` and reports success on `if (folds == 0)`, a name declared
nowhere, so the pass line is unreachable. `vvp_path` is bound to the compiled
output and then rebound to the simulator binary, and the run becomes
`vvp vvp`. Neither is a parse error.

Both rewritten as specs: 0 errors, 0 warnings, four backends exit 0.
check_fix_carries_source.t27 states the verdict lattice and the is_prose
inversion but deliberately does not restate SOURCE_SCOPES or PROSE_EXT --
those have one definition in cli/tri/src/hooks.rs and the Python parses it
rather than copying it.

tools/check_specs_generate.py had four `return 1`s in a row, so the first
class found was the only one reported, and the mildest was checked three
lines before the most serious. One stale adamw ledger line left by #4540
blinded it for the whole window in which #4533, #4553 and #4556 landed. All
four classes now report and the exit is taken once. The stale-ledger branch
had no self-check case -- the only verdict in main() nothing executed -- and
now has one, with a case that plants a stale line and a new break together.
Refs #4560

`tri types ratchet` read 81 conflicted names against a ledger of 80.

`Context` was mine. The rewritten check_fix_carries_source.t27 named its
pull-request record `Context`, a name specs/neural/forward_pass.t27 and
specs/queen/lotus.t27 had already given to two other things. Renamed to
`PrEvent`, which is what it is; `context_incomplete` moved with it to
`pr_event_incomplete` across 10 call sites.

`Edge` came from check_graph_law8.t27 -- the same porting campaign, landed
the same day. specs/tri/graph/bellman_ford.t27 has declared `Edge` as
from/to/weight since long before it; the port's is from/to/kind. Two
concepts, one name, and nothing for a cross-spec resolver to break the tie
with. The newcomer takes the narrower name: `DepEdge`.

Ratchet now reads `ledger 80, observed 80 -- CLEAN`. All four backends
(gen-c, gen-rust, gen-js, gen-ts) exit 0 on both files.

This is also how the ratchet is meant to be read. Resolving `Mpsse` while
introducing `Context` left the count unchanged; only the identity-keyed
list showed that two separate things had happened.
@gHashTag
gHashTag force-pushed the fix/link-relay-t27-port branch from 65a07fd to 81b5dac Compare September 21, 2026 15:28
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-21 15:28:56 UTC

Summary

Status Count
Total Open PRs 24
PRs with Failing Checks 21
PRs with All Checks Green 3
READY 2
FAILING 21
PENDING 0
NO CHECKS YET 0

These columns do not partition: 2 + 21 + 0 + 0 = 23, and there are 24 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=403499176a5d != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@gHashTag
gHashTag merged commit 44c68ab into master Sep 21, 2026
33 of 36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant