compass(tests): pin the backend and runner module walks to every module, not to one (#232) - #395
Conversation
…le, not to one (#232) `test_the_package_was_found` asks whether the walk over each package found anything. A walk narrowed to `rglob("__init__.py")`, sliced to `[:1]`, or made non-recursive still finds something, so each of those passed the suite with cases silently gone. In `test_backend_interface.py` and `test_runner_non_allocating.py`: - `test_the_walk_returns_every_module_under_the_root` runs the walk over a tree the test builds (`__init__.py`, `a.py`, `sub/b.py`) and asserts that exact set. The expected set is the fixture's, so it does not change when the package gains a module. - `test_every_module_the_walk_returns_is_a_case` compares the parametrisation's values to the walk, so a slice where the walk is handed to `parametrize` is refused too. - The runner's engine-import exemption is keyed by path under the package, not by basename, so a `sub/model_runner.py` is not exempt. Case ids are paths under the package, which for today's flat package are the same ids. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
|
||
| The tree is built here, so the expected set does not move when the package | ||
| gains a module. A walk that is not recursive misses `sub/b.py`, and one | ||
| narrowed to a pattern or sliced to a prefix misses `a.py` or more. |
There was a problem hiding this comment.
Non-blocking. This docstring sentence is false as written. The pin itself holds. Principle 8: "Every claim carries its measurement."
The sentence says a walk "narrowed to a pattern or sliced to a prefix misses a.py or more". I measured two narrowings on the merged tree (tip f89b1492c + head 7f45c3be8), with -k test_the_walk_returns_every_module_under_the_root. Neither of them misses a.py:
mutation of _backend_modules |
what the walk returns over the fixture | result |
|---|---|---|
sorted(PACKAGE.rglob("*.py"))[:2] |
{'__init__.py', 'a.py'}: only sub/b.py is missing |
1 failed |
sorted(PACKAGE.rglob("[!_]*.py")) |
{'a.py', 'sub/b.py'}: only __init__.py is missing |
1 failed |
The whole tests/compass suite gives the same verdict on the second one:
- tip: 1318 passed, rc 0, one case gone;
- merged: 1 failed,
test_backend_interface.py::test_the_walk_returns_every_module_under_the_root.
So the set equality catches both, and only the prose is wrong.
Suggested wording: "…and one narrowed to a pattern or sliced to a prefix misses at least one of the three." The identical sentence is in test_runner_non_allocating.py:475.
|
|
||
| The tree is built here, so the expected set does not move when the package | ||
| gains a module. A walk that is not recursive misses `sub/b.py`, and one | ||
| narrowed to a pattern or sliced to a prefix misses `a.py` or more. |
There was a problem hiding this comment.
Non-blocking. This is the same false sentence as test_backend_interface.py:124. Principle 8.
A [:2] slice drops only sub/b.py, and a [!_]*.py pattern drops only __init__.py. Neither misses a.py. The set equality refuses both, so only the prose needs the fix, for example "misses at least one of the three". The measurements are on the backend comment.
|
This review is agent-authored. It is review cycle 1 of PR #395 (part of #232), at head Verdict: APPROVE There are two non-blocking findings. Both are prose, and neither is in the code or the pins:
If they are not fixed on this branch, they need an issue before landing, per the rule "A finding not fixed in the PR that found it gets an issue". I read the eight design principles and What I checkedEvery changed line: 54 added and 2 removed, in two test files, with no production line.
Is the built-tree pin run against the real walk, or against a copy? It runs the real walk. Plants: whole
|
| id | mutation | tip f89b1492c |
merged |
|---|---|---|---|
| P1 | plant backends/sub/leaky.py (import atom.model_engine.model_runner), and rglob→glob in _backend_modules |
1319 passed, rc 0: the breach is invisible | 1 failed: test_backend_interface.py::test_the_walk_returns_every_module_under_the_root, {'__init__.py', 'a.py'} == {…, 'sub/b.py'} |
| P2 | _runner_modules()[:1], at the parametrize |
1316 passed, rc 0: 3 cases gone | 1 failed: test_runner_non_allocating.py::test_every_module_the_walk_returns_is_a_case |
| P3 (mine) | rglob("[!_]*.py") in _backend_modules, which skips __init__ |
1318 passed, rc 0: 1 case gone | 1 failed: test_backend_interface.py::test_the_walk_returns_every_module_under_the_root, {'a.py', 'sub/b.py'} == … |
| P4 (mine, re-inline the consumer) | sorted(PACKAGE.glob("*.py")), inlined at the runner's parametrize, plus the plant runner/sub/leaky.py, plus a sub bullet in runner/__init__.py's docstring |
1319 passed, rc 0: the engine guard is blind | 1 failed: test_runner_non_allocating.py::test_every_module_the_walk_returns_is_a_case |
| P4b | the P4 inline, with no plant | — | 1323 passed, rc 0 (see reservation R2) |
| P5 | plant runner/sub/model_runner.py importing the engine, plus the sub bullet |
1320 passed, rc 0: exempt by basename | 1 failed: test_only_the_binding_module_reaches_the_engine[sub/model_runner.py], {'atom.model_…model_runner'} == set() |
| P5r (revert the fix) | P5, with the base's key path.name == "model_runner.py" put back on the merged tree |
— | 1324 passed, rc 0: the breach is invisible again |
| P6 (mine) | walk bounded at depth one, [*PACKAGE.glob("*.py"), *PACKAGE.glob("*/*.py")], plus the plant backends/sub/deeper/leaky.py |
— | 1323 passed, rc 0 (see reservation R1) |
The results:
- Every red is in the guard named for its row.
- None is a line-drift guard, since every mutation preserves line count.
- No pin is inert. P5r is the witness that the path key is the load-bearing line of the exemption change.
Ruling on the exemption change: in scope, and correct
In scope. #232 item 2 names the recursive walk as an unmeasured claim that fails open (principle 6: "Refuse rather than fall back."). A basename exemption under a recursive walk is the same failure one directory in: a model_runner.py at any depth was exempt, so P5 is green at the tip.
The developer disclosed this in the phase-1 inventory before editing. It is inside the brief's file set and inside the 56-line estimate. It touches tests only and changes no production behaviour. It only makes the guard refuse more.
Correct. The evidence:
- P5 and P5r show that it bites and which line bites.
- The ids stay unchanged for today's flat package. The gate junit carries
[__init__.py],[model_runner.py],[overrides.py]and[step_output.py], as before. path == PACKAGE / "model_runner.py"compares two paths built from the same resolvedPACKAGE, so the comparison is sound.
Every comment and docstring sentence, measured
test_the_walk_returns_every_module_under_the_root, "Non-empty says the walk found something; this says it found everything": this holds over the fixture (P1, P3).- The same docstring, "A walk that is not recursive misses
sub/b.py": this holds (P1). - The same docstring, "one narrowed to a pattern or sliced to a prefix misses
a.pyor more": this is false (N1, inline).[:2]misses onlysub/b.py, and[!_]*.pymisses only__init__.py. The pin catches both. test_every_module_the_walk_returns_is_a_case, "A slice or filter where the walk is handed to the parametrisation drops cases while the walk itself still returns every module": this holds (P2, P4).- The unchanged file docstring and comment, "rglob, so a module added under the package is covered the day it lands": this now holds to one level of nesting. Deeper nesting is R1.
N2 (non-blocking, no line). The commit message says "A walk narrowed to rglob("__init__.py"), sliced to [:1], or made non-recursive still finds something, so each of those passed the suite with cases silently gone". The PR body's first paragraph says the same. The "non-recursive" part is false on today's flat packages, where no case disappears:
- the developer's own
b_globandr_globrows at the tip give 1312 passed, equal to the null; - P1 at the tip gives 1319 passed, equal to the null, and tests=1325, also equal to the null.
What a non-recursive walk hides is a breach one directory down. Principle 8. The squash message is written at landing (-F commit_message=@file), so the lander can correct it there without a new commit on the branch.
Accepted with reservation, and what the next task in this area should watch
- R1. The fixture holds exactly one level of nesting. P6, a walk bounded at depth one with a breach at
sub/deeper/, is green. No realistic spelling produces that walk, and the named defect,rglob→glob, is held, so I ask for no change (principle 3: "Add only what is necessary, and nothing more"). - R2.
test_every_module_the_walk_returns_is_a_casecompares against today's real tree. A divergent consumer is therefore green while the package is flat (P4b), and red by name as soon as a nested module exists (P4). That is the reach it needs: any site divergence that drops a real case is refused. (mark,) = …pytestmarkfails closed with aValueErrorif a second mark is ever added.- The same two tests are the convention offered for compass(tests): the tests that inspect code structure fail open on other spellings #223's three held sites and
test_kv_driverless_import.py. R1 applies there too.
ponytail-review
- The only duplication is the same two tests in two files. A shared helper would be an abstraction with two call sites (
yagni), so I did not tag it. - The fixture's path tuple and its expected-set literal repeat three strings, but merging them saves 0 lines.
Lean already. Ship.
Gate 1: merged tree, gated once
- Tree:
git merge-tree --write-tree f89b1492c 7f45c3be8=6ed48c6e69e236d79052594ab45602e618f8ab7a(rc 0). It is not the head's own tree, so it was gated as a combined tree. - Stamp:
git commit-treegave28f83b271. - Script: the tree's own
scripts/compass/gate_cpu.sh, with the.compass-commitand.compass-changedstamps. - Run:
atom.__file__=/tmp/pr395r1/merged/ATOM/atom/__init__.py, undertimeout -k 10 2400, unpiped, with no othergate_cpu.shrunning.
| tree | header | passed / skipped / xfailed | failed | GATE_CPU_RC |
|---|---|---|---|---|
merged 6ed48c6e6 |
commit: 28f83b271 (stamp), 29 files excluded + tests/plugin, gpu: not required |
5279 / 155 / 3, 194 s | 0 | 0 PASSED |
That is the expected tip count of 5275 plus the head's 4. No timing-flake class failed, so no re-run was needed.
The tip moved again during this review, to 37fba4df0, six commits later (#391, #392, #393, #396, #397, #399). They touch atom/compass/runner/overrides.py, which is walked by the runner guard, and tests/compass/test_runner_rpc_surface.py.
git merge-tree --write-tree 37fba4df0 7f45c3be8=d2d1fd4a891db5ff2167d39f834da78c23ec63eb, clean (rc 0).- On that tree,
test_backend_interface.py,test_runner_non_allocating.pyandtest_runner_rpc_surface.pygive 114 passed, rc 0. - I did not full-gate
d2d1fd4a8. The landing tree check is the step that covers it.
In the Limits paragraph of whole_number's docstring (atom/compass/kv/handoff.py), the three-line union-case sentence becomes: "a whole number whose dtype text has "bool", such as a 0-d array of a numpy union dtype with a field "is_bool", is refused as not one." That is one line shorter. The condition is the same as the Refused paragraph's, and "as not one" matches the refusal message: "... which is not a whole number; ...". The whole docstring was probed on node 18 and every clause holds: - 0-d unions with an is_bool field (i8, u1, f4, O; holding 8, 1, 0, -3, 2**62) are refused, and only the dtype-text test refuses them; - the controls are accepted. The AST is identical with docstrings masked. Gate (node 18, CPU tier, combined with #395 on 3eb94cf): 5280 passed, 155 skipped, 3 xfailed, GATE_CPU_RC=0. Closes #403 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ile name (#407) SITES in test_runner_rpc_surface.py keyed broadcast sites by the bare file name engine_core.py. atom/diffusion/engine/engine_core.py has the same name. A waited broadcast padded into that file, at a line inside PrefillEngineCore's range, turned the #394 pin red for a call no RapidServe core makes. Site.file now holds the path relative to the repo root. Its consumers are updated: - the #394 pin's waited filter; - get_num_blocks; - the forward counter keys; - the docstring citation check, which strips atom/model_engine/, so a same-named file elsewhere keeps a "/" and cannot satisfy a citation. The three consumer files keep 94 identical ids, all passing. The citation fix also closes a second collision that was silent at the tip. An unwaited exit at a cited line of a same-named file passed the citation test there; it now fails. Changed lines: +11/-7, within the brief's 2x stop. Gate (node 18, CPU tier, combined with #395 and #408 on 3eb94cf): 5280 passed, 155 skipped, 3 xfailed, GATE_CPU_RC=0. Closes #401 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…usal comment (#402) The exit bullet in atom/compass/runner/__init__.py said that "the graphs and five KV tensors it deletes stay held". This runner allocates neither: - allocate_kv_cache allocates nothing; - capture_cudagraph captures nothing. The refusal comment in model_runner.py says the opposite, and that comment is the loss list a test holds against ModelRunner.exit's body. Nothing tested the bullet's copy of the list. The bullet now says exit (engine_core.py:260) never reaches ModelRunner.exit, and points at the comment on the RPC_SURFACE check for what that loses here. The rest of the docstring is unchanged, and test_runner_rpc_surface.py passes whole (69). The review found that an eagle3 speculative config builds and loads its drafter in ModelRunner.__init__, before forward refuses it. That means the comment's loss list omits a real deletion (self.drafter). Filed as #405, outside this file set. Gate (node 18, CPU tier, combined with #395, #408 and #407 on 3eb94cf): 5280 passed, 155 skipped, 3 xfailed, GATE_CPU_RC=0. Closes #398 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Part of #232
What this does
test_the_package_was_foundintest_backend_interface.pyandtest_runner_non_allocating.pyasks whether each package walk found anything. Suppose the walk is narrowed torglob("__init__.py"), sliced to[:1], or made non-recursive. It still finds something, so the wholetests/compasssuite stays green while cases silently disappear. This PR pins each walk to finding everything. In both files:test_the_walk_returns_every_module_under_the_rootruns the walk over a tree the test builds (__init__.py,a.py,sub/b.py) and asserts that exact set.test_every_module_the_walk_returns_is_a_casecompares the parametrisation's values (pytestmark[0].args[1]) to the walk. A slice where the walk is handed toparametrizeleaves the walk whole, so the first test cannot see it.path == PACKAGE / "model_runner.py", not bypath.name. Case ids are now paths under the package. For today's flat package these are the same ids, and the gate's node-id delta below confirms it.Scope: tests only, 0 production lines, 54 added / 2 removed across two files. The estimate in the phase-1 inventory was ~56; the halt line was 112.
Phase-1 inventory and proposal, posted before this PR: #232 (comment)
The pin, and why
The expected set, over a fixture tree, not over the real package.
__init__.py, whichrglob("__init__.py")or a[:1]slice misses.monkeypatchonPACKAGE), so there is no new one.Convention, stated once. A walk over a package is held by four checks:
__init__module (new);#266 proposes no convention for these walks: it records B7, B14 and B28 as unreachable and leaves them. So this does not compete with #266.
Named result, re-measured
Node 18,
xiaobizh_n18_cpu. The tipcae9607f0and the head7f45c3be8were each staged bygit archive+docker exec -iinto my own/tmp/i232gates/<side>/ATOM, and the md5 matched on both ends.atom.__file__was asserted under each staged root. Each mutation is line-count-preserving and the harness asserts a unique anchor. The wholetests/compasssuite ran per mutation undertimeout -k 10 1800, and failures were read from junit by node id.Null control: tip 1312 passed, head 1316 passed (+4, the four new tests), 6 skipped on both sides, rc=0.
All node ids below are
tests/compass/….1. Recursion is covered. Each plant is
sub/leaky.py, containingimport atom.model_engine.model_runner.cae9607f07f45c3be8backends/, walk untouchedtest_backend_interface.py::test_the_package_imports_nothing_from_the_engine[sub/leaky.py]rglob→globin_backend_modulestest_backend_interface.py::test_the_walk_returns_every_module_under_the_rootrunner/, walk untouchedtest_runner_non_allocating.py::test_only_the_binding_module_reaches_the_engine[leaky.py],test_runner_rpc_surface.py::test_the_package_docstring_lists_every_module_beside_it[sub/leaky.py]rglob→globin_runner_modulestest_runner_rpc_surface.py::test_the_package_docstring_lists_every_module_beside_it; the engine guard is blindtest_runner_non_allocating.py::test_the_walk_returns_every_module_under_the_root, plus the same docstring testsubalso listed as a bullet inrunner/__init__.py's docstringtest_runner_non_allocating.py::test_the_walk_returns_every_module_under_the_rootAt the tip, the runner's red under
globcomes from the docstring test, which fails on any new subdirectory whatever it imports. r_plant_glob_doc shows it: once the subpackage is documented, as anyone adding one legitimately would do, the engine breach is fully green. At the head it is red by the walk's own name.2. Narrowing is refused.
rglob("*.py")→rglob("__init__.py")test_backend_interface.py::test_the_walk_returns_every_module_under_the_rootsorted(...)[:1]inside_backend_modules_backend_modules()[:1],at theparametrizetest_backend_interface.py::test_every_module_the_walk_returns_is_a_caserglob→glob, no planttest_backend_interface.py::test_the_walk_returns_every_module_under_the_rootrglob("__init__.py")test_runner_non_allocating.py::test_the_walk_returns_every_module_under_the_root[:1]inside_runner_modules_runner_modules()[:1],at theparametrizetest_runner_non_allocating.py::test_every_module_the_walk_returns_is_a_caserglob→glob, no planttest_runner_non_allocating.py::test_the_walk_returns_every_module_under_the_root3. The basename exemption (runner). The plant is
runner/sub/model_runner.py, importing the engine, with the walk untouched.test_runner_non_allocating.py::test_only_the_binding_module_reaches_the_engine[sub/model_runner.py], plus the docstring testsubalso listed in the docstring)test_runner_non_allocating.py::test_only_the_binding_module_reaches_the_engine[sub/model_runner.py]Every head failure is in the guard named for that row. None comes from a line-drift guard.
Gate 1: ATOM's suite, unmodified, as a delta
Both runs were on node 18 in
xiaobizh_n18_cpu, one at a time:scripts/compass/gate_cpu.sh;.compass-commitand.compass-changedstamps were written from the samerev-parseas the archive;timeout -k 10 2400, unpiped, and written to a log file;--junitxml.atom.__file__GATE_CPU_RCcae9607f0commit: cae9607f0 (stamp),29 files excluded + tests/plugin,gpu: not required/tmp/i232gates/tip/ATOM/atom/__init__.py7f45c3be8commit: 7f45c3be8 (stamp), same exclusions,gpu: not required/tmp/i232gates/head/ATOM/atom/__init__.pyThe node-id delta runs from 5426 to 5430 ids.
tests/compass/test_backend_interface.py::test_the_walk_returns_every_module_under_the_root,…test_backend_interface.py::test_every_module_the_walk_returns_is_a_case,…test_runner_non_allocating.py::test_the_walk_returns_every_module_under_the_rootand…test_runner_non_allocating.py::test_every_module_the_walk_returns_is_a_case.p.nameto the path under the package, and no id changed.No
test_stream_marker_properties.pytiming class failed on either side, so no re-run was needed.The tip moved during the gate, to
f89b1492c(#376, #380, #383, #386, #388). Those commits touchatom/config.py,atom/compass/kv/handoff.py, three other tests and two documents, none of them in this diff.git merge-tree --write-tree f89b1492c 7f45c3be8gives6ed48c6e69e236d79052594ab45602e618f8ab7awith rc=0. I did not gate that merged tree; the tree check at landing covers it.Lint over the two files:
black 26.5.1 --target-version py312 --check,BLACK_RC=0;ruff 0.16.7 check,RUFF_RC=0.Left for #223, and why this says Part of
test_spec_schema.py:412,test_ir_data_model.py:721andtest_clock_lp_identity.py:440, carry the same non-empty-only guard. They are out of scope pending compass(tests): the tests that inspect code structure fail open on other spellings #223's ruling and the held compass(tests): AST guards refuse the spellings they cannot read #266, and they are untouched here. Each can take the same two tests, plus a moved-root control where one is missing.test_kv_driverless_import.py:130. It is outside this task's file set, so it is noted and not edited.test_runner_step_semantics.py::_reply_attribute_readswalksatom/model_enginewithglob(compass(tests): the tests that inspect code structure fail open on other spellings #223's B14). Its exact nine-attribute set already refuses narrowing, and the file is in compass(tests): AST guards refuse the spellings they cannot read #266's diff, so it is not touched.test_runner_rpc_surface.py) is not folded in, because this diff does not edit that file.What surprised me
globreddenstest_the_package_docstring_lists_every_module_beside_it, because that test counts a subdirectory as a module. Documenting the subpackage turns it fully green. So the neighbour holds "every subpackage is documented", and not "no module under the runner reaches the engine".rglob,path.name == "model_runner.py"exempts amodel_runner.pyat any depth. So the landed recursion widened what the exemption lets through, and nothing noticed.No design-doc references in code, test names, comments or emitted data.
🤖 Generated with Claude Code