Skip to content

tri: restore cost and diffbin, and the same 634 specs now report 13 field losses where the old tool reported zero - #2159

Open
gHashTag wants to merge 1 commit into
masterfrom
w699-restore-loop-tools
Open

tri: restore cost and diffbin, and the same 634 specs now report 13 field losses where the old tool reported zero#2159
gHashTag wants to merge 1 commit into
masterfrom
w699-restore-loop-tools

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Closes #2158

What this establishes

Two measurement tools were unrecoverable, and the recovery attempt is documented rather than asserted. scripts/tri_loop/cost.py and diffbin.py were written, used to produce the "634 specs, 0 regressions" figure quoted in #2151, and never committed to any branch; the working copy was later re-cloned. All six routes named in the order were checked:

route result
dangling git objects one git stash WIP commit, holding only triage.py
reflog records the clone at b9287250 -- that is the loss, not the content
shell history no .bash_history or .zsh_history exists
CI artifacts fpga-synthesis, fpga-verilog, wp18-conformance-report only
PR and issue comments no comment carries the source of either script
session snapshot prose describing the scripts, not the scripts

So both are reimplemented from a written contract. That is the stronger outcome, because recalling what the old one roughly did would have reproduced its defect.

The old aggregate was wrong, and the same corpus now says so. Re-run over the identical 634 specs with the identical two binaries (/tmp/t27c.base and /tmp/t27c.fixed, both of which survived):

    616  unchanged
     13  field-loss
      1  strict-improvement
      4  malformed-input-tradeoff
      0  unknown

Previously: "0 regressions". specs/tri/agent/handoff.t27 goes from 35 parsed fields to 12. Verified by hand on specs/tri/crypto/sha256.t27, where base yields state and count and the candidate yields only state, having absorbed count : u64 into its type text.

The finding that decides #2151, and it is a favourable one. All 17 files whose field sets moved are inside the damaged set from #2154 -- 13 of 13 field-loss and 4 of 4 malformed-input-tradeoff. Not one well-formed spec changed behaviour, which is what 0 unknown is carrying. The recovery change is therefore safe on clean input and its entire cost is confined to files that are themselves corrupt. That reorders the queue: repair #2154 first and the field-loss question largely dissolves, because the inputs producing it stop existing.

The contract, and why each clause is there

tri diffbin -- five categories matched in order: unchanged, field-loss, strict-improvement, malformed-input-tradeoff, unknown. field-loss is tested before strict-improvement, so a change that removes a phantom and also drops a declared field is a loss, not an improvement. Exit status is driven by field-loss and unknown counts, never by a summary verdict.

The discrimination the old tool lacked: a removed field is a phantom only if its type text in the base was EMPTY. That is the signature of an identifier lifted out of a type-argument list and promoted to a field. A removed field with a non-empty base type was declared by the author, and its removal is a loss. Counting fields cannot tell the two apart, which is how the old aggregate missed them.

Second correction: only an ExprIdentifier whose parent is a StructDecl counts as a field. t27c parse prints that kind for every identifier in the tree including function bodies, so matching on kind alone would count expression operands as struct fields.

