Repository navigation
Five specs stopped generating; CI named four of them by path and the merges went through anyway #4808
Description
Re-filed from #4488. An attempt claimed that issue on 2026-09-22 and left an empty branch, and a dispatch row that has spent its retry ceiling keeps its issue for good - so no bee could take it again, however much the brief improved. The work below is carried over unchanged.
Context
Six specs on master generate under no backend and are not in
tools/specs_generate_baseline.txt. Measured on 322af1b27 by running
python3 tools/check_specs_generate.py --update-baseline, reading the diff, and
reverting it — the baseline in the tree is unchanged:
specs/account/auth.t27 parse error in fn 'logout' near line 127
specs/ml/optimizer/adamw.t27 parse error in fn 'get_effective_betas' near line 574
specs/port/tools/wp18_gate_selfconsistent_selftest.t27 parse error at module level near line 3
specs/tri/collections/maybe.t27 Expected LBrace, got RParen (')') at line 34:50
specs/tri/graph/graph_bfs.t27 parse error in fn 'init' near line 31
specs/tri/utils/config.t27 Expected LBrace, got Pipe ('|') at line 42:30
Five of the six used to generate. Each was broken by the single commit that
last touched it. Tested, not inferred: each file was restored to its parent
revision in place and put to t27c gen / gen-c / gen-rust / gen-verilog.
5 of 5 generated before, 0 of 5 after.
| Spec | Commit | PR | Merged |
|---|---|---|---|
specs/tri/utils/config.t27 |
2ed1fb047 |
#3646 | 2026-09-16 |
specs/tri/graph/graph_bfs.t27 |
0d6c079f2 |
#3626 | 2026-09-16 |
specs/tri/collections/maybe.t27 |
4da46b390 |
#3662 | 2026-09-16 |
specs/ml/optimizer/adamw.t27 |
b349809f5 |
#3909 | 2026-09-17 |
specs/account/auth.t27 |
707949095 |
#4046 | 2026-09-17 |
Every one of those commit messages says it implemented the spec.
The sixth, specs/port/tools/wp18_gate_selfconsistent_selftest.t27, was added
2026-09-20 in #4343 and has never generated. Not a regression — an
unbaselined entry the gate should be red on.
The part that is not a surprise, and the part that is
emit-bitexact ran on all five PRs and failed on all five. On four of them
it printed the broken file by name with the compiler's own message. From
#3646's run, verbatim:
FAIL: 1 spec(s) newly do not generate with any backend
specs/tri/utils/config.t27
Error: Compile error: parse error in fn 'parse' near line 42: Expected LBrace, got Pipe ('|') at line 42:30
The gate did its job and the merge happened anyway. master is
protected — by the ruleset t27-master-protection (active), not by classic
branch protection. Its required_status_checks rule names exactly three
contexts:
validate
check-linked-issue
parse-ratchet
emit-bitexact is not one of them, so it is advisory: it goes red, it is
visible on the PR page, and the merge button stays enabled. That is the
dominant cause here — 4 of 5 — and the fix is one line of repo configuration,
which is the owner's to make and is deliberately not made from this issue.
Correction. The first version of this issue said master had no branch
protection, citingGET /repos/.../branches/master/protection→ 404 "Branch
not protected". That endpoint reports only classic protection and returns
404 for a repository protected by a ruleset, which this one is. The
conclusion survives — a redemit-bitexactdoes not block a merge — but the
reason is a short required-checks list, not an unprotected branch. Read the
ruleset withgh api repos/gHashTag/t27/rules/branches/master.
The fifth is a bug in this file. On #4046 the gate printed:
FAIL: 2 spec(s) in the baseline now generate. Remove them from specs_generate_baseline.txt ...
and said nothing about specs/account/auth.t27. It had already computed it.
new is built at line 461, and fixed is tested at 485 and return 1s at
line 500 — twenty-seven lines before new is printed at 527. The
regression was found, held in a variable, and discarded by an earlier exit.
This matters more now than it did then. fixed on master today is
12, and stays non-empty until someone blesses the baseline. Until that
happens the new arm is unreachable: every newly-broken spec from here on
is reported as a stale-ledger complaint about twelve unrelated files.
tools/check_specs_generate.py has a mutation test for exactly this arm —
spawned("newly broken spec", 1, "FAIL: 1 spec(s) newly do not generate", ...)
at line 288. It passes, against a fixture tree where fixed is empty. It
proves the arm works in isolation, not that it can be reached.
Also, while the same output is being read
for sp in fixed[:10] at line 495 truncates with no overflow line. The current
failure says 12 and prints 10 — specs/vsa/packed_vsa.t27 and
specs/vsa/sequence_hdc.t27 are invisible. The departed branch ten lines
above guards its own truncation with ... and N more; this one does not.
What to write
- Report every class, then exit. Collect
departed/fixed/leaked/
newinto one verdict and return non-zero once at the end, so no class can
shadow another. The four messages keep their exact current wording — the
self-test at line 288 matches on them, and two NOW documents quote them. ... and N moreon thefixedtruncation, matchingdeparted.- A mutation case that pins the interaction, not just the arm: a fixture
with a non-emptyfixedand a newly-broken spec, asserting both strings
appear. The existing case at line 288 cannot fail this way.
Границы
- The baseline is not to be blessed as part of this.
--update-baseline
turns the board green and writes five live regressions into the ledger as
though they were always broken. Whoever fixes the five specs removes their
own lines; the twelve that started generating are a separate, legitimate
bless and want their own PR and their own reasoning. - The five broken specs are out of scope here. This issue is about the gate
being able to say so. Repairingconfig.t27,graph_bfs.t27,maybe.t27,
adamw.t27andauth.t27is five separate pieces of work. - Branch protection is the owner's decision, not this issue's. It is named
because it is the measured cause of 4 of the 5, and it is deliberately not
changed here. BACKENDSstays("c", "rust", "verilog")withZIG = "gen".jsis
not added to the census:gen-jsemits declarations only, so a spec whose
functions never lower would count as generating and weaken the gate.
Acceptance
- A tree with both a stale ledger line and a newly-broken spec prints both,
and exits 1. - A 12-entry
fixedprints 12 paths, or 10 and... and 2 more. -
tools/check_specs_generate.py --self-testcovers the interaction. -
tools/specs_generate_baseline.txtis byte-identical before and after.
The instruments
Every one of these reads and prints; none of them writes. Run them from the repository root, with <spec> replaced by the file in ## Boundary:
.t27 is not Rust and not Zig, and a bee fluent in either writes one by
accident. Measured 2026-09-20: a bee filled eight bodies in
specs/file/watcher.t27 with return Ok(());, Err(FileError::WatcherNotFound)
and for i in 0..watchers.length. The parse ratchet refused the whole file --
Unexpected token in expression: RParen, line 123 -- and a spec that does not
parse generates nothing, so every test it already carried stopped running.
At the top level the parser accepts exactly these eight forms, with an optional
pub, and no others:
const var fn enum struct test invariant bench
There is no trait, no impl, no type X = ..., no generics, no macro. What
a bee reaches for from another language, and what happens:
- Rust's result sugar (
Ok(()),Err(E::V),?) is not a construct here. - Rust macros (
println!,format!,vec!) are not constructs here. - A range loop (
for i in 0..n) is Rust and Zig, not this language. - Zig builtins (
catch unreachable,@intCast,@constCast) are not constructs here. - Cross-module reuse (
use other::fn;) parses and then generates a comment and an unqualified call, so the Zig fails withuse of undeclared identifier(use a::b; generates no import, so no spec can reuse a function from another spec #4298). It does not work yet.
t27c parse <spec> answers in one line whether what you wrote is the language.
Run it before you report, every time.
t27c spec-status <spec>- the compiler's one-word verdict: IMPLEMENTED, PARTIAL, UNWRITTEN, NOPARSE, NOFN. Exit code is 0 whatever it says, so read the word.t27c symbols <spec>- every name the file declares, with its kind. Answers "does this already exist here?" before you add it.t27c outline <spec>- per function: its locals, what it calls, what it returns. The contract you are implementing against.t27c coverage <spec>- which functions have a test and which do not. The issue asks for tests; this is how you check you wrote them.t27c lint <spec>- style and shape warnings. It OVER-REPORTShas no test or invariant: measured 2026-09-20 over 60 specs it printed 677 of those wherecoveragefound 190 untested functions, disagreeing on 59 of the 60 - it warns aboutbit_to_trit_pairinspecs/base/ternary_encoding.t27, whichtest bit_to_trit_pair_zerocalls on line 249. Read it as a hint;coverageis the answer.t27c typecheck <spec>- types, before generation. PrintsTypecheck OK (0 errors, 0 warnings)or the errors.t27c test-report <spec>- builds this spec and runs its own tests, which is what the oracle does.BLOCKEDmeans the generated Zig does not compile, with the error beside it.python3 tools/dupe_scan.py --name <function>- where that function already lives, if it does. 576 of 4021 bodies here are byte-identical copies.
The rest of the toolbelt is in docs/BEE_TOOLBELT.md.
Activity
gHashTag commentedon Oct 5, 2026
Closing as done: 44c68ab (#4562) rewrote the verdict so every class (departed, fixed, leaked, new) reports and the exit code is taken once (T118 in tools/check_specs_generate.py), and added the self-check pair 'a stale line does not hide a new break' / 'and the stale line is still reported' that pins exactly the interaction asked for, without blessing the baseline. The one residual, a '... and N more' line after fixed[:10] at line 547, is a cosmetic edit to a Python tool and cannot be done under the only-t27 rule.
Review-jam triage 2026-10-05 (lane B): this card sat in the Queen's review column holding its file boundary. Reopen if this is wrong.