Repository navigation
test: 29 mutants killed in −2 net test lines — jsoniter oracle rewrite, circe bounds property, core exists/Index, and the equivalent-mutant catalogue - #111
Merged
Conversation
kryptt
changed the base branch from
fix/nominal-resolution-onto-main
to
main
September 18, 2026 13:31
…x property — kills the end-of-input guard cluster
The prefix property drove every skip*/find* guard already; its oracle was
`Try(...).isSuccess`, i.e. "did not throw". Every mutant in the cluster turns
a Miss into a wrong-but-non-throwing Span, so the oracle was blind to all of
them.
Replaced in place with a model-generator + three-part oracle:
(a) ABSOLUTE — the generator returns the rendered children alongside the
document, so each top-level step`s expected span text is known without
a second parse. A purely differential oracle compares two runs of the
SAME mutated code and passes any uniformly-wrong scanner.
(b) BOUNDS — 0 <= start <= end <= len on both `find` and `findAll`.
(c) DIFFERENTIAL — R1/R2/R3 between prefix and full.
Separators became ", " and child counts became frequency-weighted so empty
containers occur in value position and a post-comma-space cut is reachable.
Exception = failure now, so the no-throw guarantee is kept for free.
Subsumed and deleted: section 8`s rootHit/negHit/untermOk clauses, and
literalCases rows 1-6 (valid + truncated literals). Widened the existing
numberGen exponent alternatives with digit-less "e"/"E".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
…can reach — kills 11 mutants in 9 data rows
Each row is a byte layout whose broken syntax is followed by bytes that
accidentally spell the probed member/element, so a dropped guard mis-parses
onto the target instead of being rejected by a later guard:
1"target":2 a step applied to a non-container value (78/85/108/113/118)
{"a":} a value position holding no value at all (240:41)
{Xk0":1} key-quote guard; skipString finds the CLOSING quote (168)
{"k0"1 2} colon guard (173:8)
...nested twins the same two inside a SKIPPED value (254:8, 258:8)
The existing malformed block`s oracle widened from `!find(...).isHit` to also
require `findAll(...) == Nil`, which is what makes the walkAll guards
observable; the widening covers the six pre-existing rows at no extra lines.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
…umed, 0 kills lost The new absolute oracle asserts each top-level child`s span text by index on every generated document, which is exactly what these two enumerated sweeps were: a target-position sweep plus absent-key / index-past-end / negative-index Miss rows plus a skip-over-a-sibling assertion. Measured, not assumed: stryker before the deletion 328K/58S, after 328K/58S — zero regressions by mutant key (file, line, column, mutator, replacement). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
…0 lines `JsonWalk` carries two copies of `idx < 0 || idx >= arr.length`: one in `readPath` (:56), one in `modifyPath` (:81). `JsonIndexBoundsSpec` was written for :56 but drove only `.modify(...)`, so all six operator mutants on the read copy survived while the write copy was 11/11 killed. Replace the hard-coded example with one `forAll(Gen.chooseNum(-2, 5))` property over a fixed size-3 array whose expectations are DERIVED from the backing `Vector`, driving three surfaces per index: `get` (read → :56), `modify` (Ior write → :81) and `modifyUnsafe` (silent pass-through). The range straddles all five discriminating classes — i<0, i=0, 0<i<3, i=3, i>3 — which is what the two `>=` variants need (i=3 alone kills `>=`→`>`, i>=4 alone kills `>=`→`==`). Deletions: the subsumed `unsafeOOR` / `defaultOOR` / `negIndex` assertions in JsonPrismSpec. `defaultOOR` was already redundant with JsonFailureSpec:66-71; the other two are subsumed by the property's `silent` conjunct, which asserts pass-through across every out-of-range class rather than two constants. Measured: circe 38K/12S → 44K/6S (76.00% → 88.00%), net -10 test lines, suite test count unchanged. The remaining 6 survivors are provably equivalent mutants (`i >= n` → `i == n` on two `+ 1`-incremented loop heads, and four empty-path shortcuts whose general branch computes the same value), so circe is now at its killable ceiling. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
…in 2 lines `Index`'s scaladoc promises a missed write is a silent pass-through: the Seq setter must not throw and the Map setter must not insert. Both guards survived mutation despite ContainerOpticsSpec already writing through a missed Index — `Optional#modify` returns `getOrModify`'s `Left(t)` directly and never calls the stored `reverseGet`, where the guard lives. No optic-surface operation reaches it (`from` only calls it on `Affine.Hit`; `fuseToOptional` only on `Right`; `OptionalLaws` holds the erased `Optic[S, S, A, A, Affine]`), so the assertion is made against the public `reverseGet` field. kills: optics/Index.scala:37:27, optics/Index.scala:55:27 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
…ple out / 1 property in
Replaces OpticsBehaviorSpec's 14 hand-written "ForgetfulFold extensions"
assertions with one property over an independent oracle: for each carrier row,
foldMap / headOption / length / exists must agree with an expected focus list.
Two receiver shapes are load-bearing. `extAgrees` ascribes the optic to the
bare `Optic[…]`, the only way to reach the Optic.scala EXTENSION — `Traversal`
and `ForgetFold` (via `CanFold`) both declare member twins that win by
precedence, which is why the old `each.exists(…)` / `listFold.exists(…)` lines
never touched it. `cfAgrees` ascribes to `CanFold`, reaching the trait body.
The generator straddles both monoid classes on purpose: k in {-1, 4} with
n >= 1 gives "some focus satisfies" (needed for `||` -> `&&`), k = 10 and n = 0
give "none satisfies / empty fold" (needed for the `false` seed).
Also drops CapsMatrixSpec's `(zipOptional: CanFold[…]).exists(_ > 999999)`
line and its comment: the claim was false. `zipOptional`'s carrier is `Affine`
and `addr` HITS, so `foldMap` returns `f(a)` and the monoid identity is never
consulted — measured, that assertion killed nothing.
kills: CanFold.scala:27:41, optics/Optic.scala:443:69
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
…roperty `JsonFailureSpec` asserted that `at(5)` on a one-element array yields `Ior.Both(IndexOutOfRange(Index(5), 1), …)`. That is one point of what the rewritten `JsonIndexBoundsSpec` property now asserts across the whole index range (-2..5 against a size-3 array), against an expectation derived from the backing `Vector` rather than hard-coded — so the example adds no discrimination the property does not already have. The message projection (`size=3`), which the property does not look at, stays. Measured: `project circeIntegration; stryker` re-run after the deletion gives 44 Killed / 6 Survived / 36 Ignored, byte-identical to the run before it on the full (file, line, column, mutator, replacement) key — 0 mutants flipped, 0 keys added or removed. -7 body lines, suite_test_count unchanged (the block is a composite that keeps its four other cases). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
The mutation sweep keeps getting re-triaged from scratch because the page
records only scores, never which survivors are unkillable. Forty of them are:
perf fast paths whose general path computes the same value, and loop-boundary
operators on cursors that advance by one. They are now tabulated per module,
keyed (file, line:column, mutator -> replacement), each with the reason read
off the source — core 19 (its entire survivor set), circe 6 (likewise),
schemes 8 (likewise), jsoniter 7 of 58.
Deliberately NOT certified: the 51 remaining JsonPathScanner survivors and
JsoniterPrism:130:8. One class of them was measured wrong today — 254:13
`>=` -> `>` was called equivalent and is killable, because the mutant makes the
left disjunct dead-false and the right one then indexes at
`kpos == bytes.length`. Any guard whose right-hand side indexes the array needs
`pos == length` reachability checked individually first; the page says so
instead of pretending.
Two caveats on this page were false and are retired, both re-tested today:
avro ("stryker's forked test-runner fails to initialise") scores in ~2 min, and
jsoniter ("instrumenting PathParser.parseField overflows the 64 KB method
limit") mutates clean with 0 compile errors. Root cause of the staleness:
gen-qa-report.py hard-codes the Notes column per module, so the caveat printed
over real numbers for months. The notes are corrected at the source and the
comment above them now says a claim of unscoreability must be retired when
numbers land.
Recorded as structural and permanent instead: generics' 86 NoCoverage mutants,
avro's 17 AvroPrismMacro ones and kyo's 28 RecordIsoMacro ones are all
quoted-macro bodies that expand in the compiler and have no runtime footprint.
Also adds the never-before-reported zio (0 mutants exist — no pool, score n/a)
and kyo (83.3% covered, needs the braced extension of #109 to run at all)
readings, and a note that the generated table lags between release tags, with
the current sweep's numbers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
kryptt
force-pushed
the
test/kill-survivors-jsoniter-circe-core
branch
from
September 18, 2026 13:40
a3501ac to
f8bc796
Compare
Contributor
|
🚀 Cloudflare Pages preview for https://46b33363.cats-eo-docs.pages.dev Branch alias: https://test-kill-survivors-jsoniter.cats-eo-docs.pages.dev Built from commit |
Contributor
Benchmark A/BAllocation (B/op) — authoritative
159 more benchmarks
Timing (ns/op) — directional only, same-VM but shared runner
base_sha: |
This was referenced Sep 18, 2026
Merged
kryptt
added a commit
that referenced
this pull request
Sep 18, 2026
The rebase onto main invalidated this branch's own premise. When it was written, `zio` was `ZEnvironment` / `ZLayer` / `Ref` wiring that delegated straight into ZIO's API, so stryker generated ZERO mutants for it and the honest row was `n/a`. #92 then landed the ecosystem optics — the zio-schema `DynamicValue` kit, the zio-json AST and `JsonCursor` bridge, the STM focus ops, `Chunks` — and with them a great deal of genuinely mutatable code. `project zioIntegration; stryker` on the rebased tree does not print `n/a`. It aborts, with the same UnableToFixCompilerErrorsException that #109 fixed for kyo, on `json/JsonOptics.scala`: 'Extension without extension methods' 'Not found: type To' / 'Not found: self' / 'Not found: type A' Same mechanism, same fix. stryker4s 0.20.3 re-prints every mutated file through scalameta; a SINGLE-METHOD significant-indentation `extension` clause is not a `Term.Block`, so it comes back as the one-line form with the method's leading Scaladoc replayed between the two, and the forced newline lands `def` in column 0. Four such clauses exist in the module — two in `json/JsonOptics` (`JsonCursor.optional`, `JsonCodec.stringPrism`) and two in `schema/SchemaOptics` (`Schema.dynamicPrism`, `BinaryCodec.prism`). Bracing their bodies makes the printer emit the braced form and the newline harmless. `ZioOptics`'s single-method clause already hoists its Scaladoc above `extension`, which dodges the same trap, so it is left alone. Verified, not asserted: `javap -p -c` over `JsonOptics$package$` and `SchemaOptics$package$` is byte-for-byte identical before and after, and `zioIntegration/test` passes unchanged (268 examples). With that, both effect-system rows are real measurements rather than placeholders, regenerated by `site/tools/gen-qa-report.py` from the actual report.json: | `zio` | 37 | 0 | 6 | 0 | 0 | 86.0% | 86.0% | | `kyo` | 30 | 0 | 5 | 28 | 0 | 47.6% | 85.7% | zio is a clean high-signal row: 37 killed, 6 survived, and no no-coverage block at all. kyo moved off #109's 20/4/28 (38.5% / 83.3%) because #111's survivor-killing pass landed in between; its 28 no-coverage mutants are still all `RecordIsoMacro`, so the note stands. The generator's `n/a` branch stays. Nothing hits it today, but rendering a zero-mutant module as the same em-dash it uses for "no report at all" conflates "nothing to score" with "nothing ran", and printing 0.0% would read as a test-quality failure where there is no bar to fail. The page now says so explicitly rather than pointing at zio as the example. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V
kryptt
added a commit
that referenced
this pull request
Sep 18, 2026
…run (#113) * fix(qa): score zio and kyo on the QA page `site/tools/gen-qa-report.py`'s `MUTATION_MODULES` stopped at `jsoniter`, so the two effect-system integrations never had a row on the quality-assurance page even though `mutationAll` has been running stryker over both. The generator looked for reports it was never told to look for, which is the quietest possible failure: no error, no empty row, just absence. `.github/workflows/quality.yml`'s per-module loop had drifted the same way, and further: it was missing `schemesLaws` too. A module absent from that loop produces no report.json on the release-tag run, so its row falls back to dashes — or, worse, keeps whatever numbers a developer's local run happened to leave on disk. The loop now matches the `mutationAll` alias, and both lists carry a comment saying they must stay in step. The loop takes sbt PROJECT IDS (`zioIntegration`), `MUTATION_MODULES` takes DIRECTORIES (`zio`); getting that backwards is what silently finds nothing, so it is written down. Two honest edge cases the rows had to render correctly: - `zio` generates ZERO mutants on main — it is `ZEnvironment` / `ZLayer` / `Ref` wiring that delegates straight into ZIO's API, with no operator, literal or branch for a mutator to change. That is `n/a`, NOT 0%: a module with nothing to mutate has not failed a test-quality bar, it has no bar to fail. The old code divided only when the denominator was non-zero, but it rendered the empty case as the same em-dash it uses for "no report at all", conflating "nothing to score" with "nothing ran". `n/a` is also what stryker's own console prints here. - `kyo` scores 38.5% total / 83.3% covered, and the entire no-coverage block is `RecordIsoMacro` — quoted-macro code that expands at compile time, the same structural reason `generics` scores 0%. The Notes column now says so, so the total score does not read as a coverage hole. The all-NoCoverage shape (`generics`: 0.0% total, covered undefined) is deliberately left alone — those mutants exist, they are simply never executed. Rows verified end to end: stryker was run per-module in a clone (`project zioIntegration; stryker`, same for kyo — never `<module>/stryker`) and the generator read the resulting report.json for both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V * fix(zio): brace the extension clauses, and score zio for real The rebase onto main invalidated this branch's own premise. When it was written, `zio` was `ZEnvironment` / `ZLayer` / `Ref` wiring that delegated straight into ZIO's API, so stryker generated ZERO mutants for it and the honest row was `n/a`. #92 then landed the ecosystem optics — the zio-schema `DynamicValue` kit, the zio-json AST and `JsonCursor` bridge, the STM focus ops, `Chunks` — and with them a great deal of genuinely mutatable code. `project zioIntegration; stryker` on the rebased tree does not print `n/a`. It aborts, with the same UnableToFixCompilerErrorsException that #109 fixed for kyo, on `json/JsonOptics.scala`: 'Extension without extension methods' 'Not found: type To' / 'Not found: self' / 'Not found: type A' Same mechanism, same fix. stryker4s 0.20.3 re-prints every mutated file through scalameta; a SINGLE-METHOD significant-indentation `extension` clause is not a `Term.Block`, so it comes back as the one-line form with the method's leading Scaladoc replayed between the two, and the forced newline lands `def` in column 0. Four such clauses exist in the module — two in `json/JsonOptics` (`JsonCursor.optional`, `JsonCodec.stringPrism`) and two in `schema/SchemaOptics` (`Schema.dynamicPrism`, `BinaryCodec.prism`). Bracing their bodies makes the printer emit the braced form and the newline harmless. `ZioOptics`'s single-method clause already hoists its Scaladoc above `extension`, which dodges the same trap, so it is left alone. Verified, not asserted: `javap -p -c` over `JsonOptics$package$` and `SchemaOptics$package$` is byte-for-byte identical before and after, and `zioIntegration/test` passes unchanged (268 examples). With that, both effect-system rows are real measurements rather than placeholders, regenerated by `site/tools/gen-qa-report.py` from the actual report.json: | `zio` | 37 | 0 | 6 | 0 | 0 | 86.0% | 86.0% | | `kyo` | 30 | 0 | 5 | 28 | 0 | 47.6% | 85.7% | zio is a clean high-signal row: 37 killed, 6 survived, and no no-coverage block at all. kyo moved off #109's 20/4/28 (38.5% / 83.3%) because #111's survivor-killing pass landed in between; its 28 no-coverage mutants are still all `RecordIsoMacro`, so the note stands. The generator's `n/a` branch stays. Nothing hits it today, but rendering a zero-mutant module as the same em-dash it uses for "no report at all" conflates "nothing to score" with "nothing ran", and printing 0.0% would read as a test-quality failure where there is no bar to fail. The page now says so explicitly rather than pointing at zio as the example. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Rebased onto
main— #105 has landed (squashed as75c7eaa), so this branch no longer carries its six commits and the diff is this PR's own work only: 9 files, jsoniter/circe/tests/specs plussite/docs/quality-assurance.mdandsite/tools/gen-qa-report.py. No stack ordering left to respect.Supersedes #108 (circe) and #110 (jsoniter): their commits are cherry-picked here unchanged, plus the core artifacts, a consolidation fold and the QA-page documentation. Close those two unmerged rather than merging them alongside this.
One
improve-test-leveragerun against the 2026-09-18 whole-repo sweep (201 survivors, avro handled separately). 29 mutants killed for −2 net test lines. The headline result is not a coverage gap closed — it is an oracle bug: the jsoniter prefix property already drove all 69JsonPathScannersurvivors, on every prefix, five paths, both surfaces, but asserted onlyTry(…).isSuccess, i.e. "did not throw" — and every one of those mutants turns a Miss into a wrong-but-non-throwing Span. Replacing the assertion, not adding tests, is what moved the number.Per module
jsonitercircecore(artifacts intests/)Repo-wide survivors 201 → 173.
core's 22 − 4 = 18 but reads 19 becausedata/ObjArrBuilder.scala:17:8flipped Timeout → Survived between runs — the documented timeout-oscillation class, in a builder path neither artifact touches. Recorded as incidental, not claimed, not treated as a regression.Every kill is verified by diffing
report.jsonagainst the baseline sweep on the full(file, line, column, mutator, replacement)key. 0 regressions, 0 mutant keys added or removed, in any module.Artifacts, kills per line
isSuccessprefix oracle, rewritten in placeskipObjectLooptwins)e/Eadded to the existing exponent alternativesGen.chooseNum(-2, 5)× {get, modify, modifyUnsafe}exists(p) === foci.exists(p), both surfaces)Indexmiss-write at the setter surfaceJsonFailureSpec'sIndexOutOfRangefire scenario folded into the circe propertyObjective terms
suite_test_count(the 3 suites)suite_test_loc(the 3 suites)suite_kill_densitysuite_test_countdelta −2 (jsoniter 56 → 54; circe andtests/unchanged — one example block out and one property in, and theIndexkills are assertions appended to two existing examples).iteration_leverage: 29 kills against a negative denominator. The suite is smaller and kills more.Three findings worth keeping
245:8and245:14(skipObjectreturning −1 for{}on prefix and full alike) would both have been missed.find("{\"k0\":12", $.k0)returns the Hit1. The strongest true form is full span fits inside the prefix ⇒ prefix result equals full result, with the weakened converse (sp.end <= sf.end) as a separate clause.Traversaldeclares its ownexistsandForgetFoldinherits one fromCanFold, which is exactly why the pre-existingeach.exists/listFold.existsassertions never touchedOptic.scala:443. The property has to ascribe its receiver to the bareOptic[…]to reach the extension, and use a multi-focus carrier (Tuple2'sfoldMapisf(a); it never evaluatescombine).Two plan premises were refuted by the source and the artifacts built around the corrections:
ContainerOpticsSpecdid already write through a missedIndex(the mutants survive becauseOptional#modifyshort-circuits ongetOrModify'sLeftand never calls the storedreverseGet— so the assertion has to land on the rawOptional#reverseGetfield), andCapsMatrixSpec's line claiming to coverCanFold.scala:27covered nothing (its carrier isAffineand the subject hits, so the monoid identity is never consulted) — deleted.Consolidation
Measured, never assumed: after every deletion the affected module's stryker run was repeated and diffed by mutant key.
unsafeOOR/defaultOOR/negIndexand theircovers:lines;defaultOORwas already redundant withJsonFailureSpec. ThenJsonFailureSpec'sIndexOutOfRangefire scenario —at(5)on a one-element array is one point of what the new property asserts over the whole index range. Re-run: 44 Killed / 6 Survived, byte-identical, 0 keys moved.ForgetfulFold extensionsexample block (14 hard-coded assertions, no oracle) replaced by the property; the falseCapsMatrixSpeccoverage line deleted.Net
suite_test_count−2 and net LOC −2.core's own row is +10 lines: the property needs two generic agreement helpers plus five carrier-ascribed vals where the old block used 14 fixed assertions — assertion strength rose much further than LOC (14 fixed assertions → 24 oracle-checked assertions per generated row over 15 input shapes), and the twoIndexassertions are a deliberate add, since the examples they sit beside pinOptional#modify's miss semantics and must not be removed.QA page: the equivalent mutants are now documented
site/docs/quality-assurance.mdgains a Known equivalent mutants section — 40 survivors keyed(file, line:column, mutator → replacement), each with the reason read off the source, not inherited from the sweep:core19 (its entire survivor set),circe6 (likewise),schemes8 (likewise),jsoniter7 of 58. They are perf fast paths whose general path computes the same value (PSVec.unsafeWrapnormalising a 0-length array back toEmpty;freezetrimming either way;transformMachineagreeing with the on-stack walk), and loop-boundary operators on cursors that advance by exactly one. Every future run of this skill re-triaged them; now it doesn't have to.Deliberately not certified: the 51 remaining
JsonPathScannersurvivors andJsoniterPrism:130:8. One class was measured wrong this run —254:13 >=→>was called equivalent and is in fact killable (with>the left disjunct is dead-false and the right one then indexes atkpos == bytes.length). The page says so, and states the rule: a guard whose right-hand side indexes the array needs the reachability ofpos == lengthchecked individually before it may be called equivalent.Two stale caveats retired, both disproven by measurement today
PathParser.parseFieldoverflows the JVM 64 KB method limit". False. 420 mutants, 0 CompileError;PathParser.scalaitself produced 24 mutants, 20 killed.Root cause of the staleness:
site/tools/gen-qa-report.pyhard-codes the Notes column per module, so each caveat printed over real numbers for months (numbers have been landing sinceffe5116). Fixed at the source, with a comment saying a claim of unscoreability must be retired once numbers land.Recorded as structural and permanent instead, so nobody retries it:
generics0 % (86 NoCoverage), avro's 17AvroPrismMacroand kyo's 28RecordIsoMacroNoCoverage mutants — all quoted-macro bodies that expand inside the compiler and have no runtime footprint. Plus the two modules that never had a row:zio(0 mutants exist — no pool, scoren/a, not 0 %) andkyo(83.3 % covered, but unscoreable until the single-methodextensionblock of #109 is braced).Law candidate (gated, not promoted)
exists(p) === foci.exists(p), withlength/headOptiontwins, as aFoldLaws/CanFoldTestslaw — it would fire at everycheckAllsite repo-wide and in downstream clients. Law promotion is user-gated; flagged, not done.Gates
sbt "scalafixAll; scalafmtAll"thensbt "scalafmtCheckAll; scalafixAll --check"clean;sbt "++ 3" teston the root aggregate green under Temurin 25 (kyoIntegration/name→cats-eo-kyo, so kyo was in);docs/mdoc+docs/laikaSitebuild (only the pre-existing slug warnings). avro was not touched.🤖 Generated with Claude Code
https://claude.ai/code/session_0194EHFR4NamCpTHiqy7B74V