tri cost -- per stratum: n, median, p95, min-max ms/KB, coefficient of variation. Alpha only at n >= 8, printed beside its r2 and the KB range it was fitted over. No cross-family alpha is printed at all. Not printed-with-a-caveat: not printed. A single exponent across these strata is a metric of corpus composition rather than of the parser (#2133), and a number gets quoted while its caveat does not travel with it. Measured on specs/tri, alpha was withheld for 12 of 13 strata for insufficient n, and the one printed (utils, 0.14) carries r2 0.50 over a 2.2-3.0 KB range -- a slope through a cloud, which the output says.

Build profile is labelled, so a debug millisecond cannot be quoted as a release one.

tri damage -- classifies the corrupt annotations of #2154 by shape and repairs nothing: 125 lines, 65 files, 15 distinct shapes, one emitted fixture per shape. Repair can then be a reviewable diff of a stated size per class instead of one sweep over 125 lines.

Its first draft reported 429 damaged lines. 230 of those were target : < 5000ns -- a legitimate less-than bound, not an unbalanced bracket; 17 were match arms, 10 were multi-line array literals, 6 were function signatures the field regex should never have matched. So the headline measured the regex, not the corpus. The fix was deleting the two bad signals, not tuning a threshold until the number looked right. Two survive: [[], which the language has no syntax for, and an odd ". r#" is excluded after reading the three lines it flagged.

This also revises the previous loop's figure upward from 115 lines / 63 files, because the older pattern missed the [?[] and [std.StringHashMap(...)" shapes.

scripts/ci/loop-tools-tracked.sh -- fails when a required tool is missing, when it exists but git does not track it, when anything under scripts/tri_loop/ is untracked, or when the generic dispatch line is gone. Verified to fail in exactly the pre-loss state (FAIL, four untracked files) and to pass after the commit. A check only verified in its passing state is decoration.

Its own check 4 was wrong in its first draft: it grepped scripts/tri for each command name, and tri triage passed only because the word appears in a comment. A check that detects its own documentation is worth nothing, so it now verifies the dispatch line and the resolved target file.

Dispatcher defect fixed. scripts/tri looked for a built t27c and exited before dispatching loop helpers. tri triage and tri damage read the tracker and the spec text and never touch the compiler, yet a machine with no build could not run them. Loop dispatch now precedes the binary lookup, and the new workflow job runs with no compiler present to keep it that way.

What this does NOT establish

Reproduce

./scripts/tri damage specs
./scripts/tri diffbin /tmp/t27c.base /tmp/t27c.fixed specs --jsonl /tmp/d.jsonl
./scripts/tri cost /tmp/t27c.fixed specs/tri --repeat 1 --min-kb 2
bash scripts/ci/loop-tools-tracked.sh

Closes #2158)

Two measurement tools were lost and every number they had produced became
unreproducible with them. cost.py and diffbin.py were written, quoted in #2151,
and never committed; the working copy was later re-cloned. Six recovery routes
came back empty -- dangling objects held only a git stash WIP with triage.py, the
reflog records the clone rather than the content, shell history is absent, CI
artifacts hold only FPGA outputs, no PR or issue comment carries the source, and
the session snapshot preserved prose about the scripts instead of the scripts.
So these are reimplementations from a written contract. Recalling what the old
ones roughly did would have reproduced the old one's defect.

That defect was the specification for the new one. It reported "0 regressions"
over 634 specs while files were losing declared struct fields, because a per-file
judgement had relabelled the loss as an acceptable trade and the aggregate then
printed the judgement as if it were a measurement.

  No differential result may be called "0 regressions" unless the metric
  actually checks the claimed class of loss.

diffbin now assigns five ordered categories -- unchanged, field-loss,
strict-improvement, malformed-input-tradeoff, unknown -- with field-loss tested
before strict-improvement, so removing a phantom while dropping a declared field
is a loss and not an improvement. Phantom and declared are told apart by a stated
rule: a removed field is a phantom only if its base type text was empty. Only an
ExprIdentifier whose parent is a StructDecl counts, so identifiers in function
bodies stay out of the totals.

Re-measured on the same 634 specs and the same two binaries: 616 unchanged, 13
field-loss, 1 strict-improvement, 4 malformed-input-tradeoff, 0 unknown.
handoff.t27 goes from 35 parsed fields to 12. All 17 files that moved are inside
the damaged set and no well-formed spec changed at all, which is what 0 unknown
is carrying.

cost reports per stratum with n, median, p95, min-max ms/KB and coefficient of
variation, alpha only at n >= 8 with its r2 and KB range, and no cross-family
alpha at all: that number is a metric of corpus composition rather than of the
parser (#2133), and a printed number gets quoted while its caveat does not
travel with it.

damage classifies the corrupt annotations by shape rather than repairing them
(#2154): 125 lines, 65 files, 15 shapes, one fixture each. The first draft
reported 429, of which 230 were the legitimate bound `target : < 5000ns`, so the
fix was deleting two bad signals rather than tuning a threshold.

loop-tools-tracked.sh fails when a loop tool is missing, untracked, or unrouted,
and was verified to fail in exactly the pre-loss state. The dispatcher no longer
looks for a built compiler before running helpers that never use one.
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-14 19:26:54 UTC

Summary

Status Count
Total Open PRs 22
PRs with Failing Checks 8
PRs with All Checks Green 14
READY 6
FAILING 8
PENDING 0

Seal Status

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

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.

The two tools every parser claim rests on are lost, and the one that survived reported zero regressions while fields were being dropped

1 participant