Skip to content

Every dropped token now names the recovery that took it - #2776

Merged
gHashTag merged 3 commits into
masterfrom
w699-drop-causes
Aug 29, 2026
Merged

Every dropped token now names the recovery that took it#2776
gHashTag merged 3 commits into
masterfrom
w699-drop-causes

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Refs #2754

Yesterday's tri discard classify was a keyword match over printed traces: it saw the word forall and called the bucket forall. The parser already writes down every token it throws away, so the question is now asked of the record.

What the record says, and it is not what the heuristic said

channel               tokens    runs
bdd-block-fallback     23852    1484     78%
brace-body              4602     459
top-level-resync        1894      99
stray-brace / stmt-recovery       the rest

against yesterday's forall/==> … 20991.

Five recovery sites now record a channel beside each span, and restore_bdd_fallback marks its resync so a whole-block fallback is distinguishable from an ordinary top-level skip — both call the same function. parse-complete --causes groups by (channel, head-of-run); tri discard classify reads that instead of guessing.

The old buckets are deleted, not kept as a cross-check. A heuristic standing beside a reading is two answers to one question — the shape of #2767, one level down.

brace-body at 4 602 tokens is a channel nobody has looked at: a braced body skipped wholesale, a different defect class from the braceless fallback.

Rung 5 — the parser named the shape, then threw it away

// BDD-style fn: `fn name() given ... then ...` -- a keyword-style test
// spelled as a fn (linker.t27). Detect BEFORE return-type parsing.
if self.current.kind == TokenKind::Ident && self.current.lexeme == "given" {
    self.skip_to_next_top_level();   // <- every clause, gone
    return Ok(decl);
}

The comment cites the file it silently empties. Someone understood the shape well enough to special-case it and stopped one line short of lowering it. Detection is unchanged — same token, same lexeme — and the action is now the shared clause parser that already serves test, invariant and bench.

before after
specs discarding 87 86 — the first time the population has moved
discarded tokens 30 451 30 408
Zig / rustc / cc / iverilog / ALL FOUR 217 / 214 / 158 / 373 / 63 unchanged

Only 43 tokens, against the 2 453 that given heads corpus-wide — and that gap is the finding. A head token says what the parser stopped on; a channel says which recovery threw it away. Reporting one as the other overstates every fix you plan.

The arm stays narrow: the corpus holds exactly one occurrence of the shape, so broadening it would be speculative.

An accounting gap, found and closed

The channel vector was pushed at "all three" recording sites. Two more existed at a different indentation, and zip truncated to the shorter vector and printed a clean, plausible table 27 tokens short. Nothing failed. The only signal was a total that disagreed with parse-complete's.

if parser.dropped_spans.len() != parser.dropped_channels.len() { return Err() }

Refuses instead of truncating. The two accounts now agree exactly at 30 408 — and the reason the gap was findable at all is that a second independent account of the same quantity already existed.

parse-conform               21/21, new case load-bearing
cargo test --no-fail-fast   2429 passed, 0 failed
suite --ratchet             RATCHET: CLEAN, ledger 178 -> 177
check_seal_coverage.py      exit 0
check_specs_generate.py     exit 0

🤖 Generated with Claude Code

…k it

W699 rungs 5 and 6. Yesterday's classification was a keyword match over printed
traces -- it saw the word `forall` and called the bucket `forall`. The parser
already writes down every token it discards, so the question is now asked of the
record.

WHAT THE RECORD SAYS, and it is not what the heuristic said:

    channel               tokens    runs
    bdd-block-fallback     23852    1484     78%
    brace-body              4602     459
    top-level-resync        1894      99
    stray-brace / stmt-recovery       the rest

    heuristic yesterday:  forall/==> ... 20991

