Skip to content

fix(tri): one population for the census, and a control that walks it the other way - #2933

Closed
gHashTag wants to merge 5 commits into
masterfrom
loop/census-agreement
Closed

fix(tri): one population for the census, and a control that walks it the other way#2933
gHashTag wants to merge 5 commits into
masterfrom
loop/census-agreement

Conversation

@gHashTag

@gHashTag gHashTag commented Aug 30, 2026

Copy link
Copy Markdown
Owner

The disagreement

tri unparsed report and tri unparsed locate live in the same file, run from
the same binary, and gave different answers about the same corpus:

report locate
refused at TYPECHECK 5 2
refused at SEMANTICS 1 0

Measured independently (745 specs, t27c check each, classify by message): 5
typecheck, 4 lex, 1 semantic
. report was right.

Two causes

1. locate walked its own corpus. parse_failures was lifted out last
week carrying a doc comment that says disagreement is now "structurally
impossible rather than merely tested for"
— but only prose was moved onto
it. report and locate kept their own git ls-files loops. The comment
described the intention; the code kept three walks.

2. locate checked the message FORMAT before the error's STAGE. Three
typecheck failures print no line at all:

Typecheck FAILED (6 errors, 0 warnings):

locate_one demanded a line number first, so those never reached the stage
check and landed in "nothing claimed" — a bucket that reads as the parse
question was asked and went unanswered
, when the question does not apply to
them at all. Its buckets summed to 80 against a population of 76.

The existing test locate_answers_only_for_parse_failures was green throughout.
It asserts that stage_of classifies correctly — the presence of the helper,
not the order in which the caller applies it.

The change

report and locate now read the shared scope, which carries the stage split.
locate_one takes the error text the scope already holds, so a spec is
compiled once for the population instead of once more per command.

Before changing the ruler I checked it: on all 76 parse failures the in-place
run and the temp-copy run named the same line every time, and neither left
a failure without one. Zero divergence — a reason to keep one ruler, not two.

locate:  57 confirmed + 14 refuted + 5 silent = 76   (was 57 + 14 + 9 = 80)
stages:  5 typecheck + 4 lex + 1 semantic     = 10   = what `prose` prints

tri unparsed agree

Three commands reading one variable agree by construction, so their agreement
measures nothing. The new command builds the population the other way
walking the working tree instead of asking git — and demands the same numbers:

                                  census      walk
      refused at PARSE                76        76
      refused at TYPECHECK             5         5
      refused at LEX                   4         4
      refused at SEMANTICS             1         1
      broken on purpose               21        21

      specs git tracks                   745
      specs found on disk                745

  AGREED. Both routes name the same population and the same stages.

It also names what the census is structurally blind to: a .t27 on disk that
git does not track is invisible to every command here. That is reported, not
failed on — the census legitimately speaks only about tracked specs.

Mutation-checked, both directions

mutation result
silent specs/fpga/ filter inside parse_failures refused at TYPECHECK 4 vs 5 <- DISAGREE, exit 1
untracked failing spec on disk on disk, NOT tracked 1 (1 of them fail), gate green
a second corpus walk added to prose.rs one_corpus_walk FAILS
stage_of made to require a line number guard-order test FAILS
all reverted 378 tests pass, agree green

Checks

  • cargo test -p tri378 passed, 0 failed
  • cargo clippy --all-targets — one warning fewer than master (Scope.tracked was dead); no new ones
  • rustfmt on the two touched files only; every hunk is inside code this PR edits
  • tools/wp18_selftest_gate.py — exit 0 (its "unparsed" is about SSOT, unrelated)

Refs #2864


After merging master (#2926, #2934)

feat(typecheck): a constant must fit the type it declares moved the typecheck
count while this PR was open. The finding above was measured at 21c7f7c06;
re-measured on the merged tree, with tri unparsed agree doing exactly the job
it was built for:

                                  census      walk
      refused at PARSE                76        76
      refused at TYPECHECK            10        10     (was 5 -- #2926)
      refused at LEX                   4         4
      refused at SEMANTICS             1         1

locate 57 + 14 + 5 = 76; stages 10 + 4 + 1 = 15, which is what prose
now prints. The three still agree, and the independent walk still confirms them
— which is the point: the numbers move, the agreement is what must not.

.claude/skills/ci-gates sections renumbered 370–372 → 371–373; master took
370 in #2932.

…the other way

`unparsed report` said 5 typecheck failures; `unparsed locate`, in the same
binary, said 2. Measured independently: 5 is right.

Two causes, both in `locate`:

  * it walked its own corpus. `parse_failures` was lifted out last week with a
    doc comment claiming disagreement was "structurally impossible", but only
    `prose` was moved onto it -- `report` and `locate` kept their own
    `git ls-files` loops. The comment described an intention, not the code.

  * it checked whether the error named a LINE before it checked which STAGE
    refused the file. Three typecheck failures print `Typecheck FAILED (6
    errors, 0 warnings):` and no line at all, so they never reached the stage
    check and were reported as "nothing claimed" -- a bucket that reads as "the
    parse question was asked and went unanswered", when the question does not
    apply. Its buckets summed to 80 against a population of 76.

`report` and `locate` now read the shared scope, which carries the stage split;
`locate_one` takes the error text the scope already has, so the file is
compiled once for the population instead of three times per spec. Measured on
all 76 parse failures: the in-place run and the temp-copy run named the same
line every time and neither left a failure without one -- a reason to keep one
ruler, not two.

  locate:  57 confirmed + 14 refuted + 5 silent = 76, the population exactly
           (was 57 + 14 + 9 = 80)
  stages:  5 typecheck + 4 lex + 1 semantic = 10, which is what `prose` prints

New `tri unparsed agree`: the census asks git, this walks the working tree, and
both must name the same population and the same stage split. Three commands
reading one variable agree by construction, so their agreement measures
nothing; this is the axis that can still be wrong. It also names what the
census is blind to -- a spec on disk that git does not track.

Mutation-checked, both directions:
  * a silent `specs/fpga/` filter in `parse_failures` -> DISAGREE, exit 1
  * an untracked failing spec on disk    -> named, gate stays green (the census
                                            legitimately speaks only of tracked
                                            specs; the point is naming it)
  * a second corpus walk in `prose.rs`   -> `one_corpus_walk` FAILS
  * stage made to depend on the line     -> the guard-order test FAILS

Refs #2864
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-30 00:43:45 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)=7d3956bee444 != 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

📓 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-30 00:52:45 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)=7d3956bee444 != 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-08-30 00:54:13 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)=7d3956bee444 != 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

Copy link
Copy Markdown
Owner Author

Reopened as a clean branch: two commits carried (#2933) instead of Refs #N and Check L1 TRACEABILITY refused them. Fixing that in place would mean rewriting a pushed branch, so the same tree ships with correct messages instead. Same content, byte for byte.

@gHashTag gHashTag closed this Aug 30, 2026
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