Skip to content

recover: land #186 on master (it merged into #183's branch, not master) - #196

Merged
JC-000 merged 3 commits into
masterfrom
recover/186-onto-master
Sep 6, 2026
Merged

recover: land #186 on master (it merged into #183's branch, not master)#196
JC-000 merged 3 commits into
masterfrom
recover/186-onto-master

Conversation

@JC-000

@JC-000 JC-000 commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Why this exists

PR #186 shows as MERGED, but its content never reached master.

It was stacked on #183, so its base branch was fix/174-dry-run-non-destructive
rather than master. #183 merged to master first; #186 then merged into
fix/174-dry-run-non-destructive, which was already merged and is now a dead
end. tools/test_flags_stamp_skip_is_loud.py is absent from master, and
issue #177 is still genuinely open.

This PR merges #186's branch onto current master so the work lands. No
commits were rewritten and nothing was cherry-picked; #186's two commits are
carried as-is.

Why merging the stacked branch directly would have been wrong

fix/174-dry-run-non-destructive predates #180, so a naive merge of that
branch reverts #180's three files. This PR merges #186's own branch
(fix/177-flags-stamp-skip-loud), which three-way merges correctly.

Verification

Closes #177.

🤖 Generated with Claude Code

JC-000 and others added 3 commits September 5, 2026 18:41
tools/test_build_flags_stamp.py has eight tests, every one of which shells
out to `make`. Each opened with

    missing = _toolchain_missing()
    if missing:
        print(f"SKIP: {missing} not on PATH")
        return

and a pytest test function that returns None without asserting is a PASS.
The module is pinned in pytest.ini `testpaths`, so on a machine without
cc65 -- a CI container, most likely -- bare `pytest` at the repo root
reported `7 passed in 0.01s` having verified nothing.  The SKIP lines were
not even visible: pytest captures stdout on a passing test and discards it.
The standalone runner had the same hole from the other side, printing
`PASSED: 0 failure(s)` for a run in which zero assertions executed.

The invariant being laundered is build/flags.stamp itself -- what closes
CLAUDE.md's two documented silent-failure modes.  #158 adopted the rule (an
involuntary skip is a failure; a voluntary skip is allowed but must never be
silent), #157 reintroduced it, PR #172 closed it again.

Both channels now:

  pytest lane      _require_toolchain() calls _skip_policy.require(), which
                   raises SkipPolicyError carrying the vacuity warning
                   INSIDE the reason string -- under `-ra` the reason is the
                   only channel that survives.
  standalone lane  main() hoists the check and returns cannot_run() = exit
                   2, distinct from 1 ("a check ran and failed"), plus a
                   ran+failed==0 guard so PASSED can never be printed by a
                   run that executed nothing.
  opt-out          C64_ALLOW_SKIP=1 buys exit 0 / a real pytest skip and
                   still prints the warning; exactly "1", so
                   C64_ALLOW_SKIP=0 does NOT open the hatch.

tools/_skip_policy.py is vendored from the #178 Part A work, byte-identical
to the copy on branch test/178a-skip-policy-rig-half (c3cf8a4), so this PR
is green standing alone rather than importing a module that is not on master
yet.  An identical both-added file merges without conflict in either order.
That branch also adds tools/test_skip_policy.py (unit tests for the module)
and wires the rig lane; none of that is duplicated here.

New tools/test_flags_stamp_skip_is_loud.py is the red-green.  It re-runs
the subject module in a subprocess whose PATH has had every directory
containing ca65/ld65 removed -- computed from the real PATH, not a
hardcoded /usr/bin:/bin, so it strips the toolchain wherever it is
installed.  test_the_strip_actually_strips guards the guard: a
PATH-stripping bug would otherwise make every assertion vacuous, one level
up from the defect being fixed.

Measured, with the subject module at the pre-fix revision:

    FAIL test_opt_out_is_explicit_and_still_warns
    FAIL test_pytest_run_without_toolchain_is_not_green
    FAIL test_standalone_run_without_toolchain_exits_cannot_run
    ok   test_the_strip_actually_strips
    FAILED: 3 failure(s) (4 executed)

and after:

    PASSED: 0 failure(s) (4 executed)

Bare `pytest` at the repo root: 60 passed (was 56; +4 from the new module).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#180 (#178a) amended the module after this branch copied it, so the two
copies are no longer identical (theirs c359a9b1..., the vendored one
befcf309...) and `git merge-tree` reports an add/add conflict with five
hunks in both directions. The "merges cleanly in either order" claim this
branch shipped with is now false, and the PR body is corrected to match.

Dropping the copy is free: this branch uses only require(), cannot_run()
and EXIT_CANNOT_RUN, whose contracts did not change, and all five probes
in tools/test_flags_stamp_skip_is_loud.py behave identically against
either version of the module.

Merge order is therefore load-bearing and stated in the PR body:
  #183 (this branch's base) -> #180 (brings _skip_policy.py) -> #186.

Also migrates the guard in
test_an_unrelated_option_does_not_suppress_invalidation, added to the
base branch after this one was written, to _require_toolchain(). It was
the one remaining site still spelling the silent-skip prologue by hand,
which is the whole point of #177.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JC-000
JC-000 merged commit b2931b8 into master Sep 6, 2026
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.

test_build_flags_stamp.py silently vanishes all 7 tests when ca65/ld65 are not on PATH — and it is in pytest testpaths

1 participant