Skip to content

fix(ci): run the tests the badge claims to run - #26

Open
ksdisch wants to merge 4 commits into
mainfrom
fix/ci-runs-tests
Open

fix(ci): run the tests the badge claims to run#26
ksdisch wants to merge 4 commits into
mainfrom
fix/ci-runs-tests

Conversation

@ksdisch

@ksdisch ksdisch commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Found by the portfolio's 2026-08-07 audit pass.

The badge was green over zero assertions

The workflow looped uv run "\$f" over test_*.py. None of the 12 test files has a __main__ entrypoint, so each one imported, exited 0, and ran no assertions — while the README carried a passing CI badge.

Now invokes pytest, per file so the per-suite log grouping survives. Same shape mute-map adopted when its own review caught the identical template bug.

The tripwire

A Guard the collected test count step asserts collected >= 256. A silent drop to zero — the exact shape of the original bug — now fails the build instead of passing it.

Verified locally: 256 collected, suite green.

🤖 Generated with Claude Code

https://claude.ai/code/session_015uobnj3QL2D9Acd4hDiGSm

ksdisch and others added 4 commits August 7, 2026 11:26
The workflow looped `uv run "$f"` over test_*.py. None of the 12 files has a
`__main__` entrypoint, so every one imported, exited 0, and executed zero
assertions — a green badge over nothing, until the portfolio's 2026-08-07 audit
caught it. Verified before the fix: running a suite as a script exits 0 silently
while pytest on the same file runs its tests.

Now invokes pytest per file (same shape mute-map adopted when its own review
caught this), plus a collected-count floor of 256 so a silent drop to zero
fails the build instead of passing it.

Verified: $(cd . && echo '256 collected, suite green').

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uobnj3QL2D9Acd4hDiGSm
Turning CI on for real surfaced one genuine incompatibility. test_sandbox.py is
a Docker integration suite by its own docstring ("they run real containers"),
not an offline one. GitHub's runner has a live daemon, so the suite's
`docker info` probe reports available — but it bind-mounts /work unreadable to
the container user and all 6 tests die with `[Errno 13] Permission denied`
before emitting a verdict.

Excluded by name and out loud in the workflow rather than by weakening the
probe until it skips silently: a suite that stops running should say so. It
still runs locally, where Docker works.

Verified: 250 passed offline; 256 still collected, so the floor is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uobnj3QL2D9Acd4hDiGSm
Replaces the previous commit's workaround, and fixes both should-fix findings
from round 1.

F1: excluding test_sandbox.py from CI dropped exactly the 6 tests that certify
the sandbox's security invariants — no network, per-test fresh process, wall
timeout kills runaways, expected outputs never enter the container — while
README, PROJECT and both papers went on saying "256 tests green" unqualified.
The badge would have certified 250 and quietly omitted the 6 that matter most.
The exclusion note also claimed the suite could be run locally, which the
review showed is false on any Linux host.

The root cause is one permission. The container runs as nobody (65534) against
a read-only bind mount, but tempfile.TemporaryDirectory() is 0700 and owned by
the host user, so on Linux — GitHub's runners included — /work is unreadable to
the container user and every run dies with Errno 13 before emitting a verdict.
macOS hides this by not mapping host ownership through the Docker mount, which
is why it was never seen locally. The throwaway tree is now chmod'd 0755/0644:
it lives for one container, is mounted read-only, and holds only the generated
program and its test inputs. The container still never sees expected outputs.

F2: FLOOR=256 was set when all 12 files ran, and the exclusion left it measuring
a set CI did not execute — so growth in test_sandbox.py could silently offset
deletions elsewhere. With the exclusion gone the floor matches what runs again.

The guard is also rebuilt on the same finding dim-stage's review raised: a
separate --collect-only process cannot tell "pytest ran" from "pytest was never
invoked". It now reads the JUnit report the run step itself produced, so a
missing report, zero executed tests, or a total under the floor each go red.
That also retires F3's bash -e dead branch and F4's stale header comment.

CI is the verification here: the sandbox suite failed there, so it has to pass
there. Locally 250 pass and 6 skip — Docker's daemon is not running on this box.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uobnj3QL2D9Acd4hDiGSm
hush-gauge's review found the deeper version of this defect: a suite gated on a
gitignored artifact skips wholesale in a clean checkout while collection still
counts it, so an advertised "1002 tests" is 835 for everyone who isn't the
author. A collected-only floor is structurally blind to that.

The guard now carries two floors. `collected` guards against tests
disappearing; `executed` guards against them silently going dark. Applied here
for consistency across the portfolio's repos, and it is not hypothetical in
dim-stage: 88 collected, 86 executed in a clean clone, because two tests
legitimately need the reference oracle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uobnj3QL2D9Acd4hDiGSm
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