Skip to content

Stop excluding tests by band, let tier C report instead of gate, and quote $PYTEST - #731

Open
lmoresi wants to merge 2 commits into
developmentfrom
ci/undefer-bands-and-tier-c
Open

Stop excluding tests by band, let tier C report instead of gate, and quote $PYTEST#731
lmoresi wants to merge 2 commits into
developmentfrom
ci/undefer-bands-and-tier-c

Conversation

@lmoresi

@lmoresi lmoresi commented Sep 11, 2026

Copy link
Copy Markdown
Member

Item 4 of the 2026-09 audit. Depends on nothing, but reads best after #729,
which defines what tier C means.

The bands were never a cost decision

test_06* was disabled as "potentially problematic" and stayed that way.
test_106*/test_107* were held back pending "a triage/deselect decision" that
nobody made. Measured 2026-09-12, those three bands hold 243 tests, of which
only 26 are level_3 or tier_c
. 55 are level_1 and tier_a — hardened, fast,
and exactly what CI exists to run.

One slow test (test_1064) was quarantining its neighbours because they shared
a number. They run now; the only failure among them was a test-side defect,
fixed separately in #728.

It is affordable today

The comment justifying the 120-minute cap measured the serial phase at ~55 min
on 2026-08-15, before xdist. The five successful runs before 2026-09-12 spent
34, 35, 43, 44 and 48 minutes in "Run tests". The deferred bands add ~22 min
single-process locally, considerably less under -n 4.

Sharding across parallel jobs stays the durable fix if this tightens again. It
was referenced in the workflow as "the CI-runtime issue" but never actually
filed — now #730, with these numbers.

Tier C reports rather than gates

A tier C failure demands an explanation, not a revert: these are
characterisations that can fail because the code got better. They are
excluded from the batches that set $status and run in their own pass at the
end, which is read but cannot fail the build.

One trap worth flagging in review. The exclusion goes through
PYTEST_ADDOPTS, not appended to $PYTEST. $PYTEST is used unquoted at
every call site, so bash splits -m 'not tier_c' into the words 'not and
tier_c' — and pytest then silently collects nothing and exits 0, reporting
a green run that tested nothing. Verified:

$ pytest -m "'not" "level_2'" tests/test_1070_free_surface_plume.py
no tests collected in 0.00s          # exit 0

pytest shlex-splits PYTEST_ADDOPTS, so the quoting survives there. The
reporting pass clears it with env -u, or it would select nothing itself.

Also drops a line that now runs twice

test_1072 was pulled forward out of test_107* by name because that band was
not batched. The band runs as a whole now, so the named line is redundant — it
is still covered, once.

Verified

Dry run with a stub pytest: every gating batch carries the exclusion, all the
previously-deferred bands appear, test_1072 appears exactly once, and the tier
C pass runs with PYTEST_ADDOPTS unset.

Underworld development team with AI support from Claude Code

Copilot AI lite review requested due to automatic review settings September 11, 2026 18:20
@lmoresi
lmoresi force-pushed the ci/undefer-bands-and-tier-c branch from 1c6e6bd to 33c89de Compare September 11, 2026 18:23
@lmoresi lmoresi changed the title Stop excluding tests by band, and let tier C report instead of gate Stop excluding tests by band, let tier C report instead of gate, and quote $PYTEST Sep 11, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical test failures and unresolved Tier C, dependency, and option-handling issues remain.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Broadens CI coverage by re-enabling deferred test bands and reporting Tier C tests without gating.

Changes:

  • Re-enables test_06*, test_106*, and test_107*.
  • Adds a non-gating Tier C reporting pass.
  • Updates CI runtime rationale and removes redundant execution.
File summaries
File Summary
scripts/test.sh Adjusts batching and Tier C handling. Review findings include two critical gating/dependency issues and three moderate issues involving collection scope, marker documentation, and PYTEST_ADDOPTS preservation.
.github/workflows/build_uw3_and_test.yaml Updates CI runtime rationale and sharding documentation.
Review details

Suppressed comments (1)

scripts/test.sh:269

  • This command does not limit collection to Tier C: pytest collects/imports every test module under tests/ before applying -m tier_c. That reintroduces the monolithic run this script explicitly avoids and can trigger the import-time solves/side effects documented in tests/conftest.py (including a 20+ minute collection), even when only a few Tier C tests execute. Run an explicit, collection-safe Tier C file set instead of passing the whole tree.
# answered with an explanation or a re-characterisation, never with a revert.
  • Files reviewed: 2/2 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/test.sh Outdated
# whole band was measured 2026-09-12 and passes; a band is not a unit of trust,
# and holding one back for a suspicion nobody recorded meant its level_1 files
# never ran at all.
$PYTEST tests/test_05*py tests/test_06*py tests/test_07*py || status=1
Comment thread scripts/test.sh Outdated
# the deferred bands only 26 are level_3 or tier_c; 55 are level_1 AND tier_a —
# hardened, fast, and exactly what CI exists to run. test_1064 is the slow one
# and it is excluded by WHAT IT IS (tier_c) below, not by its number.
$PYTEST tests/test_106*py tests/test_107*py || status=1
Comment thread scripts/test.sh Outdated
# unquoted at every call site, so bash would split "-m 'not tier_c'" into the
# words 'not and tier_c' and pytest would see a broken expression. pytest
# shlex-splits PYTEST_ADDOPTS, so the quoting survives.
export PYTEST_ADDOPTS="-m 'not tier_c'"
Comment thread scripts/test.sh Outdated
echo "Tier C characterisations (reported, not gating)"
echo "=========================================="
# Clear the exclusion above, or this pass would select nothing.
if env -u PYTEST_ADDOPTS pytest --config-file=tests/pytest.ini -m tier_c tests/; then
…quote $PYTEST