Five recovery sites now record a channel beside each span, and
`restore_bdd_fallback` marks its resync so a WHOLE-BLOCK fallback is
distinguishable from an ordinary top-level skip -- both call the same function.
`parse-complete --causes` groups by (channel, head of run); `tri discard classify`
reads that instead of guessing, and the old buckets are DELETED rather than kept
as a cross-check: a heuristic beside a reading is two answers to one question.

RUNG 5: the parser named the shape and threw it away.

    // BDD-style fn: `fn name() given ... then ...` -- a keyword-style test
    // spelled as a fn (linker.t27). Detect BEFORE return-type parsing.
    if ... lexeme == "given" {
        self.skip_to_next_top_level();   // <- every clause, gone
        return Ok(decl);
    }

The comment cites the file it silently empties. Detection is unchanged -- same
token, same lexeme -- and the action is now the shared clause parser that already
serves `test`, `invariant` and `bench`.

    specs discarding    87 -> 86    (the first time the population has moved)
    discarded tokens 30451 -> 30408
    Zig 217, rustc 214, cc 158, iverilog 373, ALL FOUR 63 -- all held

Only 43 tokens, against the 2 453 that `given` heads corpus-wide. That gap IS the
finding: a head token says what the parser stopped on, a channel says which
recovery threw it away, and reporting one as the other overstates every fix you
plan. The arm stays narrow because the corpus holds exactly one occurrence --
broadening it would be speculative.

AN ACCOUNTING GAP, FOUND AND CLOSED. The channel vector was pushed at "all three"
recording sites; two more existed at a different indentation. `zip` truncated to
the shorter vector and printed a clean, plausible table 27 tokens short. Nothing
failed -- the only signal was a total disagreeing with `parse-complete`'s. The
accessor now REFUSES on a length mismatch rather than zipping, and the two
accounts agree exactly at 30 408.

    parse-conform              21/21
    cargo test --no-fail-fast  2429 passed, 0 failed
    suite --ratchet            RATCHET: CLEAN, ledger 178 -> 177
    check_seal_coverage.py     exit 0
    check_specs_generate.py    exit 0

Skill 162-164.

Refs #2754

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@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-08-29 02:23:11 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=976a3c3414a9 != 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).

The elaboration ratchet caught the previous commit and it was right: linker
4 -> 6.

Keeping the FnDecl and filling its body made every backend treat it as a real
function. `gen-verilog` emits synthesizable RTL for functions and deliberately
does NOT lower tests, so the recovered clauses became

    \assert ((is_local(sym) == 1'b1));
    is_local_test = \assert ((is_global(sym) == 1'b0));

inside a task -- "Enable of unknown task ``assert''", and an assignment to the
task's own name. A task body cannot assert, and the gate said so.

`fn name() given ... then ...` IS a test, so it now parses to a TestBlock. That
teaches no backend anything new: each already knows whether it lowers tests, and
each now does the right thing with this one.

    elaboration errors  176 (baseline 176) -- OK, no module gained any
    discarded tokens    30408, 86 specs -- unchanged from the previous commit
    Zig 217, rustc 214, cc 158, iverilog 373, ALL FOUR 63 -- all held

Two notes on how this was nearly missed. I first measured with bare `iverilog`,
which reports "Size cast requires SystemVerilog" for constructs the gate's
`-g2012 -DSIMULATION` accepts -- so my reading showed the branch with FEWER
errors than master while the gate showed more. And under the default flags the
branch really does lose one error, because master's empty task trips "Task body
with no statements". Two different rulers, two opposite verdicts, both about the
same file.

Refs #2754

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@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-08-29 02:47:48 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=fcfb279c055c != 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).

Same file, bare iverilog vs the gate's `-g2012 -DSIMULATION`, opposite verdicts.
And when recovered content lands in the wrong node kind, change the node rather
than the four backends that consume it.

Refs #2754

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@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-08-29 02:48:17 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=fcfb279c055c != 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).

@gHashTag
gHashTag merged commit e4d3cdb into master Aug 29, 2026
35 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

Development

Successfully merging this pull request may close these issues.

1 participant