tri gates mutate: the cache key must cover the instrument (Refs #2161) - #2630
Merged
Conversation
Gave the boundary operator the verdict filter that makes `invert` kill 72 of 78. It appeared to do NOTHING: 26/77 before, 26/77 after. Two runs, one number, an edit that looked inert. The mutation cache keys on the gate's bytes and its control's bytes. Changing how SITES ARE SELECTED changes neither, so a rebuilt binary served 24 rows measured by the version before the change. `--fresh` gave 3/5, and only then was the filter's effect visible. The key now includes sha256(current_exe()). Verified in three states: same binary twice caches 2 rows; a rebuild caches 0 and re-measures every row; the same binary again caches 2. A cache that cannot see its own instrument change is the instrument lying about itself -- R2 one level down -- and the failure mode is the worst kind. Not a wrong number: an UNCHANGED one, which reads as "your change had no effect" and invites a conclusion about the subject. AND THE FILTER ITSELF WAS WRONG, which the same measurement showed. Sites went 77 -> 5, and among the 72 removed: check_vector_data 6/6 -> 0/0 check_seal_coverage 3/3 -> 0/0 check_catalog_integrity 1/1 -> 0/0 Those were KILLED mutants. Moving those comparisons demonstrably made the control fail, which is proof they reach a verdict, so a filter that drops them is wrong. Reverted. It cannot be repaired by widening either: the missed sites append to a list that a later statement turns into a verdict, several statements away. Verdict-reachability is a dataflow property; a line-local pattern cannot decide it, and widening one until the number looks agreeable is the move R2 forbids and `tri damage`'s 429 already demonstrated. So the 34% is not a defect. A boundary survivor means "moving this comparison did not change the verdict", which for a loop bound is correct. The kills are what identify the verdict-bearing half, after the fact, which is the only way it can be known. Refs #2161
Contributor
PR DashboardGenerated at: 2026-08-23 23:28:00 UTC
Summary
Seal Status
|
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
gHashTag
pushed a commit
that referenced
this pull request
Aug 23, 2026
`fn f(error: Err)`, `fn update(key: K, fn: anytype)` and `union(input)` -- `error`, `fn` and `union` are keywords and the natural names for those things. zig_ident already covered const decls, enum decls, struct fields, locals and function names; parameters were the sixth site and expression names the seventh. Expression position needs a different rule than binding position: zig_ident also quotes primitives, which is right for a binding and wrong in an expression, where `@as(u8, x)` names u8 ordinarily and `@as(@"u8", x)` would break a correct call. zig_expr_name quotes keywords only, segment by segment across dots so `mod.union` becomes `mod.@"union"`, and leaves @-builtins alone. The unused-parameter discard moved to the same spelling or it would name something that no longer exists. Valid specs 131 -> 132, specs behind a wall 134 -> 117, 0 regressed, gate holds at 111. Errors 970 -> 1079: seventeen walls fell, and 7 files went from 1 error to many -- vsa/vsa_core 1 -> 52, base/types 1 -> 35. All of it was already there. Closes #2630
Owner
Author
|
Correction: commit 7b23784 ("fix(emit): quote Zig keywords in parameter and expression names") ends with That commit does not address this issue and must not close it. It touches Already pushed, so the message is not being rewritten. Flagging it here so the reference is not acted on at merge. |
gHashTag
added a commit
that referenced
this pull request
Aug 24, 2026
#2630 established that the boundary column's denominator holds two populations -- comparisons that decide a verdict, and loop bounds and display cutoffs that cannot -- and that no line-local filter separates them, because a kill is the only proof of verdict-reachability and the filter removed proven kills. That leaves the column unreadable rather than wrong. `SURVIVED at boundary lines 45, 91, 214, 223, 226, 409` gives a reader nothing to act on. Two changes, neither touching the measurement. Print the source beside the line number, for this operator only: 91 `if len(out) > 6:` a display cutoff 214 `while len(v) < N:` a loop bound 226 `while j < len(src):` a scan bound 223 `if b < 0:` the only one worth reading Five of six classify themselves at a glance. The reader does the separation the tool cannot, in seconds rather than by opening the file. And say what the denominator is, in the summary, only when this operator ran -- so killed/total is not read as a rate. The rule: when a metric mixes populations you cannot separate, do not report a ratio; report the members. A rate over a mixed denominator invites exactly the conclusion this command exists to prevent, and invites it most from whoever computed it. The killed count keeps its meaning: a LOWER BOUND, established after the fact, on how many comparisons reach a verdict. Smaller than a percentage, and true. 194 tests pass. Refs #2161
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gave the
boundaryoperator the verdict filter that makesinvertkill 72 of 78. It appeared to do nothing: 26/77 before, 26/77 after. Two runs, one number, an edit that looked inert.The mutation cache keys on the gate's bytes and its control's bytes. Changing how sites are selected changes neither, so a rebuilt binary served 24 rows measured by the version before the change.
--freshgave 3/5, and only then was the filter's effect visible.The key now includes
sha256(current_exe()). Verified in three states:A cache that cannot see its own instrument change is the instrument lying about itself — R2 one level down — and the failure mode is the worst kind. Not a wrong number: an unchanged one, which reads as "your change had no effect" and invites a conclusion about the subject.
And the filter itself was wrong
The same measurement showed it. Sites went 77 → 5, and among the 72 removed:
check_vector_datacheck_seal_coveragecheck_catalog_integrityThose were killed mutants — proof the comparisons reach a verdict. A filter that drops them is wrong. Reverted.
It cannot be repaired by widening either: the missed sites append to a list that a later statement turns into a verdict. Verdict-reachability is a dataflow property, and widening a line-local pattern until the number looks agreeable is the move R2 forbids — the one
tri damage's 429 already demonstrated.So the 34% is not a defect. A boundary survivor means "moving this comparison did not change the verdict", which for a loop bound is correct. The kills identify the verdict-bearing half, after the fact, which is the only way it can be known.
The negative result is the deliverable. 194 tests pass. §106 and §107.
Refs #2161