**The bands were never a cost decision.** `test_06*` was disabled as "potentially
problematic" and stayed that way; `test_106*`/`test_107*` were held back pending
"a triage/deselect decision" that nobody made. Measured 2026-09-12: those three
bands hold 243 tests, of which only 26 are level_3 or tier_c. 55 are level_1 AND
tier_a — hardened, fast, and exactly what CI exists to run. One slow test
(test_1064) was quarantining its neighbours because they shared a number.

They run now. The only failure among them was a test-side defect, fixed
separately in #728.

**This is affordable today.** The comment justifying the 120-minute cap measured
the serial phase at ~55 min on 2026-08-15, before xdist. The five successful runs
before 2026-09-12 spent 34, 35, 43, 44 and 48 minutes in "Run tests". Sharding
across parallel jobs stays the durable fix if this tightens again — referenced in
the workflow as "the CI-runtime issue" but never filed, now #730.

**Tier C reports rather than gates.** A tier C failure demands an EXPLANATION,
not a revert (Charter §8, TESTING-RELIABILITY-SYSTEM.md): these are
characterisations that can fail because the code got BETTER. They are excluded
from the batches that set $status and run in their own pass at the end, which is
read but cannot fail the build.

**$PYTEST is now an array.** It was a string expanded UNQUOTED at all 17 call
sites, which made adding any argument containing a space unsafe: bash splits
`-m 'not tier_c'` into the words `'not` and `tier_c'`, and pytest answers that by
silently collecting NOTHING and exiting 0 — a green run that tested nothing:

    $ pytest -m "'not" "level_2'" tests/test_1070_free_surface_plume.py
    no tests collected in 0.00s          # exit 0

The first version of this change dodged it through PYTEST_ADDOPTS, which pytest
shlex-splits. Quoting the variable removes the hazard instead of routing around
it, so the next argument someone adds is safe too. Verified by dry run that the
marker arrives as a single argument, `ARG[not tier_c]`, in both the xdist and
in-process branches.

**Also drops a line that now runs twice.** `test_1072` was pulled forward out of
test_107* by name because that band was not batched. The band runs as a whole
now, so the named line is redundant; it is still covered, once.

Verified by dry run with a stub pytest: every gating batch carries the exclusion
as one argument, the previously-deferred bands appear, test_1072 appears once,
and the tier C pass runs without the exclusion.

Underworld development team with AI support from Claude Code
…re DEFERRED

Rebased onto #723 so the two changes land consistently, which immediately
surfaced an integration defect the guard itself caught: `globs_run_by` matched
the literal `$PYTEST`, and this PR rewrites every call site to the array form
`"${PYTEST[@]}"`. The guard therefore found NO globs and declared all 302 test
files dark. It now matches both spellings.

**DEFERRED is empty, and that is the point.** Every band that sat there — the
test_06NN regression suite and test_106*/test_107* — is batched now. Nothing is
excluded from CI by its number any more; a test that should not gate says so
with `@pytest.mark.tier_c`, which is a property of the test rather than of where
it falls in the numbering.

**Dependencies, which the PR did not declare.** Copilot was right that this
cannot land alone:

- #728 fixes `test_1060_nitsche_freeslip`, whose bound is still `<1e-4` against
  an observed 1.0081e-4. Enabling `test_106*` without it makes this batch fail.
- #729 puts the `tier_c` mark on the `test_1070` comparison and updates the tier
  definitions in `tests/pytest.ini` and the docs. Without it this PR enables
  `test_107*` while that comparison still gates, and the reporting pass
  contradicts a marker contract that still reads "development only, not for
  automation".

Merge order is #728, #729, #723, then this.

The PYTEST_ADDOPTS point in the same review was already addressed: the array
landed in an amended commit the review had not seen. It also removes the
objection behind it — an inherited PYTEST_ADDOPTS from the caller is no longer
discarded, because the script no longer sets it.

Verified: dry run shows the marker arriving as a single `ARG[not tier_c]`, the
tier C pass running without it, and the guard passing with 302 reachable and 0
deferred.

Underworld development team with AI support from Claude Code
@lmoresi
lmoresi force-pushed the ci/undefer-bands-and-tier-c branch from 33c89de to 5e613ce Compare September 11, 2026 18:49
@lmoresi
lmoresi changed the base branch from development to docs/audit-2026-09 September 11, 2026 18:50
@lmoresi

lmoresi commented Sep 11, 2026

Copy link
Copy Markdown
Member Author

Review points addressed.

Dependencies, which this PR did not declare — correct, and now stated. It
cannot land alone:

Merge order: #728#729#723 → this. Retargeted onto #723 to make the
last step explicit.

PYTEST_ADDOPTS — already fixed in an amended commit the review had not seen.
$PYTEST is now a bash array, which also removes the objection behind that
comment: an inherited PYTEST_ADDOPTS is no longer discarded, because the
script no longer sets it.

One thing the rebase found. Landing on #723 surfaced an integration defect
that #723's own guard caught: globs_run_by matched the literal $PYTEST, and
this PR rewrites every call site to "${PYTEST[@]}". The guard saw no globs at
all and declared all 302 test files dark. It now matches both spellings — a
useful demonstration that the guard does its job.

DEFERRED is now empty. Every band that sat there is batched; nothing is
excluded from CI by its number any more. A test that should not gate says so
with @pytest.mark.tier_c, which is a property of the test rather than of where
it falls in the numbering.

Verified: the marker arrives as a single ARG[not tier_c], the tier C pass runs
without it, and the guard reports 302 reachable, 0 deferred.

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.

2 participants