Skip to content

Commit 3c339a0

Browse files
ccie18643claude
andcommitted
skill(mutation_testing): add the trailing-padding integrity-bound class
Folds in the dominant systematic gap class from the net_proto capstone re-validation (2026-06-10): - New §6 bullet: the trailing-padding integrity bound (`<PROTO>__LEN <= declared_len <= len(frame)`) — the second `<=` tolerance survives because the test harness hard-codes payload_len=len(frame). ~26 of the 40 capstone gaps. Documents the paired LtE_Eq+LtE_GtE survivor signature, the stub-default detection grep, the single trailing_bytes_accepted fix (which also kills the first-`<=` min-length LtE_Lt and both bound lines at once), and the no-parser-test / simpler-helper wrinkles. - §6 dispatch-assert bullet: the "half-closed" partial state (dhcp6 below-only, tcp above-only; code-0 makes the below side vacuous) and its `!=`-guard cousin (hrlen/magic-cookie/version tested above-only). - Intro: records the capstone as the precedent for always running the full-from-scratch re-scan after fixes land, and for these two classes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2949a04 commit 3c339a0

1 file changed

Lines changed: 62 additions & 0 deletions

File tree

.claude/skills/mutation_testing/SKILL.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ equivalent classes (§4). Results:
3232
`docs/refactor/net_proto_mutation_audit_results.md` (the
3333
plan/sharding doc is `…_mutation_audit.md`).
3434

35+
A **capstone re-validation** of that audit (2026-06-10,
36+
full-from-scratch re-scan of all 9 changed shards) confirmed
37+
every shard reproduced its recorded score — no ineffective
38+
fixture — then the sharpened §4/§6 re-triage found **40 more
39+
cheaply-killable survivors** the first pass had bucketed as
40+
equivalent/seam. It is the precedent for **always running the
41+
full-from-scratch capstone after the fixes land** (§8), and
42+
for the **trailing-padding integrity bound** and one-sided
43+
**dispatch-/`!=`-assert** classes (§6) — the two systematic
44+
shapes that dominated the residual.
45+
3546
## When to invoke
3647

3748
- A user asks to "mutation test", "run cosmic-ray", or
@@ -391,6 +402,44 @@ and prints each result; it never strands. Always
391402
test file is a whole-thing gap. Close it with the full
392403
per-file test (the §8 test-matrix in `unit_testing.md`),
393404
not a one-off; it converts the most mutants per unit effort.
405+
- **The trailing-padding integrity bound — THE most prolific
406+
class at scale** (net_proto capstone, ~26 of 40 gaps across
407+
udp, icmp4 ×5, tcp, ip4, icmp6 ×6). Every length-bearing
408+
parser guards a *chained* bound of the shape
409+
`<PROTO>__LEN <= <declared_len> <= len(frame)` (the
410+
`<declared_len>` is the IP/UDP-layer payload length: `plen`,
411+
`ip__payload_len`, `ip6__dlen`). The parser **deliberately
412+
tolerates trailing lower-layer padding** — the second `<=`
413+
accepts `declared_len < len(frame)`. But the test harness's
414+
IP stub almost always hard-codes
415+
`payload_len = len(frame)` (and the `minimum_length_accepted`
416+
boundary fixture uses an exact-fit frame), so **no test ever
417+
has `declared_len < len(frame)`** — and the second
418+
comparison's `LtE_Eq` (`<=``==`) and `LtE_GtE` (`<=``>=`)
419+
mutants survive on *every* such parser. Symptom in the
420+
survivor scan: paired `ReplaceComparisonOperator_LtE_Eq` +
421+
`LtE_GtE` at the same `col` of the `... <= len(frame)`
422+
bound line, recurring identically across sibling message/
423+
protocol files. **Detect it** by grepping the integrity
424+
test for the stub default (`payload_len=len(frame)` /
425+
`dlen=len(frame)`); if every fixture fits exactly, the
426+
tolerance is unpinned. **Close it** with ONE
427+
`trailing_bytes_accepted` boundary test per parser: a valid
428+
*minimum* message (valid checksum over its own bytes) **plus
429+
N padding bytes**, with the IP stub's `payload_len` set to
430+
the *un-padded* length (`< len(frame)`); assert it parses.
431+
That single frame also exercises the *first* `<=` at
432+
equality (`<PROTO>__LEN == declared_len`), so it kills the
433+
min-length `LtE_Lt` survivor for free, and — because it
434+
drives the full parser — it kills the same mutant on *both*
435+
the parser-level and per-message bound lines at once (cf.
436+
tcp L81 + L93). Watch for messages with **no parser test at
437+
all** (icmp6 Packet Too Big): there the whole bound is
438+
unpinned and you add a fresh integrity-boundary file. The
439+
one harness wrinkle: combined `*__parser.py` files often use
440+
a simpler `_packet_rx_with_ip*(frame)` helper with no
441+
`payload_len` kwarg — extend it to
442+
`(frame, *, payload_len=None)` before adding the test.
394443
- **The dispatch-guaranteed assert, untested everywhere**
395444
(net_proto). The `buffer[0] == int(Type)` / `from_bytes(...)
396445
== int(Type)` kind-byte assert at the top of every option's
@@ -403,6 +452,19 @@ and prints each result; it never strands. Always
403452
`from_buffer` over a valid frame, expecting `AssertionError`.
404453
Detect the gap quickly by scripting the `<=` mutation across
405454
every option and re-running just that option's suite.
455+
**Half-closed is the common partial state** (capstone
456+
re-validation): an earlier pass often pins only ONE side —
457+
dhcp6 had wrong-type-*below* but not *above* (6 options →
458+
`Eq_GtE` survives), tcp had *above* but not *below* (5
459+
options → `Eq_LtE` survives). For an option with code `c`,
460+
`Eq_GtE` dies only on a byte `> c`, `Eq_LtE` only on a byte
461+
`< c`; a code-0 kind-byte (`EOL`/`PAD`) makes the *below*
462+
side vacuously equivalent (no byte `< 0`). Always check
463+
*which* direction survives and add the missing one. The
464+
same one-sided shape appears on `!=` field guards
465+
(`hrlen != 6`, `magic_cookie != COOKIE`, `ver != 4`):
466+
tested only *above* the value, `NotEq_Gt` survives until you
467+
add a *below* case — see "One-sided length boundaries".
406468
- **Degenerate / weak fixtures.** The single most common
407469
*arithmetic* gap. A test asserts the right output but for an
408470
input where many mutations coincide:

0 commit comments

Comments
 (0)