Skip to content

docs(claude): a pytest count is a property of a run, never of a suite (phaze-ljfi5) - #540

Merged
SimplicityGuy merged 1 commit into
mainfrom
wt/bead/issue/phaze-ljfi5
Aug 25, 2026
Merged

docs(claude): a pytest count is a property of a run, never of a suite (phaze-ljfi5)#540
SimplicityGuy merged 1 commit into
mainfrom
wt/bead/issue/phaze-ljfi5

Conversation

@SimplicityGuy

Copy link
Copy Markdown
Owner

What

Adds a short note to the end of CLAUDE.md's "A gate is green only if its own pytest summary line says so" section: the summary line is the standard of green, but the count in it is not a portable quantity. It describes the population that ran, not the suite.

This qualifies the pytest-summary-line rule; it does not weaken it. Every other paragraph in that section is about a status that lies. This is the adjacent case where nothing lies and a reader is still misled, and the note opens by saying so, so it cannot be read as licence to trust a gate less.

Two mechanisms, split by whether the line admits to it

  • The marker filter, which the line DOES declare. Measured on this tree 2026-08-25: uv run pytest --collect-only -q -m browser tests/browser collects exactly 180, and grep -rn -- "--deselect" justfile pyproject.toml .github/ scripts/ returns nothing. So the 180 deselected in every full-suite line in this file is the -m 'not browser' marker filter, all of it β€” the bead's first two "sources of variation" are one mechanism, and the note says so rather than implying a --deselect set that does not exist.
  • Which branch of the recipe ran, which the line does NOT declare. Where the gate selects, it hands pytest explicit node ids, so unselected tests are never collected and leave no deselected and no trace at all. 417 passed / 35.57s selected against ~8,000 / ~20 min for the whole suite β€” a factor of 19, both green, both printing a summary line. The variation that dwarfs the other is the invisible one.

Deliberately does not enumerate the gate's verdicts

An earlier draft said "RUN and ESCALATE are two populations". phaze-fqfds landed a third (a docs-only diff runs the prose floor), which would have dated that sentence within the day. The note makes the quantifier-free claim instead β€” one command, several populations, and the variation is the hazard rather than any particular count of branches β€” and points a reader who wants the enumeration at scripts/select_impacted_tests.py, which is the thing that decides. Enumerating them in CLAUDE.md is a separate docs bead's territory, and a fixed enumeration is the trap phaze-g9cus had just finished removing from this same file three times over.

Composes with phaze-g9cus rather than around it

g9cus landed mid-flight and took two things off the table: it forbade re-enumerating the ledger's field list anywhere but the ledger section, and it established the report-vs-rc-only ratio with its mechanism and a warning not to read it forward. An earlier draft did both. Neither is restated. The ledger paragraph now states only what survives a report being present: no field records which branch of the recipe ran, the key is (tree hash, validate-cmd hash), and every phaze boundary hashes the same command β€” so a replayed count is a number with no population attached.

Figures

The bead cited 8027 for the escalated side; the ledger's report entries read 8038 of 8041. Both are real and both are already in this file β€” g9cus's 8027 was a green full suite on a pre-opt-in tree, the 8038 is the report beside it. Rather than pick one, the note uses the pair: two green full-suite runs eleven apart, because the suite grew between them. That is a third axis of variation and it is why "the suite passes" does not survive being carried from one run to the next even with the recipe held fixed.

Validation

bh work check phaze-ljfi5, seat counts (phaze_counts_cdd93696_test, Redis DB 21):

uv run ruff check .          All checks passed!
uv run mypy .                Success: no issues found in 326 source files
🎯 selector: docs 11 prose-guard module(s) for 1 documentation path(s): CLAUDE.md
phaze test database: 'phaze_counts_cdd93696_test' on localhost:5433 (from TEST_DATABASE_URL, exclusive)
======================= 193 passed, 1 warning in 11.51s ========================
⏱️  just test-fast: 11 prose-guard module(s) in 28s. NOT the full suite.
GATE_EXIT=0

bh work submit replayed that verdict (validation verdict reused (sha 0a43a8d, tree 8adf24d, recorded 2026-08-25T13:55:36-07:00)) and ran no tests of its own β€” the check run above is the measurement.

The gate demonstrated the note's own claim. It produced a third green count β€” 193, not 417, not ~8,000 β€” and the JUnit hook recorded {"tests": 193, "passed": 193, ...} onto the ledger entry that submit then replayed. The durable record for this bead's submit says "193 passed", under the same key shape as an 8038-passed full-suite verdict, with nothing in it naming which of the two you are looking at.

