Skip to content

fix(asvs): compare the type the payload stated, not the keys it did not mention (BACKLOG #1242) - #612

Merged
wshallwshall merged 4 commits into
mainfrom
lander/1242-typeguard-lift
Aug 26, 2026
Merged

fix(asvs): compare the type the payload stated, not the keys it did not mention (BACKLOG #1242)#612
wshallwshall merged 4 commits into
mainfrom
lander/1242-typeguard-lift

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Lifts the two live commits from the stranded PR #487 (lander/432-resolved), per Cleaner's
grader-then-refuter grading (.git/mefor-coord/handoffs/CLEANER-2026-08-26-FOR-LANDER-stranded-pr-grading.md).
Everything else on that 45-commit branch is either superseded by what already landed on main or a
drop; this is the two-commit live remainder, cherry-picked onto a fresh branch off current main
rather than resolving PR 487's own 13-hunk conflict.

  • 5e03aa59c -- BACKLOG #1242's own fix: the type-guard compared against the KEYS the payload did
    not mention (k not in c), not the TYPE it stated. A payload that CARRIES a key it is corrupting
    passed silently. Found by the ASVS Tracker against a scoping gap the original author flagged as
    unable to check themselves.
  • 6dd9a0b2c -- drops a vault-derived cell total from the comment the fix above introduced. The
    figure discloses the uncovered set by subtraction over a closed public requirement set, which is
    why the scorecard is vaulted in the first place.

Both cherry-picked clean except one conflict in scripts/asvs/apply.py + tests/test_asvs_apply.py:
main had since landed BACKLOG #1307 (retirement) and #1308 (banned-glyph) tests in the same region.
Not a real conflict -- both sides added independent tests near the same anchor. Resolved by keeping
all of them: the two new #1242 tests slot in immediately after the existing retype test they are
siblings of (the test docstring calls it "the sanity arm"), main's #1307/#1308 tests keep their
position after. Verified no duplicate test functions and no leftover conflict markers before
committing.

Verified before pushing:

  • test_asvs_apply.py: 33/33 passed (ruff format/check clean, mypy clean).
  • The type fix itself: confirmed against origin/main's actual (pre-cherry-pick) content that the old
    k not in c and type(was[k]) is not type(now[k]) guard is what's live today -- the code Cleaner
    described as already-fixed only appeared mid-conflict because git's 3-way merge had already
    auto-applied that non-conflicting hunk; caught and re-verified against git show origin/main:...
    directly before trusting it.
  • Claimed BACKLOG #1242 (scripts/coord/claim.ps1 -Take 1242) before committing; not claimed by
    anyone else at the time.

Does not touch PR 487 itself, which stays open pending its own precondition/DROP list from the same
grading pass.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

wshallwshall and others added 2 commits August 26, 2026 10:09
…ot mention (BACKLOG #1242)

RETRACTING MY OWN SCOPING, on someone else's measurement. I wrote the type guard's
`k not in c` clause and flagged it as the part my own judgment could not check --
a mutation test by the author proves a guard is CONNECTED, never that it is
connected to the right thing. The ASVS Tracker measured it and the hole is real.

THE HOLE. With the writer's dict branch disabled, a payload OMITTING the key was
refused while a payload CARRYING it exited 0 and wrote a Python repr into a TOML
string. So the guard stopped looking at the exact moment a cell is rewritten.

NOT A CORNER, and this is why it outranks a tidier fix: of 345 cells in the record
exactly ONE holds a top-level non-scalar, and the natural payload for rewriting
that cell ECHOES the key. The guard covered every cell that cannot be hurt and
skipped the one that can.

THE FIX KEEPS THE PROPERTY THE SCOPING WAS FOR. The intent was right -- a payload
that intentionally retypes a field is an EDIT, and a guard refusing legitimate
writes is a guard someone disables. The payload STATES a type, so compare against
it rather than declining to look: an intentional retype agrees with its own
payload and passes, a writer corruption disagrees in BOTH zones.

_ORDERED is excluded because render() coerces those by design (int(cell['level']),
the quoted emissions), so a payload stating another type there is NORMALISED, not
corrupted -- refusing it would be the cry-wolf failure the scoping exists to avoid.

MUTATION-PROVEN, and it changed the change. Four mutants:
  revert to `k not in c`            -> killed
  always use the LIVE type          -> killed
  drop the _ORDERED exclusion       -> SURVIVED, so I wrote the test that kills it
  drop the _SUBTABLES exclusion     -> SURVIVES, and is documented as such
The third is the point: 23 tests stayed green while that clause did nothing, which
is this item's own defect one level up. _SUBTABLES is left in as belt-and-braces
with the reason written down rather than claimed as covered -- evidence and absence
render as arrays of tables on both sides, so it cannot fire today, and that is a
property of the current writer rather than an invariant.

VERIFIED: ruff format + check clean (0.15.22, matching constraints.lock), mypy
clean on the changed module, 24 passed in tests/test_asvs_apply.py and 411 passed
/ 23 skipped across -k asvs. Interpreter resolves messagefoundry to THIS worktree,
checked by printing __file__. The venv lacks the x12/xml extras, so pytest printed
INCOMPLETE RUN -- none of these is a full-suite claim.

THIS DOES NOT CLOSE #1242, and the banner is not mine to write.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The figure is vault-derived and this file ships to PyPI. A coverage count over a
closed public requirement set discloses the uncovered set by subtraction, which is
why the scorecard is vaulted in the first place.

`main` already words this clause without a figure. Merging this branch as-is would
have brought the number BACK, and `git merge-tree` reports NO CONFLICT because the
two sides edited different line ranges of the same comment -- so nothing would have
objected. That was the second of two blockers recorded on PR #487.

ONLY THE COMMENT MOVES. The guard itself is this branch's fix and is deliberately
untouched: main still carries `k not in c`, which skips every key the payload
carries, and repairing that is what these commits are for.

RECORDED BECAUSE I NEARLY DID THE OPPOSITE. Comparing the two comment blocks, I
judged main's better-worded and figure-free version to be the later revision and
concluded the fix was to take main's whole block. It is the EARLIER one. Diffing
the CODE rather than the prose showed main still has the unrepaired guard, so
taking its comment would have shipped a comment describing an implementation that
no longer exists -- and would have read as reverting the fix. Prose quality is not
version order.

A note now sits inline saying the total is omitted on purpose, so a future merge
that reintroduces it has something to contradict.
@wshallwshall
wshallwshall enabled auto-merge (squash) August 26, 2026 15:12
@wshallwshall
wshallwshall merged commit 8a33a24 into main Aug 26, 2026
39 of 40 checks passed
@wshallwshall
wshallwshall deleted the lander/1242-typeguard-lift branch August 26, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant