Skip to content

The accessibility gate was shallowing this checkout, and five tests about tags paid for it - #74

Merged
ChelseaKR merged 1 commit into
mainfrom
fix/browser-gate-shallow-fetch
Sep 7, 2026
Merged

ChelseaKR merged 1 commit into
mainfrom
fix/browser-gate-shallow-fetch

Conversation

@ChelseaKR

Copy link
Copy Markdown
Owner

What was wrong

Every open pull request in this repository was red on the same five tests, none of which
had anything to do with the changes under review:

FAILED tests/test_release_claims.py::test_this_checkout_can_be_trusted_to_know_the_repositorys_tags
FAILED tests/test_release_claims.py::test_the_declared_version_is_tagged_or_the_readme_says_it_is_not
FAILED tests/test_release_claims.py::test_citation_declares_a_release_date_only_for_a_version_that_was_tagged
FAILED tests/test_release_claims.py::test_every_dated_changelog_section_names_a_version_that_was_tagged
FAILED tests/test_release_claims.py::test_the_changelog_says_the_current_work_is_unreleased_while_it_is
AssertionError: the tag list here cannot be trusted: this is a shallow clone.
  actions/checkout fetches no tags at the default depth ... Check out with fetch-depth: 0

The message names a cause it did not measure. fetch-depth: 0 is set on the verify
job, the logged fetch carries no --depth, and the same refspec reproduced locally on
git 2.55.0 produces a clone that is not shallow. Something else was shallowing the
checkout after actions/checkout finished with it.

It is tools/a11y_browser. On a pull_request run Playwright's git-info plugin calls
gitDiff, and gitDiff opens with:

// node_modules/playwright/lib/runner/index.js
await runGit(["fetch", "origin", ci.prBaseHash, "--depth=1", "--no-auto-maintenance",
              "--no-auto-gc", "--no-tags", "--no-recurse-submodules"], gitDir);

The harness's working directory is inside this work tree, so gitDir resolves to the
repository root and --depth=1 writes .git/shallow there, holding the pull request's
base SHA. tests/test_release_claims.py then refuses to read the tag list — which is the
correct refusal, and the reason that file exists: a shallow checkout cannot tell an
untagged repository from an unfetched one, and this project has no tags, so "none found"
read as "none exist" would be a vacuous pass.

It read as intermittent, and it was a race. Whether it fired depended on whether
pytest -n auto happened to schedule tests/test_a11y_browser_gate.py before the
release-claims tests. Pushes to main were unaffected — with no PR base SHA there is
nothing to fetch — so main stayed green while pull requests failed at random. That is
the worst shape for a CI defect: it looks like flake, and the branch it lands on looks
guilty.

Measured

On a runner, on a pull_request event, via a throwaway diagnostic branch (deleted):

Point in the job git rev-parse --is-shallow-repository
after actions/checkout (fetch-depth: 0, no --depth in its fetch) false, no .git/shallow
after make lock-check sync node-sync false, no .git/shallow
after make browser-sync false, no .git/shallow
pytest tests/test_release_claims.py alone 18 passed
after the full suite under -n auto true, .git/shallow = d4f533f, the base SHA

Then bisected by running each subprocess-invoking module on its own:

=== RUN: tests/test_a11y_browser_gate.py ===
SHALLOW CREATED BY: tests/test_a11y_browser_gate.py
--- FETCH_HEAD --- d4f533f75ea27b7c9ce0fec7a7bf2c5e86c099ee
--- shallow    --- d4f533f75ea27b7c9ce0fec7a7bf2c5e86c099ee
=== RUN: tests/test_a11y_gate.py ===          clean
=== RUN: tests/test_determinism_gate.py ===   clean
=== RUN: everything else ===                  clean

