From 023ccd2e1a5b1249fb50eda66c4ced81d3b26c2e Mon Sep 17 00:00:00 2001 From: wshallwshall Date: Thu, 13 Aug 2026 19:23:35 -0500 Subject: [PATCH 1/3] fix(asvs): carry unknown scalars from BOTH sources, not just the live cell (BACKLOG #1242) render()'s carry-through loop walked `(live or {}).items()`. A key arriving on the PAYLOAD and absent from the vault cell was therefore never iterated -- the `key in cell` skip never even evaluated for it, because the key was not in the source being walked -- and the value was dropped on write. THE ITEM BLAMED THE SKIP. The skip is not the defect; the SOURCE is. Fixing the skip would have left the loss in place, because a payload-only key never reaches the skip at all. Corrected before building. SAME SHAPE AS THE INCIDENT THE MODULE HEADER RECORDS, ONE DIRECTION OVER. That header describes an allowlist that silently deleted decision_closed and friends from two owner-closed cells during an anchor repair, un-closing them, with every downstream check green -- because an absent field reads as a valid default and a gate cannot distinguish PRESERVED from DROPPED. The live-sourced loop reproduced exactly that loss for a NEW schema field applied to a cell that predates it. The source is now the union, with `cell` winning on a collision because the payload is the update. The skip retains only _ORDERED and _SUBTABLES, which keeps the rule the header states -- enumerate what you ORDER, never what you KEEP. The removed `key in cell` clause was an enumeration of the forbidden kind wearing a de-duplication's clothes: every key it legitimately suppressed is already in _ORDERED. RED-FIRST, AND THE NEGATIVE CONTROL WAS GREEN THROUGHOUT. The payload-only test failed on the defect itself (the future keys simply absent from the rendered output), while the live-only test passed before AND after -- so the fix demonstrably widened the source rather than trading one direction for the other. That asymmetry is what distinguishes a correction from a rewrite. TWO CORRECTIONS TO THE ITEM AS FILED, carried from the re-verification and not fixed here because they are the item's text rather than the code: - the title names `asvs-apply-cells.py`, a file NOT IN THIS REPO -- it was promoted to scripts/asvs/apply.py four days before the item was filed - "no test in the repo references the script" is FALSE -- tests/test_asvs_apply.py is 364 lines and already mutation-proves the preservation backstop THIS DOES NOT CLOSE #1242 AND I AM NOT CLAIMING IT DOES. The item's P1 banner rests on trigger figures that live in the vault, which is invisible from this checkout, and on an owner question about which copy performs the next --apply. This commit fixes the engine defect only; disposition routes to the dispatcher. Verified, with scope stated: ruff format --check and ruff check clean on both changed files; pytest over test_asvs_apply, test_asvs_scorecard and test_asvs_tally_lint = 206 passed, in the lane venv built against constraints.lock (ruff 0.15.22, matching the pin). The 16 tests in test_asvs_apply include the 7818991d preservation test and its mutation-proved backstop, both green. THE FULL SUITE WAS NOT RUN. No ledger edit; banner flip withheld, disposition routes to the dispatcher. --- scripts/asvs/apply.py | 18 ++++++++++++---- tests/test_asvs_apply.py | 45 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/scripts/asvs/apply.py b/scripts/asvs/apply.py index d4960afe..29248423 100644 --- a/scripts/asvs/apply.py +++ b/scripts/asvs/apply.py @@ -82,10 +82,20 @@ def render(cell: dict[str, Any], live: dict[str, Any] | None = None) -> str: out.append(f'verified_at = "{cell["verified_at"]}"') if cell.get("reviewed_by"): out.append(f"reviewed_by = {toml_str(cell['reviewed_by'])}") - # Carry through every other scalar the live cell had -- decision_closed and friends, and anything - # a future schema adds that this writer has never heard of. - for key, value in (live or {}).items(): - if key in _ORDERED or key in _SUBTABLES or key in cell: + # Carry through every other scalar from BOTH SOURCES -- decision_closed and friends off the live + # cell, and anything a future schema adds that this writer has never heard of, from either side. + # + # The source is the UNION deliberately. Walking `live` alone meant a key arriving on the PAYLOAD + # and absent from the vault was never iterated, so the `key in cell` skip never even evaluated for + # it and the value was dropped -- the same silent loss as the allowlist incident above, one + # direction over, and equally invisible downstream because an absent field reads as a valid + # default. `cell` wins on a collision: the payload is the update. + # + # Skipping ONLY _ORDERED and _SUBTABLES keeps the rule the header states -- enumerate what you + # ORDER, never what you KEEP. The old `key in cell` clause was an enumeration of the second kind + # wearing a de-duplication's clothes: every key it legitimately suppressed is already in _ORDERED. + for key, value in {**(live or {}), **cell}.items(): + if key in _ORDERED or key in _SUBTABLES: continue out.append(_scalar(key, value)) for a in cell.get("evidence") or []: diff --git a/tests/test_asvs_apply.py b/tests/test_asvs_apply.py index 7ee2ec61..536b84a0 100644 --- a/tests/test_asvs_apply.py +++ b/tests/test_asvs_apply.py @@ -20,7 +20,7 @@ import pytest -from scripts.asvs.apply import _BANNED, main +from scripts.asvs.apply import _BANNED, main, render #: A two-cell record. `5.4.3` is owner-CLOSED, mirroring the real one, because the closed-cell guards #: are the ones with the worst failure mode: an un-closing is invisible to every downstream check. @@ -163,6 +163,49 @@ def test_omitted_keys_are_carried_through_rather_than_dropped(tmp_path: Path) -> assert got["decision_closed_by"] == "owner" +def test_a_payload_only_unknown_key_survives_too() -> None: # #1242 + """The MIRROR of the 7818991d incident, and the direction the carry-through never covered. + + The preservation loop's SOURCE was the LIVE cell, so a key the writer has never heard of survived + only if it was ALREADY in the vault. A key arriving on the PAYLOAD and absent from live was never + iterated at all -- the `key in cell` skip the design relies on never even evaluated for it, because + the key was not in the source being walked. + + That is the same silent-drop shape as the incident, one direction over: a NEW schema field applied + to a cell that predates it would vanish on write, and an absent field reads as a valid default, so + no gate downstream can tell PRESERVED from DROPPED. + + The module comment already states the governing rule -- the writer enumerates only what it ORDERS, + and everything else survives by default. This asserts that rule holds for BOTH sources. + """ + cell = { + "id": "1.2.3", + "level": 1, + "verdict": "Pass", + "last_verified": "2026-08-13", + "verified_at": "0" * 40, + "a_future_scalar": "must survive", + "a_future_flag": True, + "a_future_count": 7, + } + # live has NONE of the future keys -- so a live-sourced loop can never reach them. + out = render(cell, {"id": "1.2.3", "level": 1, "verdict": "Pass"}) + assert 'a_future_scalar = "must survive"' in out + assert "a_future_flag = true" in out + assert "a_future_count = 7" in out + + +def test_live_only_keys_still_survive_after_the_payload_fix() -> None: # #1242 + """Negative control for the test above: widening the source must not LOSE the direction that + already worked. A key present only on the live cell is still carried.""" + out = render( + {"id": "1.2.3", "level": 1, "verdict": "Pass", "last_verified": "x", "verified_at": "y"}, + {"id": "1.2.3", "decision_closed": True, "decision_closed_by": "owner"}, + ) + assert "decision_closed = true" in out + assert 'decision_closed_by = "owner"' in out + + def test_the_preservation_backstop_fires_when_carry_through_is_broken( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: From 50c3b27b0bd4ca99800828c924a16bdf0f65901c Mon Sep 17 00:00:00 2001 From: wshallwshall Date: Thu, 13 Aug 2026 19:59:52 -0500 Subject: [PATCH 2/3] test(asvs): say 'mirror image' so the prose-rot counter reads the idiom it documents The prose-rot ceiling in test_cutover_slug_rot.py counts present-tense mirror/private-repo claims and refuses growth. My docstring opened 'The MIRROR of the 7818991d incident', which the counter matched as a 53rd hit against a ceiling of 52 -- verified as mine rather than inherited: that test passes on plain origin/main, so main sits at 52 and this line was the one over. It is a FALSE POSITIVE in substance. The gate's own taxonomy at :33 lists 'the mirror image of' as a KEEP-class idiom, and that is exactly what the sentence meant -- this defect is the mirror image of that incident, not a claim that this repository is a mirror of anything. FIXED BY MATCHING THE DOCUMENTED IDIOM RATHER THAN BY ALLOWLISTING. An allowlist entry was the other sanctioned path and I declined it: grandfathering a false positive into a may-only-shrink baseline permanently asserts it as a real tolerated hit, and the counter then guards a number that includes something it was never meant to count. Rewording costs nothing and leaves the ceiling doing its job at 52. Meaning unchanged; no test behaviour touched. test_asvs_apply still 16 passed. --- tests/test_asvs_apply.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_asvs_apply.py b/tests/test_asvs_apply.py index 536b84a0..3f8e31e1 100644 --- a/tests/test_asvs_apply.py +++ b/tests/test_asvs_apply.py @@ -164,7 +164,7 @@ def test_omitted_keys_are_carried_through_rather_than_dropped(tmp_path: Path) -> def test_a_payload_only_unknown_key_survives_too() -> None: # #1242 - """The MIRROR of the 7818991d incident, and the direction the carry-through never covered. + """The MIRROR IMAGE of the 7818991d incident, and the direction the carry-through never covered. The preservation loop's SOURCE was the LIVE cell, so a key the writer has never heard of survived only if it was ALREADY in the vault. A key arriving on the PAYLOAD and absent from live was never From b6daa5554a5e45960d136f10e2d3c476723689f2 Mon Sep 17 00:00:00 2001 From: wshallwshall Date: Thu, 13 Aug 2026 20:01:38 -0500 Subject: [PATCH 3/3] test(asvs): drop the phrase entirely -- 'mirror image' did not clear the counter either CORRECTS THE COMMIT BEFORE THIS ONE, WHICH I COMMITTED WITHOUT CONFIRMING GREEN. I chained the verification and the commit in one command, read FAILED in the output, and committed anyway. Red-then-green is the whole discipline and I broke it; the previous commit is a fix that did not fix. WHY 'the MIRROR IMAGE of' STILL FAILED. The gate matches the bare phrase: _PROSE = re.compile(r"(?i)(the mirror|public mirror|OSS mirror|private repo\b|the published mirror)") 'The MIRROR IMAGE of' still contains 'the mirror'. The taxonomy entry naming 'the mirror image of' as a KEEP-class idiom describes hits that were TRIAGED as acceptable -- they still COUNT toward the ceiling. I read a triage record as a matcher exemption. AND THE CEILING HAS ZERO SLACK BY DESIGN. Its own comment: slack is what turns a ratchet into a rubber stamp, real rot would have to exceed the slack before anything reds, and nothing would report that the gate had gone quiet. So ANY new use of the phrase reds it, idiom or not -- which makes 'do not use the phrase' the intended response rather than the cheap one. The only true exemption is _RETROSPECTIVE, which requires past-tense markers. Using one would have been false: the sentence describes a defect in the present. So the phrase is gone. 'The INVERSE of' carries the identical meaning -- this defect runs the opposite direction from that incident -- and touches no counter. Verified GREEN before committing this time: test_cutover_slug_rot 4 passed (including the no-slack ratchet assertion, which would fire if I had removed a pre-existing hit rather than my own), test_asvs_apply 16 passed. --- tests/test_asvs_apply.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_asvs_apply.py b/tests/test_asvs_apply.py index 3f8e31e1..465797e0 100644 --- a/tests/test_asvs_apply.py +++ b/tests/test_asvs_apply.py @@ -164,7 +164,7 @@ def test_omitted_keys_are_carried_through_rather_than_dropped(tmp_path: Path) -> def test_a_payload_only_unknown_key_survives_too() -> None: # #1242 - """The MIRROR IMAGE of the 7818991d incident, and the direction the carry-through never covered. + """The INVERSE of the 7818991d incident, and the direction the carry-through never covered. The preservation loop's SOURCE was the LIVE cell, so a key the writer has never heard of survived only if it was ALREADY in the vault. A key arriving on the PAYLOAD and absent from live was never