Notes for the reviewer

  • The docs-only gate path emits no coverage line, so the three-state GATE_EXIT table's "green" row is unsatisfiable on this path. Left alone deliberately β€” a separate docs bead owns the fast gate's un-enumerated outcomes.
  • Docs-only change: CLAUDE.md, +56 lines, no code touched.

πŸ€– Generated with Claude Code

https://claude.ai/code/session_01GazEx6kCcyQuRvifWb3aBF

… (phaze-ljfi5)

This repo treats the pytest summary line as the standard of green, correctly.
Nothing said that the COUNT in it is not a stable quantity. It describes the
population that RAN, not the suite, so two green lines are not comparable and
neither carries what you would need to compare them.

QUALIFIES the pytest-summary-line rule, does not weaken it. Every other
paragraph in that section is about a status that LIES; this is the adjacent
case where nothing lies and a reader is still misled, and the note says so
explicitly so it cannot be read as licence to trust a gate less.

=== TWO MECHANISMS, SPLIT BY WHETHER THE LINE ADMITS TO IT ===

The bead named three sources of variation -- marker filter, --deselect set,
selected-vs-escalated recipe. Measured on this tree 2026-08-25, the first two
are one mechanism: `grep -rn -- "--deselect" justfile pyproject.toml .github/
scripts/` returns nothing, and `uv run pytest --collect-only -q -m browser
tests/browser` collects exactly 180 -- so the `180 deselected` in every
full-suite line in this file is the `-m 'not browser'` marker filter, all of
it. All three are still named; the note states the measured relationship
rather than implying a --deselect set that does not exist, and says a future
one lands in the same visible counter.

That regrouping is what makes the point sharper than the bead had it: the
marker filter is DECLARED by the line, and the recipe is NOT. Where the gate
selects, it hands pytest explicit node ids, so unselected tests are never
collected and leave no `deselected`, no trace at all. The variation that dwarfs
the other by a factor of 19 is the invisible one.

=== DELIBERATELY DOES NOT ENUMERATE THE GATE'S VERDICTS ===

An earlier draft said "RUN and ESCALATE are two populations". phaze-fqfds
landed a third (a docs-only diff runs the prose floor), which would have dated
that sentence within the day. The note now makes the quantifier-free claim --
one command, several populations, and the VARIATION is the hazard rather than
any particular count of branches -- and points a reader wanting the
enumeration at scripts/select_impacted_tests.py, which is the thing that
decides. Enumerating them here is a separate docs bead's territory, and a
fixed enumeration is the trap phaze-g9cus had just finished removing from this
same file three times over.

=== COMPOSES WITH phaze-g9cus RATHER THAN AROUND IT ===

g9cus landed while this bead was in flight and took two things off the table:
it forbade re-enumerating the ledger's field list anywhere but the ledger
section, and it established the report-vs-rc-only ratio with its mechanism and
a warning not to read it forward. An earlier draft did both. Neither is
restated now. The ledger paragraph instead states only what SURVIVES a report
being present: no field records which branch of the recipe ran, the key is
(tree hash, validate-cmd hash), and every phaze boundary hashes the same
command -- so a replayed count is a number with no population attached.

=== FIGURES ===

Selected: 417 passed, 1 skipped in 35.57s (dev/fastsuite, 2026-08-25, already
the check-fast row of the recipes table).

The bead and the dispatch both cited 8027 for the escalated side; the ledger's
report entries read 8038 of 8041. Both are real and both are in this file --
g9cus's 8027 was a green full suite on a pre-opt-in tree, the 8038 is the
report beside it. Rather than pick one, the note uses the pair: two green
full-suite runs eleven apart, because the suite GREW between them. That is a
third axis of variation and it lands squarely in this bead's scope -- it is
why "the suite passes" does not survive being carried from one run to the next
even with the recipe held fixed. The headline comparison is given as ~8,000
and ~20 min, since neither exact figure is the argument; the factor of 19 is.

Cross-references phaze-qsyc0's "a gate's M is a property of a RUN, never of a
COMMAND" as the same idea one level down, rather than restating it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GazEx6kCcyQuRvifWb3aBF
@SimplicityGuy
SimplicityGuy merged commit 53229c3 into main Aug 25, 2026
11 checks passed
@SimplicityGuy
SimplicityGuy deleted the wt/bead/issue/phaze-ljfi5 branch August 25, 2026 21:20
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