What changed

  • tools/a11y_browser/playwright.config.ts declares
    captureGitInfo: { commit: false, diff: false }. Playwright reads an undeclared half
    as "capture when this looks like CI", so leaving it unset is not the same as off. This
    harness reads static pages off disk as file:// URLs; it has no reason to read git at
    all, and none to write to it.
  • tests/test_a11y_browser_gate.py hands the harness an environment with
    GITHUB_ACTIONS, GITHUB_EVENT_PATH, GITLAB_CI and JENKINS_URL removed. The
    existing CI: "" did nothing, because ciInfo() keys off those names rather than off
    CI — that is exactly why the first obvious fix in the file had already failed to
    prevent this.
  • tests/test_browser_gate_touches_no_git.py is new and holds both halves.
  • CHANGELOG.md under [Unreleased].

Both fixes are kept, deliberately: the config is the durable one (it holds for npm test
and for a maintainer running the harness by hand), the environment scrub is the one a
Playwright default cannot silently undo at the next version bump.

How it was verified

827 passed locally (-n 2; the two browser specs skip without the Playwright browser
binary, and CI runs them). ruff check and ruff format --check clean.

Each control was run against the fault it exists to catch, with the sabotage asserted
present in the file
before the result was read:

Sabotage Asserted in file Result
captureGitInfo: { commit: false, diff: true } grep -n captureGitInfo showed diff: true …captures_no_git_info[diff] FAILED
the whole captureGitInfo line deleted grep -c captureGitInfo0 [commit] and [diff] both FAILED
harness_env returns dict(os.environ) grep -n showed the replacement line all four …not_told_it_is_running_in_ci FAILED

Restored from byte copies (SHA-256 compared against the pre-sabotage copies), 7 passed.

The environment control sets each variable before checking it is gone. Asserting that
GITHUB_ACTIONS is absent from an environment that never had it is an assertion that
cannot fail, and this repository has an ADR about that.

What this does not do

It does not change tests/test_release_claims.py. That test was right and its refusal was
right; the only thing wrong with it is that its message names actions/checkout as the
cause when the checkout was innocent. Rewording it would be a separate, smaller change and
is left out so this one stays reviewable.

Two open pull requests, #70 and #71, were red on this alone.

Prepared with AI assistance; reviewed before submission.

…bout tags paid for it

On a pull_request run, Playwright's git-info plugin calls gitDiff, which begins with
`git fetch origin <pr base sha> --depth=1` (node_modules/playwright/lib/runner/index.js).
tools/a11y_browser runs with its working directory inside this work tree, so --depth=1
wrote .git/shallow at the repository root naming main's own tip. tests/test_release_claims.py
then refused to read the tag list, which is the right refusal, and `make verify` failed on
branches whose diffs had nothing to do with tags, releases or accessibility.

It read as intermittent because it was a race: whether it fired depended on whether
`pytest -n auto` scheduled tests/test_a11y_browser_gate.py before the release-claims tests.

Measured on a runner on 2026-09-06, on a pull_request event, with fetch-depth: 0 and no
--depth anywhere in the checkout's own fetch: .git/shallow absent after checkout, absent
after `make browser-sync`, present the moment that one test module ran, holding d4f533f,
the base SHA. Bisected by running each subprocess-invoking test module alone.

playwright.config.ts now declares captureGitInfo with both halves false; Playwright reads
an undeclared half as "capture when this looks like CI", so unset is not off. harness_env
also strips GITHUB_ACTIONS, GITHUB_EVENT_PATH, GITLAB_CI and JENKINS_URL, because the
plugin keys off those rather than off CI, which is why setting CI to "" did nothing.
tests/test_browser_gate_touches_no_git.py holds both halves, and sets each variable before
checking it is gone so it is not asserting an absence that was never there.
@ChelseaKR
ChelseaKR merged commit 5b34dd8 into main Sep 7, 2026
6 checks passed
ChelseaKR added a commit that referenced this pull request Sep 7, 2026
… the checkout

The previous run failed on tests/test_release_claims.py because
tools/a11y_browser wrote .git/shallow into the work tree. That is fixed on main
(PR #74). A re-run replays the merge commit recorded when the run was created,
which predates the fix, so this asks for a fresh one.
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