Skip to content

S1: the no-copy scan must not print what it forbids #242

Description

@Polichinel

Part of the #241 epic. Registers C-89.

Problem

test_no_coordinate_value_is_copied_into_this_repo exists because README.md once carried four real coordinate values in a public repository. It has three parts, and they disagree about the one rule that matters: never print the value.

The Python branch prints it. tests/test_env_declaration.py:1065:

copied.append(f"{source.relative_to(_PKG)}:{node.lineno} = {node.value!r}")

The markdown branch, 68 lines below, was rewritten on 2026-08-11 to report "assigns a registry value to {name}" for precisely this reason. _describe_changes was added in the same change with a docstring whose whole subject is that a value must never reach a public log. The invariant was stated, applied to one branch, and left off the other. This fires on exactly the violation the scan exists to catch, and the file's own comment records that this half runs in CI as of 2026-08-10. CI logs are world-readable and are not retroactively redactable.

The rotation proof checks one side. :937:

assert "value:" in changed[canary] and "at-the-pin" not in changed[canary]

Only the pinned value is asserted absent. The freshly rotated value — the more damaging one, and the one the fixture already names "after-rotation" — is never asserted absent. A regression in _describe_changes that digests one side and interpolates the other passes this proof.

Work

  1. Change the AST branch to report location and coordinate name only, matching the markdown branch's wording. The name is not secret; the value is.
  2. Extend the rotation proof to assert both fixture values absent from the message.
  3. Add one guard that asserts the no-print invariant about the test as a whole, not about a branch — e.g. a check that no registry value appears in any message this test can construct, exercised over a synthetic registry with both branches firing. This is the piece whose absence let one branch drift from the other.

Acceptance criteria

  • Injecting a real coordinate value as a .py string constant fails the scan, and the failure message contains neither the value nor any substring of it.
  • Injecting the same value in markdown fails, with the same guarantee.
  • Both branches' messages are asserted value-free by a test, not by review.
  • The rotation proof asserts both "at-the-pin" and "after-rotation" are absent; mutation-proven by interpolating each side raw in turn and watching the proof fail.
  • Full suite green, ruff clean.

Implementation notes

Do this story first. S2 rewrites the matcher; getting the no-print invariant asserted before that moves means S2 cannot reintroduce this.

The "about the whole test" guard is the interesting part and should not be skipped as ceremony — it is the only thing that would have caught this. One approach: factor message construction into a small helper both branches call, and assert over the helper.

Testing

pytest -q tests/test_env_declaration.py, plus the two injections above run and reverted. Use cp backups; never git checkout --, git stash, or git restore.

Labels

story, implementation, testing

Activity

  1. added
    storyA single reviewable unit of an epic
    testingTest/parity/validation work
    on Aug 12, 2026
  2. Polichinel commented on Aug 12, 2026

    @Polichinel
    CollaboratorAuthor

    Gate 1 — this blocks PR #240. Unchanged in scope; it is first because the invariant must be in place before #243 moves the matcher under it.

    One note from the expert review: #243's pair-matching approach removes the captured value entirely, so it will hold this invariant structurally rather than by discipline. That is a reason to do #243 soon after — not a reason to skip this one, since the AST branch prints regardless of how markdown is matched.

  3. added a commit that references this issue on Aug 12, 2026
  4. Polichinel commented on Aug 12, 2026

    @Polichinel
    CollaboratorAuthor

    Landed in #252 (merged to development).

    Four iterations. The fourth deleted 140 of the 195 lines the first three added — including two of my own additions that were themselves defective:

    • the drift-report test claimed to drive branches it never entered (_ABSENT is a sentinel object, not None), and those branches return constant strings and cannot leak
    • _carries was strictly worse than plain in — measured, it condemns a bare safe file path, because this package's directory names are five-character declared values

    The leak is closed and mutation-proven, the guarantee keeps exactly one 19-line check, and the file ends at 1200 vs 1145 rather than 1340.

    Four surviving scope mutations routed to #243. Two residuals and two deferrals registered under C-89 with triggers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    implementationCode implementation workstoryA single reviewable unit of an epictestingTest/parity/validation work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions