mutation gate: kill the holes mutmut 3.8.0 exposed, and take #673 by hand - #674
Merged
Merged
Conversation
…d them mutmut 3.8.0 (2026-09-12) fixed a bug where methods of decorated classes were never mutated. That made __str__ on FlashReport, BudgetReport and Violation mutable for the first time and turned the weekly mutation job red on unchanged code — 20 new survivors, 7 of them "no tests", meaning no selected test had ever executed those lines. The reason is CLAUDE.md #1 in a new disguise. test_flash_safety.py has always contained assert isinstance(rep, FlashReport) and rep.ok, str(rep) which reads like coverage of __str__ and is not: Python evaluates an assert's message only when the assertion FAILS, so on every green run that str(rep) never ran. The one apparent reader never read. What that left unguarded is not cosmetic. FlashReport.__str__ renders "FLASH-SAFE" if self.ok else "FLASH RISK" — the eye-safety verdict, in the words a human reads — so a mutant inverting it prints FLASH-SAFE over a figment the analyzer rejected. BudgetReport.__str__ has the same conditional head plus the lines enumerating the violations. test_report_strings.py executes all three for real and asserts both directions of every verdict. The heads are pinned with startswith and the violation lines by equality, not "in": an "in" check passes on a head mutmut has wrapped to [XXFLASH-SAFEXX] and on a report joined with "XX\nXX". It is listed in [tool.mutmut] because a killer suite mutmut is not told to run cannot kill anything. Signed-off-by: LetsGetToWorkBro <info@labyrinth.vision>
mutmut 3.8.0 also added "Mutate the condition of ternary expressions",
and one of the conditions it reached was the SELF rewrite in
_cycle_analysis:
target = sid if t.target == SELF else t.target
Mutated to "(t.target == SELF) or True", every timeout hop collapses
into a self-loop — and it survived every test in the file. A ring of
equal scenes cannot see it: s0 to s1 to s0 at 2 emits / 2 s reads
exactly like s0 to s0 at 1 emit / 1 s.
An asymmetric cycle sees it at once. One fast emitting scene and one
slow silent one sustain 1 emit per 10.1 s, well under the 1/s budget,
while the fast scene read alone as a self-loop is 10 emits/s — so the
mutant invents a flood in a figment that has none, which on the
wearer's side is a Repertoire refusing to compile something safe.
Mutation-verified per CLAUDE.md #2: committed first, applied the
mutation, confirmed exactly this test fails (rate 10/s against the
expected 0.0990), restored from the repository root.
Signed-off-by: LetsGetToWorkBro <info@labyrinth.vision>
The weekly job went red on 2026-09-14 against a commit that was green on 09-07, with no diff between them. mutmut 3.8.0 (released 09-12) is the whole explanation: it fixed "methods of decorated classes are not mutated" and added ternary-condition mutation, so the net widened by 37 mutants and 23 more survived. Reproduced both versions locally before touching anything: 3.7.0 gives exactly the documented 229/15/1, 3.8.0 gives 245/22/1. The delta is 20 dataclass __str__ mutants (7 of them never executed by any test) plus 3 ternary-condition ones. Twenty-one of the twenty-three are now KILLED rather than accommodated (test_report_strings.py, and the asymmetric-cycle test in test_budgets_cycle_math.py), which is why flash_safety returns to its original 15 and budgets lands at 231 rather than 245. The two that are accepted are itemized in place, with the reason each is equivalent. Also corrected: privacy's single documented survivor is PrivacyGate.__init__ setting _incognito to None, not set_incognito's bool(on) coercion — right reasoning, wrong line, and the ceiling had been passing on a mutant the comment did not name. The header now says what to do when this goes red with no code change, since the bound on mutmut stays wide on purpose: a wider net finding untested code is the gate working, and raising a ceiling to match it without reading the new survivors is how a gate gets muted. Signed-off-by: LetsGetToWorkBro <info@labyrinth.vision>
Dependabot proposed widening the face extra to insightface <3 and could not relock, so the lock-freshness gate blocked it exactly as designed. Taken here with the relock. The bump needed no proving, because CI had already proved it without anyone noticing. real-models.yml installed insightface UNPINNED while the face extra capped it at <2, so when 2.0 shipped on 2026-09-08 the weekly job silently began exercising a version the project forbade — run 34837133098 (09-14) installed insightface-2.0, ran every real_model test with zero skips against the real buffalo_l model, and passed. Read the 2.0 wheel to confirm the adapter's surface survives: providers is no longer a named parameter of FaceAnalysis.__init__ but is still honoured through **kwargs, and prepare()/get() keep their signatures. So every entry on that install line now carries its bound. The comment above it has claimed "with the project's own bounds" all along, and insightface was the one entry with none — a job that tests what nobody can install is testing the wrong thing, in either direction. The lock takes 2.0 rather than keeping 0.7.3, so the declared bound, the lock and the version CI exercises finally agree. It also shrinks the graph: easydict and prettytable drop out and the duplicate albumentations/albucore forks collapse, 473 packages to 469. Signed-off-by: LetsGetToWorkBro <info@labyrinth.vision>
LetsGetToWorkBro
deleted the
claude/brain-retention-lifecycle-jp7nzv
branch
September 17, 2026 05:37
This was referenced Sep 17, 2026
Closed
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.
two things from the post-vacation sweep: the last dependabot item, and the mutation gate the triage loop flagged in #637.
the mutation gate went red on unchanged code, and it was right to
#637 caught it:
mutation.ymlfailed 09-14 against the same commit that passed 09-07, and since that job isn't a PR gate every PR since showed a full row of green.mutmut 3.8.0 (released 09-12) is the whole explanation. it fixed "methods of decorated classes are not mutated" and added ternary-condition mutation, so the net widened by 37 mutants. reproduced both versions locally before touching a ceiling — 3.7.0 gives exactly the documented 229/15/1, 3.8.0 gives 245/22/1.
the 23 new survivors were 20 dataclass
__str__mutants and 3 ternary ones. seven came backno tests— never executed by anything. the reason is CLAUDE.md #1 wearing a new hat:that's the only
str(rep)in the killer suites, and it's an assert message — python evaluates it only when the assert fails. on every green run it never ran. soFlashReport.__str__, which renders"FLASH-SAFE" if self.ok else "FLASH RISK"— the eye-safety verdict a human reads — had no guard at all.21 of the 23 are killed, not accommodated:
test_report_strings.pyexecutes all three__str__methods and asserts both directions of every verdict. heads pinned withstartswithand violation lines by equality, notin— anincheck passes on[XXFLASH-SAFEXX]and on a report joined with"XX\nXX".(t.target == SELF) or Truecollapses every timeout hop into a self-loop, and a ring of equal scenes can't see it (2 emits/2s reads like 1 emit/1s). an asymmetric cycle can — one fast emitting scene + one slow silent one sustains 1 emit per 10.1s, under the 1/s budget, while the fast scene alone as a self-loop is 10/s. the mutant invents a flood in a figment that has none. mutation-verified per CLAUDE.md feat(reality-compiler): NL → Halo behavior pipeline (intent parser, codegen, emulator, validator) #2: committed first, applied it, confirmed exactly that test fails, restored from the repo root.so
flash_safetyreturns to its original 15 andbudgetslands at 231, not 245. the two accepted are itemized in place with why each is equivalent.also corrected: privacy's ceiling had been passing on
PrivacyGate.__init__'s_incognito = Nonewhile the comment namedset_incognito'sbool(on)— right reasoning, wrong line.ran the workflow's own gate logic against the measured results: all-OK, exit 0.
#673: insightface 2.0
dependabot couldn't relock, so the lock-freshness gate blocked it as designed. the bump needed no proving because CI had already proved it unnoticed:
real-models.ymlinstalled insightface unpinned while thefaceextra capped it at<2, so when 2.0 shipped 09-08 the weekly job silently began exercising a forbidden version — run34837133098installedinsightface-2.0, ran every real_model test with zero skips against the real buffalo_l model, and passed.read the 2.0 wheel to confirm the adapter survives:
providersis no longer a named param ofFaceAnalysis.__init__but is still honoured via**kwargs;prepare()/get()unchanged.every entry on that install line now carries its bound — the comment above it has claimed "with the project's own bounds" all along and insightface was the one entry with none. the lock takes 2.0 so bound, lock and tested version finally agree, and it shrinks the graph: easydict and prettytable drop out, the duplicate albumentations/albucore forks collapse, 473 → 469 packages.
ruff + mypy clean (843 files). local suite has 42 failures from missing optional wheels in this container (
c2pa,keyring,zeroconf) — verified identical onorigin/mainwith these commits absent, so they're environmental, not from this change. CI has the full set.Generated by Claude Code