diff --git a/docs/probes/lexmatchthunk_unmask_2026-08-22/A_ARM_MASK_MECHANISM.md b/docs/probes/lexmatchthunk_unmask_2026-08-22/A_ARM_MASK_MECHANISM.md new file mode 100644 index 00000000000..7c752f5addb --- /dev/null +++ b/docs/probes/lexmatchthunk_unmask_2026-08-22/A_ARM_MASK_MECHANISM.md @@ -0,0 +1,226 @@ +# What the LexMatchThunk mask actually is — measured at `967b5bc1b92`, B arm not run (2026-08-22) + +This document is the A-arm finding that **stopped the B arm before it was built**. The +[pre-registration](PRE_REGISTRATION.md) commits the lane to reporting rather than widening when the +declared one-variable repair turns out not to be one variable, and that is what happened. The +registered population and join rule are untouched and remain valid for whoever lands the repair. + +Every claim below was produced by executing the compiler built from this tree, against +hand-written controls in a scratch source root. No claim here is read off the emitted corpus. + +## The mask is NOT an emitter decision + +The emitter already has the arm the repair was assumed to need. A **non-generic** record with a +function-typed field emits the field-closure call correctly: + +``` +type Thunk { apply: fn(String) -> String } +type Algebra { combine: fn(Thunk, Thunk) -> Thunk } +``` +→ `(th.apply)(s.clone())` and `(a.apply)((b.apply)(s.clone()))`, **0 diagnostics**. + +So "the emitter renders a field-closure call as a method call" is false as a general statement, and +a repair aimed at the emitter would have been aimed at working code. + +## The discriminator is GENERICITY, in a one-line controlled pair + +| declaration | result | +|---|---| +| `type Algebra { combine: fn(Thunk, Thunk) -> Thunk }` | emits `(a.apply)(…)`, clean | +| `type Algebra { combine: fn(R, R) -> R }` | `method 'apply' cannot be resolved: receiver type 'Primitive()' establishes no method surface` | + +Same body, same call, same tree, same binary. **The generic carrier is the ALGEBRA, never the +thunk** — a distinction worth stating flatly, because the lane briefly lost a cycle to reading it +the other way: `v2.compiler.tokenize` `LexMatchThunk` is a concrete `fn(String) -> LexMatchResult` +and always was, and a minimal thunk with no algebra around it does **not** reproduce the refusal. +The refusing receiver is `open_r`, a parameter of the lambda initializing `delimited: fn(R, R, R) +-> R` on the generic `v2.std.compilers.lexing` `LexPatternFold`. The runnable pair that varies +*only* algebra genericity, with the same non-generic thunk in both arms, is in +[`controls/`](controls/). `LexPatternFold` is the generic form, so every lambda parameter in `v2.compiler.tokenize`'s algebra (`open_r`, `body_r`, +`close_r`, …) reaches the emitter **with no type at all**. That is exactly the shape +`v1.compiler.infer` `unresolved_method_frontier_note` already records as `Primitive()` — "a lambda +parameter whose type never propagates" — and the same note names "the v2 tokenizer's own +`LexMatchThunk { apply: fn(s) }` idiom" as its motivating residue. The frontier row admits the +call; the emitter then emits a method call; rustc refuses it as `E0599`; and the E0308 sites +downstream of it in the same file are never reached. That is the mask, end to end. + +## It is not one seam + +The instantiation fails to reach the lambda **even when the expected type is fully explicit**: + +``` +fn build_annotated() -> Algebra { + Algebra { unit: Thunk { apply: fn(s) { s } }, + combine: fn(a, b) { Thunk { apply: fn(s) { a.apply(b.apply(s)) } } } } +} +``` +→ still refuses. So this is not only the call-argument seam, where the formal is the *callee's* own +type variable (`fold_lex_pattern(algebra: LexPatternFold)`); the annotated-return seam fails +too. + +A candidate repair was written and executed rather than argued about: thread the generic +instantiation through the record-literal field loop as a left fold, which is the same substitution +`v1.compiler.infer`'s call seam already performs for arguments (`ArgGenericFoldState`). It was +regenerated into the seed mirror, the compiler was rebuilt from it, and **it fixed neither case**, +so at least one further seam sits underneath. It was reverted rather than carried: an inference +change that buys nothing is not a smaller version of a repair, it is a change with no consumer. + +**How strong that negative result is, stated exactly (2026-08-22).** The install *was* verified at +the file level — `git diff --stat` on the mirror showed 228 insertions / 170 deletions, and cargo +recompiled `v1-compiler` from it — so this is **not** the silently-failed-restore class, where the +patched file never changed at all. What was **not** done is a symbol-level check that the rebuilt +binary carries the change. So the honest strength is *mirror confirmed changed and crate confirmed +rebuilt, binary not symbol-verified*. It is also no longer cheaply re-checkable: re-installing that +candidate mirror on current main fails to compile (3 errors, `E0063` — the seed's `Node` has moved +since `967b5bc1b92`), so re-verification means regenerating the candidate at its own ref. The +conclusion is left standing at that strength rather than upgraded by assertion, and the repair lane +should treat "at least one further seam" as *measured but not binary-verified*. + +## A fail-open found beside it, worth its own lane + +A generic record literal's fields are **not checked against the instantiation at all**: + +``` +fn generic_bad() -> Algebra { Algebra { unit: "x", unary: fn(a) { a } } } +``` +→ `compiled: 6 files emitted, 0 diagnostics` + +**Still true on current main**, re-run 2026-08-22 at `abf7194e2b` with a compiler built from that +tree: same fixture, same `0 diagnostics`. So it is not an artifact of the `967b5bc1b92` snapshot, +and the repair lane holding it is holding a live defect rather than a historical one. + +`unit: R` with `R = Thunk` silently accepts a `String`. Two facts belong in the row rather than in +whoever reads it: + +- **It is not a build artefact.** It reproduces on the same binary that produced every other result + in this document, including the controls that behave correctly. +- **It is a FLOOR class, not a differentiating one.** "Values inhabit declared types" is the + ordinary compiler floor DESIGN §4b names, and a failure there is a below-baseline safety + regression — never compensated by higher-order capability. It is below the ladder rather than low + on it. + +It sits adjacent to the mask because both are the same missing propagation: the instantiation never +reaches the field expectation, so nothing checks against it and nothing types the lambda parameters +bound from it. One lane holds both or they are fixed twice. It is independent of the A/B — it would +still be true if the mask were repaired tomorrow. + +## Consequence for the board, unchanged by any of this + +Repairing the mask will make **~68 E0308 sites appear** in `v2_compiler_tokenize.rs`. That is an +**exposure event, not a regression**: those sites exist at `967b5bc1b92` and are unobservable +because a blocking error aborts the pipeline before the phase that would report them. The +registered population, the prediction `unexplained = 0`, and the join rule in the pre-registration +are the instrument for reading that rise when it happens. + +## Amendment (2026-08-22): the B arm needs a repeat-run control + +Recorded here, beside the A-arm baseline, rather than in the pre-registration — that file is frozen +by design, and this is a fact learned after it, not a re-specification of it. It does not change +the registered population, the prediction, or the join rule; it adds a control the B arm must carry. + +**The emitter is nondeterministic.** `royal-dove-436` observed two consecutive emits of the same +tree, same binary, same environment differing on `v2_lens_enforcement_vocab.rs` and +`v2_std_cross_tree_resolution.rs`, then characterised the class on request: + +- **Shape: pure line reordering.** Across three emits of one unchanged tree, all three pairwise + different, the entire difference is one `pub use` line moving one position; line counts identical + (336) and the sorted-line test identical. So it is import-emission order — a set or map iteration + — not a value nondeterminism. +- **The churn SET itself varies.** The second run churned only one of the two files. So a single + run cannot even establish the churn population, which means **an exclusion list derived from one + run is not sound** — a stronger objection than the one this amendment first recorded. +- **`rustfmt` normalizes it away**, verified by execution with a discriminating control (two files + differing only in the order of two `pub use` lines converge byte-identically after formatting). + +**What the B arm does with that, and why it is now cheaper than the first version of this +amendment:** compare **`rustfmt`-normalized** output, which makes this class *unrepresentable in the +oracle* rather than something measured and subtracted — strictly better than reporting a variation +number someone then has to interpret. The **≥ 2 emits per arm** control stays, run over normalized +output: it is now a *detector for any class that survives normalization*, not a subtraction. If it +returns zero variation the variation report is unnecessary; if it does not, it has found a class +that matters more than this one and earns its place. + +**The caveat, unclosed and stated as such:** this shows the *reordering* class does not survive +normalization, not that *no* nondeterminism does. The known raw-corpus churn is 36–40 files on the +full corpus against two files on this closure, and nobody has shown that population is all +reordering. + +**One property of this lane's instrument makes the risk smaller than it looks:** the A/B compares +**diagnostic boards**, not emitted bytes, and the join rule registered for it already excludes +generated line numbers — which is the only thing a pure `pub use` reordering can move. The control +is still required, because that argument covers the characterised class and not the uncharacterised +remainder. + +## The baseline arm must be re-taken at the repair's parent (2026-08-22) + +Registered here as soon as it became true, and before any repair exists, because it is the kind of +thing that is cheap now and unrecoverable later. **The A arm published above is at +`967b5bc1b92`, and main has since moved.** It remains the *mechanism* baseline — what the mask is, +and why — but it is **not a valid comparison arm** for the repair when that lands. + +The A/B needs its two arms **one variable apart**. So the baseline is re-taken at the **repair +commit's own parent**, and the B arm at the repair commit. Comparing a post-repair board against +`967b5bc1b92` would put every unrelated landing in between inside the delta, and the join would +attribute other lanes' work to the repair — a difference that is real, arrives with a plausible +story, and is entirely an artifact of the two arms being several refs apart. + +Nothing else about the registration changes: the population, the prediction and the join rule are +fixed, and the arms simply move together to the repair's own ref pair. + +## Status: PARKED at the B arm + +The unmask was re-scoped out of this lane by `smart-ram-730` and dispatched as a v1 inference repair +carrying the fail-open above. This A/B is **parked, not cancelled**: the registered population, the +join rule and this A-arm baseline stand, and the B arm costs one probe run plus one python run once +the repair lands, because the classifier is committed and the raw log is published. + +## Amendment: the arm pair is named by SHA, because "the parent" is a non-discriminating name (2026-08-23) + +Committed **before the B arm was run and before any B-arm number existed**, which is the only +condition under which amending a pre-registration is worth anything. The provenance is checkable +independently of this file: the defect was disclosed to `smart-ram-730` and `calm-heron-887` on the +dashboard, by name, before the run started. + +The clause above says the baseline is re-taken at "the repair commit's own parent". That phrase +**resolves uniquely for a linear commit and ambiguously for a merge**, and the registration never +recorded which kind it assumed. The repair is a merge with two parents, and the two answer +differently: the first (`df90bc8541`) already contains the (c) and (a) commits and would yield a +null delta; the second (`c07d13a49f`) is pure main. + +**The arms, named by SHA:** + +| arm | sha | what it is | +|---|---|---| +| A′ | `c07d13a49f` | pure main, no repair — the differential baseline | +| B | `974ac5d808` | the repair, `(c)` + `(a)` only | + +"Second parent" is **not** an acceptable repair of the phrase: it is still positional, and it +inherits the identical failure the moment anyone rebases, re-merges or reorders. A SHA is the +discriminating identity; every shorter spelling of it is a nickname that happens to resolve today +(DESIGN §3). `smart-ram-730` reports this as the fourth instance of that class in one night — a +basename collision across two `complexity.dag`, a prefix regex matching a spelling where a concept +was meant, "Cut A" minted twice by two lanes, and now "its parent" — all of them surfacing in +**reporting** rather than in code, which is where nothing instruments for it. + +**What did NOT change:** the registered population (68 rows), the prediction +(`unexplained additions = 0`), the join rule (file + normalized expected/found relation + mechanism, +never a generated line number), and the reporting convention. Only the arm *names* are made +discriminating. + +### A consequence the original registration did not state, and should have + +The registered 68-row population was derived at `967b5bc1b92`, which is **not** either arm. So A′ +must be *measured*, not reused from the published partition. This is not the forbidden re-derivation: +the standing constraint from `smart-ram-730` is not to re-derive the population **after exposure**, +and A′ carries no repair, so measuring it is upstream of any exposure. The 68 stays exactly as +committed, and the A′ population is reported **beside** it rather than replacing it — two arms +several refs apart is precisely the confound the clause above was written to avoid, and quietly +substituting A′'s rows for the registered 68 would reintroduce it under a new name. + +### Held constant across the pair, checked rather than assumed + +`#8929` (the `file` transport rust realization handler) is an ancestor of **both** arms, verified with +`git merge-base --is-ancestor`. Before it, this subject could not be emitted at all — exit 1, 0 files, +five typed transport refusals — so the A/B was unrunnable, not merely unrun. It is a **held constant**, +not a confound, and it is why `177 files / 503 emit diagnostics` still serves as this subject's +identity stamp instead of needing a waiver. diff --git a/docs/probes/lexmatchthunk_unmask_2026-08-22/B_ARM_RUNBOOK.md b/docs/probes/lexmatchthunk_unmask_2026-08-22/B_ARM_RUNBOOK.md new file mode 100644 index 00000000000..f9f98396b4a --- /dev/null +++ b/docs/probes/lexmatchthunk_unmask_2026-08-22/B_ARM_RUNBOOK.md @@ -0,0 +1,145 @@ +# B-arm runbook — what to run when the censor is removed (2026-08-22) + +Written **before** the repair exists, so it is a procedure rather than a description of whatever was +done. It executes what [`PRE_REGISTRATION.md`](PRE_REGISTRATION.md) commits; it does not amend it. +If a committed rule turns out to be wrong, that is reported as a **falsified pre-registration**, not +quietly improved. + +Owner: whoever lands the repair, so exposure and measurement sit together. The two raw `cargo.log`s +are published either way, so the adjudication can be re-run independently by anyone. + +## BLOCKER, measured 2026-08-22 on `761c0d094d`: this subject's board is currently UNTAKEABLE + +**`src/v2/compiler/03_ingest.dag` cannot be emitted on current main.** Measured, not inferred — +`gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/03_ingest.dag --target +rust` exits **1** with **0 files emitted** and five typed refusals, one per file-transport operation +its closure declares: + +``` +'file' transport emission is not modeled: operation 'Filesystem.Write' … cannot be emitted for +target 'rust' — the file transport dispatch supplies only the operation name and an indent depth … +Bind a realization handler for the 'file' transport (DESIGN §3: interface shape and transport are +two facts); do not add a per-target renderer +``` +(`Filesystem.Write`, `WriteOwnerOnly`, `Read`, `Delete`, `List`, all declared in +`extdeps.filesystem.filesystem_io`.) + +**This is a fail-closed repair working, not a regression** (gunbc#8858): the file transport emitter +previously fabricated a read for every operation, ignored its path template, and dropped `Write`'s +content, producing code that compiled in no target. The absence was spelled as *output* and is now +spelled as a *refusal*. A newly refusing pipeline is the expected signature of that repair — the +same shape, in the opposite direction, as the rising board this registration already pre-commits to +not reading as regression. Both are the instrument getting more honest and looking worse. + +**Two consequences for this runbook, and neither is optional:** + +1. **Steps 1–2 cannot run** until a realization handler is bound for the `file` transport. Not + "will give a different answer" — the probe reaches cargo with nothing to build. The gate is + independent of the `(c) → (a) → (b)` chain and can lift before or after it. +2. **The A-arm baseline in [`A_ARM_MASK_MECHANISM.md`](A_ARM_MASK_MECHANISM.md) was taken at + `967b5bc1b92`, i.e. through the fabricating emitter.** Some unknown share of that board's rows + are the fabrication's own artifacts — one cited fabricated shape is a Rust write declaring + `Result<(bool), _>` and returning a `String`, which is an **E0308**, the exact class this + partition measures. **No share is estimated here**, because there is no basis for one and a + guess would be the residual-explained-by-mechanism error this lane has already made. The + qualitative statement is enough and is the registered one: the 315 series is not comparable to + the next takeable board in the way a same-subject A/B would normally assume. + +### Confirmed at the repair SHA, 2026-08-22 + +Measured at `calm-heron-887`'s (c)+(a) repair `ffddac8b55` (parent `f5be77a16a`), with a compiler +built from that tree — so this is the gate's status on the tree the B arm would actually use, not +an extrapolation from main: + +| check | result | +|---|---| +| `03_ingest` emit | **exit 1, 0 files**, the same five `file` transport refusals | +| `controls/algebra_genericity_pair.dag` `arm_b` | **still red** — one `Primitive()` refusal | + +Both are **confirmations of predictions registered in advance**, from opposite lanes: the repair +author predicted `arm_b` would still be red because (c)+(a) do not close (b), and this runbook +registered the transport gate as independent of the chain. So the B arm is blocked twice over, by +two unrelated gates, and neither reading is a repair having failed. + +## Arms + +| arm | ref | +|---|---| +| **A′** | the repair commit's **parent** | +| **B** | the repair commit | + +**Not** `967b5bc1b92`. That ref is the *mechanism* baseline in +[`A_ARM_MASK_MECHANISM.md`](A_ARM_MASK_MECHANISM.md) and main has since moved; comparing across +several refs puts every unrelated landing inside the delta, and the join would attribute other +lanes' work to the repair. + +## Steps + +1. **A′ and B boards**, at their own refs: + ``` + CSSL_STD_SEED_LINK=1 PROBE_KEEP_LOG_DIR= PROBE_EXPECT_BASE_SHA= \ + bash docs/probes/curated_cargo_probe_one.sh src/v2/compiler/03_ingest.dag "" + ``` + This is **one entry, M=1** — the `03_ingest` closure (177 emitted files), not a whole-corpus + compile. That distinction is now enforced rather than advisory: `gunbc.whole_corpus_compile_admission` + refuses a both-source-roots compile with `WholeCorpusCompileBudgetBelowMeasuredDemand` when readable + host memory is below measured demand, instead of starting and being `SIGKILL`ed — which used to + report as a silent exit-137 zero, making any count grepped from such a run a memorial to a killed + process. An `--entry`-scoped probe is unaffected. Measured cost on this hardware: ~13 min cold, of which ~4m15 is the probe building + `gunbc` + `cssl_assemble` from the tree it is measuring. +2. **Twice per arm**, comparing `rustfmt`-**normalized** output. The emitter is nondeterministic — + pure `pub use` line reordering — and the churn *set itself varies* run to run, so one run per arm + cannot even establish the churn population. Normalization makes that class unrepresentable rather + than measured-and-subtracted; the second run is then a **detector for any class that survives + normalization**. +3. **Classify each arm:** `python3 docs/probes/e0308_classify_sites.py `. +4. **Join B** against [`registered_masked_population.tsv`](registered_masked_population.tsv) on the + committed key: **file + normalized expected/found relation + mechanism**. Never generated line + number — both arms renumber freely, and the stronger key (enclosing declaration) is not + recoverable for the historical roster. +5. **Report** the three movements separately — visible board, repair movement + (`LexMatchThunk.apply` `E0599` N→0), exposure movement (Y newly observable, J joined, K newly + classified successors, `unexplained`) — and never lead with a single before→after total. + +## Remote dispatch, with the three things that bite + +``` +ctrl-build --remote -- bash -lc ' +set -uo pipefail +export CSSL_STD_SEED_LINK=1 +export PROBE_KEEP_LOG_DIR=/tmp/keep +export PROBE_EXPECT_BASE_SHA= +bash docs/probes/curated_cargo_probe_one.sh src/v2/compiler/03_ingest.dag "" +echo "LOG_B64_BEGIN"; gzip -9 -c /tmp/keep/03_ingest.cargo.log | base64 -w200; echo "LOG_B64_END" +' > out.txt 2>&1 +``` + +- **Env vars go inside the remote script.** `ctrl-build` forwards `RUSTFLAGS` and friends and *not* + these; it prints `forwarding env: (none)` and the same-base refusal you armed silently never + exists on the runner. +- **Return the log in the same dispatch.** The runner's filesystem is gone afterwards and the + `cargo.log` *is* the measurement. Redirect to a file; piping the dispatch through `tail`/`head` + eats the payload. +- **Do not background it.** A backgrounded `ctrl-build` dies with its shell and exits 0 with a + truncated log, which reads as success. + +## Preflight, before interpreting anything + +**Record whether the tree's stage0 mirrors are at their regen fixed point, because this probe +compiles the MIRROR.** `curated_cargo_probe_one.sh` builds `gunbc` from `src/v1/stage0`, so the arm +measures whatever the mirrors say — not what the `.dag` authority says — and the two are only the +same thing at the fixed point. A **hand-resolved mirror is not a regen receipt**: during an +integration the mirrors can legitimately be hand-brought to a consistent state *before* the true +regen fixed point exists (the branch has to build before it can be regenerated). An arm taken on +such a tree measures a compiler nobody's authority produced, and its result is not attributable to +any `.dag` change. Check with `claim_executor --required-regen --source-root dag --source-root +src/v2` and record the verdict beside the SHAs; if it refuses, say so in the report rather than +reading the board. + +Source SHA, compiler identity beside it (rebuilt from the tree, not the baked image), and a healthy +-pool positive control. For the mechanism controls that control is +[`controls/algebra_genericity_pair.dag`](controls/algebra_genericity_pair.dag), and **what it is an +acceptance test for is the whole `(c) → (a) → (b)` chain, not any single step** — measured with (a) +applied, `arm_b` still refuses. So a red `arm_b` after (c) or after (a) is expected and falsifies +nothing; **`arm_b` going green is the trigger for this runbook**, and `arm_a` staying clean +throughout is the harness check. See [`controls/README.md`](controls/README.md). diff --git a/docs/probes/lexmatchthunk_unmask_2026-08-22/PRE_REGISTRATION.md b/docs/probes/lexmatchthunk_unmask_2026-08-22/PRE_REGISTRATION.md new file mode 100644 index 00000000000..351608a86c5 --- /dev/null +++ b/docs/probes/lexmatchthunk_unmask_2026-08-22/PRE_REGISTRATION.md @@ -0,0 +1,113 @@ +# PRE-REGISTRATION — LexMatchThunk unmask A/B (registered 2026-08-22, before the B arm was built) + +This file is committed **before the B arm exists**. Everything below is a commitment made without +having seen B's output; the receipt published later reports against exactly these terms, and any +departure from them is stated as a departure rather than silently re-specified. The commit that +introduces this file introduces no repair and no B-arm measurement — that is the point of it being +its own commit. + +## The experiment — one variable + +| arm | tree | +|---|---| +| **A** | current main emitter, `967b5bc1b92ee66250e06a7870c132b48a16b80a` — already measured, see [`e0308_partition_2026-08-22.md`](../e0308_partition_2026-08-22.md) | +| **B** | the same source sha + **only** the `LexMatchThunk` invocation repair | + +Held identical across arms: source sha, entry (`src/v2/compiler/03_ingest.dag`, M=1), probe +(`curated_cargo_probe_one.sh`), `CSSL_STD_SEED_LINK=1`, empty `shim_lib_rel`, cargo manifest and +toolchain, and the emitted-file roster (177 files — a change in the roster invalidates the A/B and +is reported as such rather than absorbed). + +**Acceptance for B being the declared variable and nothing else:** every +`no method named 'apply' found for struct 'Rc'` `E0599` site is gone in B, and no +other emitter decision is edited. If the repair cannot be made without touching a second decision, +that is reported and the A/B is re-registered — not quietly widened. + +## What is registered as the expected population + +**The registered population is 68 canonical E0308 sites in `src/v2_compiler_tokenize.rs`**, taken +from the 2026-08-21 partition at `2a2bd0ad59` and copied verbatim into +[`registered_masked_population.tsv`](registered_masked_population.tsv): **B3 36, T2 32**, whose +pairs are + +``` +18 Rc> | String (T2) +18 Rc | integer (B3) +18 Rc | i64 (B3) + 7 String | Rc> (T2) + 4 Rc>| String (T2) + 3 String | Rc> (T2) +``` + +**The 68 is a masked candidate population, not a predicted delta.** It will not land exactly, and +three named reasons are registered in advance so that none of them can be presented afterwards as +a discovery: + +1. some of those sites may have changed independently while they were hidden; +2. one historical site may split into several diagnostics; +3. another may surface under a **different error code entirely** and so never appear on an E0308 + board at all. + +## The prediction + +**Unexplained additions = 0.** An *unexplained addition* is a newly-visible canonical E0308 site in +`src/v2_compiler_tokenize.rs` that joins to no row of the registered population under the join rule +below. Unexplained additions above zero is the **interesting** outcome — it would mean the repair +exposed something the historical partition never contained — and it is reported as its own counted +line, never absorbed into the join. + +## The join rule, registered in advance + +Newly-visible sites are joined to the registered population on: + +- the **emitted file** (`src/v2_compiler_tokenize.rs`), and +- the **normalized expected/found relation** — module-path noise removed, elided/full spellings + reconciled, direction-insensitive — as produced by the committed classifier + [`e0308_classify_sites.py`](../e0308_classify_sites.py), and +- the **mechanism** its current pair classifies to. + +**Never by generated line number.** A generated line is not a semantic identity and both arms +renumber freely. + +**One registered limitation, stated now rather than discovered later:** the brief's stronger key — +enclosing source declaration or function — is **not recoverable for the historical roster**, +because the 2026-08-21 lane published a per-site TSV but no emitted tree, so the enclosing function +of those 68 sites cannot be reconstructed at their own ref. The B arm's own sites *do* carry it, +and the receipt will publish the enclosing function for every newly-visible site as evidence +beside the join, but the join itself can only run at pair-and-mechanism grain. This is a weaker key +than requested and it is registered as such **before** any result is known. + +## How the result will be read — registered so it is not decided on the fly + +- **High join rate + `unexplained=0`** → the masking hypothesis is confirmed and the successor + population is known. +- **`unexplained > 0`** → the interesting outcome; reported as its own count with each site's + pair, mechanism and enclosing function, and explicitly *not* folded into the join rate. +- **A joined site that arrives under a different mechanism than its historical row** is reported as + a conversion, with both roots named — not counted as a match and not counted as unexplained. + +## Reporting convention (adopted by `smart-ram-730`, 2026-08-22) + +No adjusted total is invented. `315 + 68` is unit-invalid: 315 is raw coded rustc rows and 68 is +canonical sites. One headline metric plus a completeness standing: + +``` +A: coded=315 | coverage=partial | mask=LexMatchThunk.apply | masked_candidates=68@historical-site-grain +B: coded=X | coverage=tokenize-unmasked | newly_exposed=Y | unexplained=0 +``` + +Three movements are reported separately, and the receipt never leads with `315 → X` alone: + +1. **visible board** — the coded row count in each arm; +2. **repair movement** — `LexMatchThunk.apply` `E0599` `N → 0`; +3. **exposure movement** — `Y` newly observable, `J` joined, `K` newly classified successors, + `unexplained`. + +The rise is an **exposure event, not a regression**: those sites exist in A and are unobservable +there, because a blocking error aborts the pipeline before the phase that would report them. + +## Causal order this experiment protects + +Unmask → fresh full board → fresh tokenize repartition → **then** repair the newly exposed roots. +No T2 or B3 repair is stacked ahead of this run; designing a repair against a diagnostic set that +does not yet exist on the current tree would be planning a successor board nobody has observed. diff --git a/docs/probes/lexmatchthunk_unmask_2026-08-22/controls/README.md b/docs/probes/lexmatchthunk_unmask_2026-08-22/controls/README.md new file mode 100644 index 00000000000..039e8adac88 --- /dev/null +++ b/docs/probes/lexmatchthunk_unmask_2026-08-22/controls/README.md @@ -0,0 +1,71 @@ +# The controlled pair, as a runnable file (2026-08-22) + +`algebra_genericity_pair.dag` is the discriminating control for +[`../A_ARM_MASK_MECHANISM.md`](../A_ARM_MASK_MECHANISM.md). **Both arms declare the SAME +non-generic thunk**; they differ only in whether the *algebra carrying the lambda* is generic. + +``` +gunbc compile --source-root --entry /t/arms.dag --output-dir /tmp/out --target rust +``` + +## Preflight — compiler identity beside source identity + +Recorded because a local diagnostic read against a stale binary is indistinguishable from a real +result, and this lane already spent a rebuild proving that the reverted candidate changed nothing: + +| axis | value | +|---|---| +| source subject | `967b5bc1b92ee66250e06a7870c132b48a16b80a`, worktree clean (`git status --short` empty) | +| compiler | `target/release/gunbc` **deleted and rebuilt** from that tree (`cargo build --release -p v1-compiler --bin gunbc`, 2m13s) — not the session image's baked `/usr/local/bin/gunbc`, which predates this CLI | +| postdates-stale check | the baked binary rejects `--entry` outright; the rebuilt one accepts it, so the two cannot be confused | +| healthy-pool positive control | **arm A**, which compiles clean in the same run — a refusal in both arms would mean the harness, not the variable | + +Result on that binary — **one file, one run, one hard diagnostic**: + +| arm | algebra | thunk | result | +|---|---|---|---| +| `arm_a_non_generic_algebra` | `PlainFold { delimited: fn(Thunk, Thunk, Thunk) -> Thunk }` | non-generic | **clean** | +| `arm_b_generic_algebra_concrete_thunk` | `GenericFold { delimited: fn(R, R, R) -> R }` instantiated at `Thunk` | **the same** non-generic thunk | `method 'apply' cannot be resolved: receiver type 'Primitive()'` | + +**Why this pairing and not a minimal thunk:** a bare thunk with no algebra around it does **not** +reproduce the refusal, because the genericity that loses the type is in the **algebra**, never in +the thunk. `v2.compiler.tokenize` `LexMatchThunk` is a concrete `fn(String) -> LexMatchResult` and +always was; the receiver that arrives untyped is `open_r`, a parameter of the lambda initializing +`delimited: fn(R, R, R) -> R` on the generic `v2.std.compilers.lexing` `LexPatternFold`. A +reproduction that drops the algebra drops the variable. + +## What this pair is the acceptance test FOR (corrected 2026-08-22, before any B-arm measurement) + +**`arm_b` is the TERMINAL red for the whole `(c) → (a) → (b)` chain, not a per-step red.** Measured +by `calm-heron-887` on a tree with **(a) applied**: `arm_b` still refuses. So: + +- a red `arm_b` after (c), or after (a), is **EXPECTED** and is **not** a falsification of anything — + it means the chain is incomplete, not that the step failed; +- `arm_b` going green is the **chain's completion signal**, and the trigger for the B arm; +- `arm_a` staying clean throughout remains the harness check: if `arm_a` ever refuses, the harness + moved rather than the variable. + +**Why this correction is legitimate rather than a moved goalpost:** it fixes a *stated expectation* +that had never been tested against the intermediate state — the pair had never been run against a +tree carrying (a) when the instruction was written — and it is recorded **before** the measurement +it governs. Nothing about the registered population, the prediction, or the join rule changes. + +**The failure mode it exists to prevent, in both directions:** reading a red `arm_b` after a correct +(c)/(a) landing as the repair being ineffective; or weakening a correct control to make it green, +which is the worse of the two because it destroys the terminal acceptance test for the entire chain. + +**Provenance of the MEASUREMENT behind it, corrected by its author (2026-08-22).** The first run +supporting this row was invalid: the mirror half of the (a) patch had been reverted to build a +baseline, the patched copy was saved under `/tmp`, the container wiped `/tmp` between the two steps, +and the restoring `cp` failed silently — so both arms were baseline against baseline. The tell was +in the output and is worth keeping: **two arms reporting IDENTICAL counts (11 and 11, 158 and 158) +is not agreement, it is one instrument run twice.** Re-measured on a genuinely patched binary, with +the symbol verified in the source *and* in the built binary first, the result is unchanged and this +row stands as written. It is recorded because this document's edit was made while the supporting +evidence was invalid, and a claim's basis is part of the claim. + +**Provenance of the correction:** `calm-heron-887` first ran this pair and reported `compiled 5 +files, 0 diagnostics` — a shell race (a trailing `&` bound to the whole and-chain, so the compile +read a partially written module), which they diagnosed and disclosed themselves rather than filing +it as a defect in the control. Re-run clean, `arm_b` refuses and `arm_a` is clean, as documented +above. diff --git a/docs/probes/lexmatchthunk_unmask_2026-08-22/controls/algebra_genericity_pair.dag b/docs/probes/lexmatchthunk_unmask_2026-08-22/controls/algebra_genericity_pair.dag new file mode 100644 index 00000000000..e0392565619 --- /dev/null +++ b/docs/probes/lexmatchthunk_unmask_2026-08-22/controls/algebra_genericity_pair.dag @@ -0,0 +1,35 @@ +module t.arms + +type LexResult + = Rejected + | Accepted { remaining: String } + +type Thunk { + apply: fn(String) -> LexResult +} + +type PlainFold { + delimited: fn(Thunk, Thunk, Thunk) -> Thunk +} + +type GenericFold { + empty: R + delimited: fn(R, R, R) -> R +} + +fn arm_a_non_generic_algebra() -> PlainFold { + PlainFold { + delimited: fn(open_r, body_r, close_r) { + Thunk { apply: fn(s) { open_r.apply(s) } } + } + } +} + +fn arm_b_generic_algebra_concrete_thunk() -> GenericFold { + GenericFold { + empty: Thunk { apply: fn(s) { Rejected } }, + delimited: fn(open_r, body_r, close_r) { + Thunk { apply: fn(s) { open_r.apply(s) } } + } + } +} diff --git a/docs/probes/lexmatchthunk_unmask_2026-08-22/registered_masked_population.tsv b/docs/probes/lexmatchthunk_unmask_2026-08-22/registered_masked_population.tsv new file mode 100644 index 00000000000..fa4dd49ad09 --- /dev/null +++ b/docs/probes/lexmatchthunk_unmask_2026-08-22/registered_masked_population.tsv @@ -0,0 +1,69 @@ +file line col expected found pair_signature root reason entry +src/v2_compiler_tokenize.rs 134 33 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 134 41 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 134 59 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 134 67 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 38 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 46 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 64 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 72 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 92 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 100 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 118 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 126 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 147 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 155 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 173 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 135 181 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 136 40 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 136 48 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 136 66 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 136 74 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 136 94 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 136 102 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 136 120 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 136 128 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 44 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 52 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 70 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 78 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 98 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 106 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 124 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 132 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 176 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 184 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 202 Rc i64 expected `Rc`, found `i64` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 137 210 Rc integer expected `Rc`, found `integer` B3 modeled_numeric_vs_native 03_ingest +src/v2_compiler_tokenize.rs 145 15 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 146 11 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 157 16 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 198 16 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 199 13 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 228 13 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 228 13 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 228 25 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 251 13 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 251 13 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 251 25 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 272 13 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 272 13 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 272 25 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 297 13 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 340 13 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 340 13 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 340 25 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 362 13 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 370 13 String Rc> expected `String`, found `Rc>` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 465 57 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 466 58 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 467 62 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 561 99 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 562 36 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 588 36 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 591 99 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 592 36 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 620 36 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 622 99 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 623 36 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest +src/v2_compiler_tokenize.rs 685 43 Rc> String expected `Rc>`, found `String` T2 text_carrier_vs_string 03_ingest