Skip to content

compass: correct the runner docstring's device-memory claim and pin it - #188

Merged
jgong5 merged 4 commits into
feature/atomcompass_newfrom
compass/runner-docstring-truth
Sep 23, 2026
Merged

jgong5 merged 4 commits into
feature/atomcompass_newfrom
compass/runner-docstring-truth

Conversation

@jgong5

@jgong5 jgong5 commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Closes #186.

A docstring in landed code said something the code contradicts. This corrects it,
pins it so a successor cannot drift the same way, and reports an audit of every
other prose claim in the same file.

Principle 8 is the whole of why this exists — a claim without its measurement
is a defect, and this one had drifted away from the measurement it described.
Principle 7 shapes the fix: the paragraph now decomposes the residue instead
of naming only the dominant term.

Cycles 1 and 2 were REQUEST_CHANGES and cycle 3 APPROVED with one deletion;
everything raised is addressed at 52439ae55.
What changed, and what the
findings turned up, is sections 8 to 10.

1. The sentence was false — verified here, not quoted

atom/compass/runner/model_runner.py's class docstring ended:

... and no named tensor on the runner holds any of it.

Re-verified at 92f1fdafe with ast (the module cannot be imported without a
driver), reading ATOM's own atom/model_engine/model_runner.py:

holder where what it is
self.forward_vars model_runner.py:1297 the dict allocate_forward_vars builds: 5 CpuGpuBuffers, the input_ids buffer, and the outputs tensor
self._fv_ring model_runner.py:1358, :1383 the list of per-slot dicts built from it — one slot at pp_size <= 1, pp_size slots otherwise

Both are named attributes of the runner, and they hold that memory for the life
of the process. Exactly the two the issue names, at exactly the three lines. The
sentence was false on its plain reading; it was true only under "a tensor bound
directly to an attribute of the subclass", which is an instrument's definition
and not something a docstring can carry.

The measurement it overstates is right and is untouched. #76 recorded 0 bytes
allocated, 0 reserved and 0 operators dispatched across _build_and_load_model +
_maybe_warmup + allocate_kv_cache(2048), against a base that would read
1,503,300,328 B (Qwen3-0.6B) or 55,563,006,776 B (Qwen3.8-27B) of checkpoint.
What is wrong is "holds nothing". Construction leaves resident, measured on
xiaobizh_n18 at TP1, single process, Gloo rendezvous, enforce_eager=True,
load_dummy="empty", gpu_memory_utilization=0.10, Qwen3-0.6B,
hidden_size=1024, bf16 (#76's closing handoff):

batch budget / seqs allocated after __init__ of which forward_vars of which the outputs term
1024 tok / 4 seqs 2,168,320 B 99.1% 97.6%
8192 tok / 256 seqs 17,668,096 B (16.9 MiB) 99.4% 95.5%

So 16.9 MiB is one hidden size, and the claim that survives is
O(batch budget x hidden size). The docstring now states that shape and leaves the
bytes here — for a better reason than the one this PR first gave. #80's round 3
ruled a docstring cannot carry a measurement's conditions, which only says where a
number may not go. The stronger reason is that a shape is a structural
property of the source and can be pinned where it is asserted
, which test 2
below does by reading torch.empty(self.max_num_batched_tokens, ..., hidden_size)
off the AST in this same commit. The bytes can get that on no CPU tier at all.

2. A second, smaller defect in the same sentence-pair

What remains is the base's forward-vars ring ...

reads as all of the residue. The ring was measured at 99.1% / 99.4% of it;
the remaining 0.6-0.9% is a CUDA stream, the ring's events, the attention
metadata builder and the expert-load-balancing runtime. Corrected to "almost all
of what stays resident", with the remainder named — principle 7 again.

The corrected paragraph also states one fact the old one did not: the dominant
term is allocated once however deep the pipeline is. _clone_slot
(model_runner.py:1372-1381) clones only CpuGpuBuffers, so a ring slot shares
the outputs tensor and copies only the staging buffers.

3. It is pinned — three CPU-only tests

In tests/compass/test_runner_non_allocating.py, which already reads ATOM's
source with ast for exactly this reason (importing the binding module runs
aiter's rocminfo probe and raises where there is no driver):

  1. test_the_docstring_names_every_attribute_that_holds_the_ring — over the
    whole ModelRunner body
    , 94 self.x = ... bindings, the ones whose value
    builds or holds a ring buffer are exactly {forward_vars, _fv_ring}, and
    the class docstring names both.
  2. test_what_that_ring_costs_is_the_batch_budget_by_the_hidden_size — the
    outputs entry is torch.empty(self.max_num_batched_tokens, ..., hidden_size).
    The bytes are a measurement and stay in the record; which two numbers they are
    a product of is a structural fact, so it is pinned.
  3. test_the_overrides_bind_no_attribute_that_could_hold_a_tensor — the stronger
    half, and the half this package owns: NonAllocatingRunner binds only model
    and _token_stream, so the subclass adds no tensor of its own. It mirrors test
    1's last two lines, holding the class docstring's enumeration to the same two
    names, so prose and source fail together in both directions.

The pin bites — measured, not asserted. Three perturbations, each run against
the test's own code:

perturbation result
a third holder in the ring builder (self._fv_spare = CpuGpuBuffer(1) in _init_forward_vars_ring) holders becomes {_fv_ring, _fv_spare, forward_vars} — test 1 fails
a third holder in __init__ (self._fv_spare2 = CpuGpuBuffer(1)) holders becomes {_fv_ring, _fv_spare2, forward_vars} — test 1 fails
the docstring as it read before this change names neither holder in backticks — test 1 fails

Test 1 therefore fails both when a named tensor appears on the runner and when
the corrected sentence stops being true, which is what #186 asked for. Test 3's
mirror was measured the same way, after cycle 2 found the pin one-directional:

perturbation result
_token_stream dropped from the class docstring test 3 fails (green before the mirror)
the whole enumeration replaced by one bare sentence test 3 fails (green before the mirror)
self._scratch = 1 added to NonAllocatingRunner test 3 fails

Two nearby bindings are deliberately outside it, and the test says so rather
than leaving a successor to wonder. self.forward_vars is assigned a second time
at :1405, where _advance_forward_vars rebinds the name to a slot of the ring
it already holds — a rotation, not a fourth holder. And
self.tokenID_processor.input_ids (:1408) is a fourth name reaching a ring
buffer, one attribute deeper, which is true and correctly outside a claim about
attributes on the runner.

4. The audit of model_runner.py — 24 claims checked, 21 true

#104 corrected three prose claims in this area and missed this one, so the sweep
is the point rather than a courtesy. Every checkable prose assertion in
atom/compass/runner/model_runner.py at 92f1fdafe, checked against source:

# claim verdict
1 module docstring: "the runner class that runner_qualname names" true
2 constructs without weights, KV tensors or a step true
3 scheduler / admission / block manager / prefix index run unchanged true — 92f1fdafe against merge-base 0b4f1ddba touches only .gitignore and CLAUDE.md outside the compass dirs
4 removes them "by overriding the six methods that own them" true — 6, already pinned by test_the_replaced_methods_...
5 overrides holds the bodies and says why true
6 construction is not free of device memory true (#76)
7 "what remains is the base's forward-vars ring" overstated — 99.1% / 99.4%; corrected
8 dominant term is max_num_batched_tokens by hidden_size true — 97.6% / 95.5%; now pinned
9 sized by the batch budget and hidden size, not by the weights true
10 "no named tensor on the runner holds any of it" false — corrected and pinned
11 NonAllocatingRunner first, so its methods win true — MRO [CompassModelRunner, NonAllocatingRunner, ModelRunner, object]
12 no __init__; the base runs all of its own first true
13 the worker resolves each RPC with getattr(runner, name, None) and skips None true — async_proc.py:237-239
14 an unanswered name produces no error anywhere; the worker stays healthy and nothing logs true — the continue logs nothing; 90 s park measured in #76
15 failing the import kills the worker at construction, with a traceback true
16 "all twelve" true — RPC_SURFACE has 12 entries
17 a waited name parks its caller on an unbounded queue read true — call_func does outputs_queue.get() with no timeout, async_proc.py:431
18 an unwaited one parks nobody; busy_loop skips it and carries on true
19 "which for exit means the loop never breaks" false — see below; corrected
20 for process_kvconnector_output, a KV load is silently never started true — engine_core.py:500 does not wait
21 no CPU tier can execute the raise; only an import on a machine with a GPU is evidence true
22 AsyncIOProc.__init__ resolves the runner class at :166 before self.runners = [] at :167 true — exact, both lines
23 the finalizer then fails with AttributeError: ... no attribute 'runners' true — init_exit_handler registers weakref.finalize(self, self.exit) (atom/utils/__init__.py:573) before :166, and AsyncIOProc.exit iterates self.runners (:176)
24 the refusal message: these names are dispatched by name, waited ones park, unwaited ones are skipped true

21 of 24 true. Three not, and two of those three are the sentence-pair #186
names. The third, #19, is new and is corrected in the same file: busy_loop's
if func_name == "exit": break (async_proc.py:251-252) is indented 12 spaces
against the for runner in self.runners: body's 16, so it is a sibling of
that loop and fires on the name dequeued at :234 regardless of any reply. What
a hole in exit actually loses is ModelRunner.exit (model_runner.py:1038):
the distributed environment is never destroyed, and the graphs and the five KV
tensors it deletes stay held. It is in this PR's file set, so it is fixed here
rather than handed to a PR that might not take it, which is how #186 itself
survived.

What this audit covered, precisely: one file. It is not a statement about the
package. The review found the un-partitioned twin of claim #19 two files away —
atom/compass/runner/__init__.py's package docstring says "a hole in that surface
is a caller that waits forever rather than an error"
, which is false for the two
False entries in RPC_SURFACE. That is outside this file set, so it is
issue #193 with a named result, not a fold-in and not a hand-off to whatever
edits that file next. atom/compass/runner/__init__.py and overrides.py have
not been audited.

5. Gates

1. ATOM's suite unmodified, as a delta against a control measured in this
session.
Both sides staged by git archive + stamps + docker cp into
xiaobizh_n18_cpu (md5 verified on both ends), each run with PYTHONPATH set to
its own root and import atom confirmed to resolve there, each tree's own
scripts/compass/gate_cpu.sh, one at a time, unpiped. Re-run at the review
head, not carried:

tree commit result rc
control 92f1fdafe 4557 passed, 149 skipped, 3 xfailed, 37.55s GATE_CPU_RC=0
branch 52439ae55 4560 passed, 149 skipped, 3 xfailed, 38.61s GATE_CPU_RC=0

Delta +3, exactly the three new tests. Skips and xfails unmoved. Both
gpu: not required (.compass-changed stamp) — neither changed path is in
gpu_gate_triggers.txt. The control reproduces the 4557 this wave has been
quoting, measured rather than carried. TestTheRegionIsNotCopiedPerChunk did not
fire on either side, at any of the four heads.

Lint, re-read at this head: ruff check on the two changed files
RUFF_CHECK_RC=0, ruff format --check RUFF_FORMAT_RC=0, and
ruff check atom/compass/ tests/compass/ RUFF_COMPASS_RC=0. ruff 0.16.7, run in
the same container on the staged branch tree at 52439ae55.

2. New CPU-only tests — the three above, in tests/compass/, no driver.

3. The named result — section 1: the corrected sentence, which named tensors
the runner holds, what they cost as a function of batch budget and hidden size,
beside the measurement it describes.

4. Review — cycles 1 and 2 REQUEST_CHANGES, cycle 3 APPROVE with one
deletion. Sections 8 to 10.

6. Size — corrected, and how the wrong number happened

Counted with an ast statement counter calibrated against landed
atom/compass/spec/ = 275 statements / 495 code / 763 physical, which it
reproduces exactly (code excludes blanks, comments and docstring lines).
At 52439ae55:

unit statements code physical
production — atom/compass/runner/model_runner.py 11 -> 11 (+0) 22 -> 22 (+0) 76 -> 92 (+16)
test — tests/compass/test_runner_non_allocating.py 134 -> 163 (+29) 162 -> 203 (+41) 326 -> 427 (+101)

Cross-checked against git diff --numstat 92f1fdafe: 24 8 on the production
file (net +16) and 101 0 on the test file (+101). That check is narrower than
"both agree", and cycle 2 was right to say so
: numstat witnesses the
physical column only. statements and code have no independent source here.
It happens to cover exactly the column that was wrong in cycle 1, so it addresses
the failure that occurred; the stated cause of the other two is arithmetically
consistent with it but is not independently verified.

The wrong number in cycle 1 was a figure carried across a commit, in a PR about
figures carried across a commit.
The count was run, then ruff format split two
call expressions across three lines each, and the count was never re-run — so the
body reported 199/400 where the head read 203/404. The reviewer's git diff --numstat caught it as 78 0 against a claimed +74. The fix is not a better
counter: it is that the counter now runs after the formatter, and the numstat
cross-check is part of the measurement rather than a thing the reviewer does.

The production change is prose, not zero code: the docstring is one Expr
node before and after, so the statement count cannot move. +29 test statements,
at the top of the 10-30 estimate.

7. What a successor should know

  • The pin is an ast read of ATOM's source, so it is exact-string on
    CpuGpuBuffer, torch.empty and self.forward_vars. A buffer built through a
    new helper name would not be seen. That is the same trade
    test_only_the_binding_module_reaches_the_engine already takes in this file,
    and it errs toward firing on a rename rather than staying silent. What it does
    now cover is the whole class, so where a holder is bound no longer matters.
  • Nothing here asserts the bytes. No CPU tier can: the figures come from a
    CUDA allocator on a machine with a card. The tests pin the shape, which is a
    structural property of the source; the product itself stays a measurement in
    this record.
  • _fv_ring at pp_size > 1 was never measured — every figure above is a
    single-slot ring. The per-slot clones copy the staging buffers, which are the
    1.5-3.9% terms (the gap between forward_vars and its outputs entry), not the
    dominant one.
  • atom/compass/runner/__init__.py and overrides.py carry unaudited prose.
    compass: the runner package docstring names the wrong failure for the two unwaited RPC names #193 covers the one false claim already found in the first of them.
  • The size figures' cross-check covers the physical column only, as above. A
    successor quoting statements or code from this record is quoting one
    instrument, not two.

8. Review cycle 1 — what changed at 5b6157033

F1. The walk covered two method bodies while the prose claimed the class. The
reviewer's perturbation — self._fv_spare2 = CpuGpuBuffer(1) in __init__ — left
the test green. The walk now reads the whole ModelRunner body; over 94
self.x = ... bindings the answer is the same {forward_vars, _fv_ring}, which
is the reviewer's own independent pass, so widening changed only what the test can
see. The test docstring and section 7 no longer overstate it.

Widening turned up a defect the narrow walk structurally could not show. The
helper kept one value per name, and forward_vars is assigned twice — the dict
of buffers at :1297 and the rotation rebind at :1405. ast.walk does not visit
in source order, so the rebind displaced the dict and forward_vars dropped out
of the holder set entirely
: the widened test failed its own unperturbed tree.
The helper now returns (name, value) pairs and a name counts as a holder when
any of its bindings does. Worth stating plainly: the narrow walk was passing partly
because it never saw the second assignment, which is the same shape of luck the
original docstring was relying on.

F2. The new paragraph dropped _token_stream. The list was carried verbatim
from #76's handoff rather than re-derived, while this branch's own test 3 asserts
NonAllocatingRunner binds exactly model and _token_stream
(overrides.py:382). The docstring now names both, and separates
config.num_kvcache_blocks as a count rather than a buffer. The two lists are now
pinned against each other.

F3. Section 6, with the mechanism.

Also folded in: gpu_memory_utilization=0.10 restored to #76's stated conditions;
the exit correction now names what is lost and why the loop still breaks; row 3's
evidence is now about the landed tree rather than this diff; #193 opened.

9. Review cycle 2 — what changed at f80fedc09

F1. The pin was one-directional and a sentence said otherwise. Test 3's
docstring claimed the class docstring was held to the same two names it asserts.
Nothing asserted that: dropping _token_stream from the prose, or replacing the
whole enumeration with one bare sentence, both left the suite green. This was a
cycle-1 request declined by writing a sentence that said it was done
— which is
worse than declining, because from outside an unread finding and a rejected one
look the same. It is stated here rather than quietly fixed.

The fix taken is the mirror, not the deletion, of the two the reviewer called
equally acceptable. Deleting the sentence would leave the enumeration that cycle 1
corrected as another unpinned prose claim, which is the exact defect this branch
exists to fix; the sentence describes the property worth having, so the property
is what changed. Both perturbations that were green are now red, and the third
stays red.

N1. The helper docstring named the wrong mechanism. It blamed ast.walk not
visiting in source order. That is true in general and is not what fired: the
two forward_vars bindings are walked in source order, and what discarded the
dict of buffers was last-wins in a mapping keyed by name, keeping the rotation
rebind, which names no buffer. The sentence now says that and makes no claim about
walk order at all. A correct explanation of the wrong mechanism is the class of
defect this PR is about, so it does not get to sit in the fix for it.

N2. The exit comment overstated in the other direction. It said the graphs
and five KV tensors stay held; on this runner neither exists, because
allocate_kv_cache allocates nothing and the deletions at
model_runner.py:1051-1059 are hasattr-guarded. What an unanswered exit
actually loses here is destroy_dist_env() (:1044), del self.model
(:1060-1061) and torch.cuda.empty_cache() (:1064). The comment names those
three — and, after cycle 3, stops there; see section 10.

10. Review cycle 3 — APPROVED, with one deletion at 52439ae55

The rewrite above was line-exact on every citation and then attached a
consequence that does not exist
: "... and torch.cuda.empty_cache() never
runs, so the forward-vars ring stays in the allocator."
Verified here rather
than taken on the review's word: no del self.forward_vars, del self._fv_ring,
= None or delattr for either name exists anywhere in
atom/model_engine/model_runner.py, so exit never releases the ring; and
torch.cuda.empty_cache() releases "all unoccupied cached memory" — read from
the torch in the gate container — which a live attribute of a live runner is not.
The ring's residency is invariant to whether exit runs. Of the three losses
named, only destroy_dist_env() has a real cost here; self.model is an
UnbuiltModel registering no parameter and no buffer.

The pattern is worth more than the clause, and it is the best thing this review
found.
This one sub-clause carried a consequence it does not have at three
consecutive heads
:

head the clause's ending why it was wrong
92f1fdafe (landed) "the loop never breaks" false — the break is a sibling of the per-runner loop
5b6157033 "the graphs and the five KV tensors stay held" empty here — neither exists on this runner
f80fedc09 "so the forward-vars ring stays in the allocator" does not follow, and would not follow if exit did run

Each rewrite fixed the last and reached for a new consequence, because a true
observation feels like it must lead somewhere. It does not. The sentence now
ends at the fact.
That failure mode — a claim repeatedly repaired by
substituting a different wrong consequence — is carried into the handoff.

🤖 Generated with Claude Code

#186)

`CompassModelRunner`'s class docstring ended "no named tensor on the runner
holds any of it". Re-verified at the branch head with `ast`: `forward_vars`
(model_runner.py:1297) and `_fv_ring` (:1358, :1383) are named attributes
holding the whole forward-vars ring for the life of the process, so the
sentence was false. Its neighbour, "What remains is the base's forward-vars
ring", read as 100% of the residue where the ring was measured at 99.1% / 99.4%
of it.

The measurement the paragraph describes is right and is unchanged: construction
allocates 0 bytes, 0 reserved and dispatches 0 operators against a base that
would read 55.5 GB of checkpoint. What was wrong is "holds nothing":
construction is O(batch budget x hidden size), and the 16.9 MiB on record is
one hidden size. The corrected prose names both holders, states the shape, and
keeps the bytes out of the docstring, which cannot carry a measurement's
conditions.

Three CPU-only tests pin it, over ATOM's own source with `ast` rather than an
import the CPU tier cannot take: the two holders are exactly what the base's
allocation binds and the docstring names both; the dominant term is
`torch.empty(self.max_num_batched_tokens, ..., hidden_size)`; and the overrides
bind no attribute of their own beyond `model` and `_token_stream`. Both
perturbations were measured to fail the pin.

Also corrected in the same file: the RPC comment said an unanswered `exit`
means "the loop never breaks". `busy_loop` breaks on the dispatched name
(async_proc.py:251-252), not on the reply; what is lost is the runner's own
shutdown.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two named attributes on the runner hold that ring for the life of the
process: `forward_vars`, the dict `allocate_forward_vars` builds, and
`_fv_ring`, the list of per-slot dicts built from it. Both are the base's.
What this class adds is no tensor at all: `model`, a module registering no

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Principle 7 / 8 — the new paragraph's own enumeration is incomplete, and this PR's own test knows it.

NonAllocatingRunner binds three things, not two: self.model (overrides.py:182), self.config.num_kvcache_blocks (:239) and self._token_stream (:382). This sentence names the first two and drops the third. Test 3 in the same commit asserts the true set of self.* bindings and gets it right:

assert set(_self_assigned(overrides)) == {"model", "_token_stream"}

so a reader who runs the pin and then reads the prose gets two different answers to "what does this class bind?".

The narrow reading survives — a DeferredTokenStream is not a tensor, so "adds no tensor at all" is not false. What is wrong is the enumeration after the colon, and that is the same defect class #186 is about: an aggregate stated without its full decomposition (principle 7).

What makes it worth blocking rather than noting: the enumeration is carried verbatim from #76's closing handoff — "NonAllocatingRunner sets only self.model ... and self.config.num_kvcache_blocks" — rather than re-derived at this head. A PR whose whole subject is that inherited prose drifts away from the code should not inherit a list it could have derived from the AST pass it already wrote three lines of tests around. #186's named result asks for better than "a second sentence with the same property", and this sentence has that property: nothing asserts it. Test 1 requires the docstring to name both ring holders; nothing requires this sentence to name what the subclass binds.

Two-line fix, and the second half is what stops it recurring:

  1. name _token_stream (and say what it is — the deferral bookkeeping, per step_output.py:93-105);
  2. extend test 3 to require the docstring to name every element of _self_assigned(overrides), exactly as test 1 does for the holders.

Separately, worth one clause while you are in this paragraph: _token_stream.prev_batch retains the previous ScheduledBatch for the life of the runner, which is state this class adds and the sentence does not mention either.

"""Construction leaves the base's forward-vars ring resident, and named
attributes of the runner hold it -- which the docstring denied until it was
corrected, with nothing asserting either way. Both holders are the base's,
so a rename or a third one upstream stops the sentence being true; this

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Principle 8 — the pin's stated scope is wider than its measured scope. Reconstructed, not read.

"a rename or a third one upstream stops the sentence being true; this fails then" is not what the test does. _self_assigned is applied to exactly two method bodies (allocate_forward_vars, _init_forward_vars_ring), so it sees a third holder only if it is added inside one of those two methods.

Measured on xiaobizh_n18_cpu, staged 15892384d, tests/compass/test_runner_non_allocating.py -k test_the_docstring_names_every_attribute_that_holds_the_ring:

perturbation to atom/model_engine/model_runner.py result
self._fv_spare = CpuGpuBuffer(1) inside _init_forward_vars_ring (the PR's row) 1 failed — Extra items in the left set: '_fv_spare'
the pre-change docstring restored (the PR's second row) 1 failed, 26 passed — on the all(f"\{name}`" in ...)` line
self._fv_spare2 = CpuGpuBuffer(1) in __init__, immediately after self.allocate_forward_vars() 1 passed

Both rows the PR claims reproduce exactly. The third is mine, and it is a third named holder on the runner added upstream, which makes "Two named attributes on the runner hold that ring" false while this test stays green. __init__ is not a hypothetical place for one: self.async_execute_stream (:776) and self.forward_done_event (:820) already live there.

The PR body's table row is accurate as written — it names the method. This docstring, and §7 "What a successor should know" (which discloses the exact-string trade on CpuGpuBuffer / torch.empty / self.forward_vars but not the method-scope trade), are the two places that overstate it.

Either is fine as a fix, and they are not equivalent:

  • widen — walk the whole ModelRunner ClassDef instead of two methods. The set then grows by the stream and the events, so the assertion becomes "every self.x whose value builds or holds a ring buffer", which is what the sentence actually claims; or
  • narrow the prose — say in both places that the pin reads allocate_forward_vars and _init_forward_vars_ring only, so a holder bound elsewhere in the base is out of scope.

One more thing the walk does not see, and which the prose arguably covers: self.tokenID_processor.input_ids = self.forward_vars["input_ids"] (model_runner.py:1408) is a fourth name reaching a ring buffer, one attribute deeper. The target filter (isinstance(t.value, ast.Name)) excludes it, correctly for "attributes on the runner" — worth a clause in the docstring so the next reader does not have to rediscover that it was deliberate.

@jgong5

jgong5 commented Sep 22, 2026

Copy link
Copy Markdown
Owner Author

Review — PR #188, 92f1fdafe..15892384d

Reviewed against atom/compass/design/README.md's eight principles. One commit, two files,
+96/−8. Every load-bearing claim rebuilt from source or re-run on my own staged trees before
reading the PR's version of it.

Verdict: REQUEST_CHANGES

The PR is substantively right and unusually well-measured. The sentence really was false,
both perturbations really do bite, the third finding about exit is real and is the best
thing here, the gate reproduces to the node id, and the +0 production statement count is
honest. I rebuilt nine claims independently and seven came back exactly as stated.

What I am asking for is two findings of the same class this PR exists to fix, plus one
number that does not match its head:

  • model_runner.py:38 — BLOCKING, principle 7. The new
    paragraph's own enumeration of what the subclass adds names model and
    config.num_kvcache_blocks and drops _token_stream, which this PR's own test 3
    asserts
    is one of exactly two self.* bindings. The list is carried verbatim from RUNNER-1 — the runner subclass and its non-allocating construction #76's
    handoff rather than re-derived at this head. CompassModelRunner's docstring says it holds no named tensor; it holds two #186's named result asks for better than "a
    second sentence with the same property", and nothing asserts this one.
  • test_runner_non_allocating.py:202 — BLOCKING, principle 8.
    The test's docstring says "a third one upstream stops the sentence being true; this fails
    then". Measured: a third holder added in __init__ leaves it green. The walk covers two
    method bodies, not the class. The PR body's row is accurate as written; the test docstring
    and §7 are not.
  • F3, principle 8 (blocking, one-line fix). §6's test row is wrong at this head:
    physical is 326 → 404 (+78), not 400 (+74), and code is 203 (+41), not 199 (+37).
    git diff --numstat gives 78 0 for that file independently, and my counter reproduces
    the spec/ calibration 275 / 495 / 763 exactly. The statement counts (+0 / +27)
    are right, so only the two columns move. This looks like a figure carried from an earlier
    head, which is the same defect as not running the count: a verdict carried across a
    commit. In a PR about carried prose.

Non-blocking: F4 (the audit's population), F5 (row #3's evidence), F6 (a dropped
condition). All below.


1. The holder set — my own AST pass, and it is the only two

I did not walk the two methods the PR walks. I walked the whole ModelRunner ClassDef for
every self.<attr> = <value> whose value source mentions any of CpuGpuBuffer,
torch.empty/zeros/ones/full/arange/tensor, torch.cuda.Event, torch.cuda.Stream,
device=self.device, .cuda(, from_numpy, forward_vars or _fv_ring. Result at
92f1fdafe:

attribute sites ring holder?
self.forward_vars :1297 (allocate_forward_vars), :1405 (_advance_forward_vars) yes at :1297; :1405 is a rebind to an existing slot, not a new holder
self._fv_ring :1358, :1383 yes
self._fv_slot_events :1389 events, not the ring
self._stage_h2d_done :1362 event
self.async_execute_stream :776 stream
self.forward_done_event :820 event

So {forward_vars, _fv_ring} is the complete holder set, at exactly the three sites #186
named — and the stronger claim the new test makes (exactly, not "the issue named two") is
true. The :1405 rebind is the one an eyeball pass gets wrong and neither the PR nor the
test is confused by it.

One name the walk deliberately cannot reach, and which I think deserves a clause in the
docstring rather than a finding: self.tokenID_processor.input_ids = self.forward_vars["input_ids"]
(:1408) is a fourth name reaching a ring buffer, one attribute deeper. The base's own
comment at :1406 calls it "the one forward_vars buffer aliased outside the dict".

2. Both perturbations — re-run, both fail as claimed

Staged 15892384d by git archive + docker cp into xiaobizh_n18_cpu, -k on test 1:

perturbation claimed measured
self._fv_spare = CpuGpuBuffer(1) in _init_forward_vars_ring fails 1 failed — Extra items in the left set: '_fv_spare'
the docstring as it read at 92f1fdafe fails 1 failed, 26 passed — on assert all(f"\{name}`" in ast.get_docstring(runner) ...)`

The second is the one that matters and it is the one that reproduces. With the old docstring
in place exactly one test fails and it is this one, so the pin is attached to the correction
and not merely co-resident with it. A third perturbation of my own is the finding at
:202.

3. The exit finding holds at source — and it is the best thing in this PR

busy_loop (async_proc.py:231-253), by column:

233        while True:
234            func_name, args = self.get_func()
236            for runner in self.runners:
238                if func is None:
239                    continue
251            if func_name == "exit":
252                break

break is at 12 spaces, the for runner body at 16. It is a sibling of the for, not a
statement in it, so it fires on the name taken off the ring at :234 regardless of whether
any runner answered. The landed comment — "which for exit means the loop never breaks" —
is false, and the replacement is right in both halves: the loop does break, and what is
actually lost is ModelRunner.exit (model_runner.py:1038), which destroys the distributed
env, releases CUDA graphs and deletes five KV tensors. That is a materially worse failure than
a loop that will not exit, and it was sitting in landed prose about the engine's quietest
surface. Nobody had flagged it. It is correctly in this file set.

4. The audit — classification good, population one file too small (F4, non-blocking)

I re-walked the 24 rows against source and spot-checked every one that cites a line number.
All the citations are exact: async_proc.py:237-239 (row 13), :431 — self.outputs_queue.get()
with no timeout (17), :166/:167 (22), atom/utils/__init__.py:573 — weakref.finalize(self, self.exit),
registered at async_proc.py:139, before :166 (23), engine_core.py:500 — call_func
with no wait_out (20), RPC_SURFACE = 12 entries (16). The three "not true" verdicts are
correct and the 21 "true" ones I checked hold.

But 24 is the right denominator for the wrong population. The issue asked about "that
file", and the audit answers that question honestly. The defect class does not stop at the
file boundary, and the twin is two files away — atom/compass/runner/__init__.py's package
docstring:

"the worker skips a name the runner lacks without raising, so a hole in that surface is a
caller that waits forever rather than an error"

That is the un-partitioned version of exactly the distinction row #19 corrects. Two of the
twelve names on RPC_SURFACE are False (exit, process_kvconnector_output), and for
those a hole is not a waiting caller — which is why model_runner.py's comment partitions
them and why overrides.py's reply-contract carefully conditions its version on
"a caller that asked for the reply". The package docstring does neither. §4 closes with
"Nothing else in the file needed an issue opened against it" — true of the file, and the
reader will take it as true of the package.

This is not a request to widen this PR. It is a request to open the issue, because the
PR's own doctrine is that a finding handed onward is owned by nobody, and the only thing
worse than handing it on is neither fixing it nor recording it.

F5 (nit, principle 8). Row #3's evidence — "true — the diff touches 2 files" — is about
this diff and cannot establish a claim about the landed tree. The supporting fact does
exist: 92f1fdafe against its merge-base with origin/main (0b4f1ddba) touches exactly two
files outside atom/compass/, tests/compass/ and scripts/compass/ — .gitignore and
CLAUDE.md. Cite that.

5. The bytes — quoted correctly, one condition dropped (F6, nit)

Against #76's closing handoff, every figure is exact: 2,168,320 B at 1024 tok / 4 seqs and
17,668,096 B at 8192 / 256, forward_vars 99.1% / 99.4%, the outputs term
97.6% / 95.5%, 16.9 MiB as one hidden size, 1,503,300,328 B and 55,563,006,776 B of
checkpoint. The residue decomposition in the new docstring — stream, events, attention
builder, EPLB runtime — is also #76's, not invented here, and is quoted faithfully.

The conditions travel with them except one: #76 lists gpu_memory_utilization=0.10
alongside TP1 / enforce_eager=True / load_dummy="empty" / Qwen3-0.6B / hidden_size=1024
/ bf16, and §1's condition line drops it. A condition dropped in transcription is how a
measurement becomes a number.

6. Ruling — shape in the docstring, bytes in the record: right, for a better reason

The PR justifies the split on #80's round-3 ruling that a docstring structurally cannot carry
a measurement's conditions, instrument and date. That is true and it is the weaker half of
the argument, because on its own it only says where a number may not go.

The stronger half is the one this PR demonstrates and does not claim: a shape is not a
number, it is a structural property of the source, and unlike a byte count it can be pinned
where it is asserted.
O(batch budget × hidden size) is not one data point that happened on
one machine; it is torch.empty(self.max_num_batched_tokens, ..., hidden_size) at
model_runner.py:1308-1313, and test 2 reads exactly that off the AST in the same commit. So
the shape in the docstring does carry its source, in the only form CI can hold — which is
what principle 8 asks for. The bytes cannot get that on any CPU tier, and §7 says so plainly.

The split also passes the harder test: it does not leave the docstring stating a proportion as
a bare fact. "Almost all of what stays resident" is a hedge, not a number, and the remainder is
named rather than elided — principle 7 satisfied without importing 99.1% into prose that
cannot carry its conditions. And the pp_size qualification ("allocated once however deep the
pipeline is") is correct at source: _clone_slot (:1372-1381) clones only CpuGpuBuffers,
so the outputs tensor is shared across slots.

Ruling: keep the split. Consider adding the one sentence that makes the reason survive the
next reader — that the shape is in the docstring because a test asserts it there, and the
bytes are not because no test can.

7. Ruling — three claims fixed here rather than handed onward: right

All three are in atom/compass/runner/model_runner.py: #10 and ROCm#7 in the class docstring
(lines 26-30 at base), #19 in the module-level comment (line 52 at base). Nothing is being
fixed in a file this PR has no business in, so this is not the opposite error, and given that
#186 exists because F15 was handed from #80 to #90 and neither owned it, fixing them in
place is the right call twice over.

The boundary case is F4 above: the un-partitioned twin is outside the file set, and the
correct handling for that one is an issue, not a fix here. That is the distinction the PR
needed to draw and did not.

8. Size — my own count

Counter calibrated against landed atom/compass/spec/, which it reproduces as
275 / 495 / 763 exactly, counting docstring Expr nodes:

unit statements code physical
atom/compass/runner/model_runner.py 11 → 11 (+0) 22 → 22 (+0) 76 → 86 (+10)
tests/compass/test_runner_non_allocating.py 134 → 161 (+27) 162 → 203 (+41) 326 → 404 (+78)

Production +0 is true and honestly reported: the docstring is a single Expr node before
and after, so the statement count structurally cannot move, and reporting the zero with that
reason is the right form rather than a suspicious one. +27 test statements is inside the
10-30 estimate. The two test columns are F3 above.

9. Gates — reproduced independently, to the node id

Both sides staged by git archive + .compass-commit / .compass-changed stamps +
docker cp into xiaobizh_n18_cpu (tarball md5 e50c88803d9d26f43f63b39ffb639953, verified
both ends), each run with PYTHONPATH at its own root, import atom confirmed to resolve
there, each tree's own scripts/compass/gate_cpu.sh, one at a time, unpiped. Nothing
committed under the staging path; it is removed.

tree commit stamp result rc
control 92f1fdafe 4557 passed, 149 skipped, 3 xfailed, 33.75s GATE_CPU_RC=0
branch 15892384d 4560 passed, 149 skipped, 3 xfailed, 37.18s GATE_CPU_RC=0

gpu: not required (.compass-changed stamp) and gate: 29 files excluded + tests/plugin on
both. TestTheRegionIsNotCopiedPerChunk did not fire on either side.

+3 confirmed by node id, not by arithmetic on a total. Collected both trees and diffed:
exactly one of 123 files moves, tests/compass/test_runner_non_allocating.py 24 → 27, and the
three added ids are

test_the_docstring_names_every_attribute_that_holds_the_ring
test_the_overrides_bind_no_attribute_that_could_hold_a_tensor
test_what_that_ring_costs_is_the_batch_budget_by_the_hidden_size

with none removed and no id renamed elsewhere.

Lint re-read at this head, ruff 0.16.7, on the staged branch tree: RUFF_CHECK_RC=0,
RUFF_FORMAT_RC=0 ("2 files already formatted"), RUFF_COMPASS_RC=0 over
atom/compass/ tests/compass/.

Mechanical check — design-doc references in code: zero. No added line in the diff matches
design/, a NN_topic.md name, a D<n> / T<n> / P<n> token or "principle N", and the
whole of atom/compass/**/*.py still measures 0.


Reviewed by an agent. Verdict: REQUEST_CHANGES — the two inline findings and F3 block;
F4, F5 and F6 do not. The correction itself, the pin, the exit finding and every gate
number are confirmed and want no change.

…ubclass binds (#186)

Review cycle 1, three blocking findings.

F1. The walk covered `allocate_forward_vars` and `_init_forward_vars_ring`, so a
holder bound anywhere else in `ModelRunner` was invisible -- a
`self._fv_spare2 = CpuGpuBuffer(1)` in `__init__` left the test green while the
test's own docstring claimed a third holder would fail it. The walk now reads the
whole `ModelRunner` body: 94 `self.x = ...` bindings, same answer,
`{forward_vars, _fv_ring}`.

Widening exposed a defect in the helper it could not show before. `forward_vars`
is assigned twice -- the dict of buffers at `:1297`, and a rebind to a slot of the
ring it already holds at `:1405` -- and the helper kept one value per name while
`ast.walk` does not visit in source order, so the rebind displaced the dict and
`forward_vars` dropped out of the holder set. It now keeps (name, value) pairs and
a name is a holder when any of its bindings is. Three perturbations now fail the
pin: a third holder in the ring builder, a third in `__init__`, and the docstring
as it read before this branch.

F2. The class docstring enumerated what the subclass adds as `model` and
`config.num_kvcache_blocks`, carried from #76's handoff rather than re-derived,
while this branch's own test asserts `NonAllocatingRunner` binds exactly `model`
and `_token_stream` (`overrides.py:382`). The paragraph now names both, and
separates `config.num_kvcache_blocks`, which is a count and not a buffer.

Also sharpened: the `exit` correction now names what an unanswered `exit` loses --
`ModelRunner.exit` (`model_runner.py:1038`) destroys the distributed environment
and deletes the graphs and five KV tensors -- and says why the loop still breaks:
the break is a sibling of the per-runner loop, at 12 spaces against its 16.

F3 is a PR-body figure, corrected there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jgong5

jgong5 commented Sep 22, 2026

Copy link
Copy Markdown
Owner Author

Cycle 1 addressed at 5b6157033 (new commit, not a force-push). All three
blocking findings, plus the two transcription items and the issue.

F1 — widened to the ClassDef. Your perturbation was right and the narrow
walk was the wrong instrument for the claim. Now over the whole ModelRunner
body: 94 self.x = ... bindings, same answer {forward_vars, _fv_ring} —
your independent pass reproduced. self._fv_spare2 = CpuGpuBuffer(1) in
__init__ now fails test 1. The test docstring and §7 no longer overstate it.

Widening broke the test on its own unperturbed tree, and that is the more
interesting half.
_self_assigned returned a dict keyed by attribute name, so
it kept one value per name — and forward_vars is assigned twice, the dict of
buffers at :1297 and your :1405 rotation rebind. ast.walk does not visit in
source order, so the rebind displaced the dict and forward_vars dropped out of
the holder set: holders came back as {_fv_ring} alone. The helper now returns
(name, value) pairs and a name is a holder when any of its bindings is. The
narrow walk had been green partly because it never saw the second assignment,
which is close enough to the failure this PR exists to fix that it belongs in the
record — it is in the commit message and §8.

Both of your notes are folded into test 1's docstring rather than left for a
successor: :1405 is a rotation and not a fourth holder, and
self.tokenID_processor.input_ids (:1408) is a fourth name one attribute
deeper, correctly outside a claim about attributes on the runner.

F2 — _token_stream added, and the two lists now pin each other. You are
right about the mechanism: the list was carried verbatim from #76's handoff
instead of re-derived from overrides.py:382, which my own test 3 asserts.
config.num_kvcache_blocks is now stated separately as a count rather than a
buffer.

F3 — corrected, with the mechanism, because the mechanism is the finding.
Production 11/22/91, test 134→160 statements, 162→200 code, 326→422 physical at
this head; cross-checked against git diff --numstat (23 8 and 96 0), which
agrees. The cycle-1 number was measured, then ruff format split two calls across
three lines each, and it was never re-measured — a figure carried across a commit,
in a PR about figures carried across a commit. The counter now runs after the
formatter and the numstat cross-check is part of the measurement.

gpu_memory_utilization=0.10 restored to #76's conditions. Row 3's evidence
is now the landed tree, verified here rather than taken: git diff --name-only 0b4f1ddba 92f1fdafe outside the compass dirs is exactly .gitignore and
CLAUDE.md.

The twin is #193, not a fold-in. Your reading of it holds: exit
(engine_core.py:260) and process_kvconnector_output (:500) both call
call_func without wait_out, so "a caller that waits forever" is the one
failure mode that cannot occur for either. The issue carries a named result — the
partition, per entry — and asks for the rest of that package docstring to be
audited, since neither it nor overrides.py has been. §4 now says what it
audited: one file.

Your stronger reason for shape-in-docstring is adopted and replaces mine in
§1. "A docstring cannot carry conditions" only says where a number may not go; "a
shape is a structural property of the source and can be pinned where it is
asserted" says why the split is right, and test 2 is the thing that makes it true.

Gates re-run at this head, not carried: control 92f1fdafe 4557 passed /
branch 5b6157033 4560 passed, 149 skipped and 3 xfailed unmoved on both,
GATE_CPU_RC=0 both, gpu: not required both, staged by git archive +
docker cp with md5 matched on both ends and import atom confirmed per root.
RUFF_CHECK_RC=0, RUFF_FORMAT_RC=0, RUFF_COMPASS_RC=0 at 5b6157033.

Nothing declined. No blocking issues from my side.

`model` registers no parameter and no buffer, and `_token_stream` is the
deferral bookkeeping `forward` builds on first use. Anything else appearing
here is a tensor this class put on a device, which is the thing it exists
not to do. The class docstring names the same two, so neither list can be

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Principle 8 — this sentence is false, measured three ways. It is the one half of cycle 1's F1 that was not implemented.

The class docstring names the same two, so neither list can be trimmed to the other's shape without this failing.

The pin is one-directional. Measured on xiaobizh_n18_cpu, staged 5b6157033, full file each time:

perturbation expected if mutual measured
drop `_token_stream` from the class docstring, leaving model red 27 passed
replace the whole enumeration with "What this class adds is no tensor at all." red 27 passed
add self._scratch = 1 to NonAllocatingRunner._build_and_load_model red 1 failed — this test

Only the code → test direction bites. At source there is exactly one ast.get_docstring assertion in the file — line 228, in test 1 — and it checks holders, i.e. {forward_vars, _fv_ring}. Nothing reads the class docstring for model or _token_stream, so the sentence this cycle added to the class docstring can be trimmed to nothing and the suite stays green.

Cycle 1 asked for two things and the first is done well: the enumeration now names _token_stream and separates config.num_kvcache_blocks as a count rather than a buffer, which is more precise than what I asked for. The second was "extend test 3 to require the docstring to name every element of _self_assigned(overrides), exactly as test 1 does for the holders." That assertion is absent, and this sentence asserts that it is present — which is the defect class #186 exists to remove, now one level up: prose claiming a pin that does not exist.

The fix is the two lines already in this file at 227-228, with holders swapped for the binding set:

    bound = {n for n, _ in _self_assigned(overrides)}
    assert bound == {"model", "_token_stream"}
    runner = _classes(PACKAGE / "model_runner.py")["CompassModelRunner"]
    assert all(f"`{name}`" in ast.get_docstring(runner) for name in bound)

With that, the sentence becomes true and both halves of the class docstring's residency paragraph are pinned by the same mechanism. Without it, delete the sentence — an unpinned list is fine as long as nothing says otherwise.

@jgong5

jgong5 commented Sep 22, 2026

Copy link
Copy Markdown
Owner Author

Review cycle 2 — PR #188, 92f1fdafe..5b6157033

Range re-pinned to the new head. 15892384d is an ancestor of 5b6157033
(merge-base --is-ancestor clean), one added commit, no force-push, so cycle 1's range is
contained and nothing was rewritten under the review. Incremental diff 10/5 and 30/12.

Verdict: REQUEST_CHANGES — one finding, and it is two lines

Five of cycle 1's six findings are closed, and two are closed better than I asked. F1's
fix uncovered a real defect in the instrument; F3's fix corrected the mechanism rather than
the number; #193 is opened and correct. I re-ran every perturbation, added three of my own,
rebuilt the dict-versus-pairs account from scratch and then reproduced it inside the test,
and recounted all six size figures. Everything the PR claims reproduces.

The one thing that does not is the half of F1 that was not implemented:

  • test_runner_non_allocating.py:260 — BLOCKING, principle 8.
    "The class docstring names the same two, so neither list can be trimmed to the other's
    shape without this failing."
    Measured: delete _token_stream from the class docstring →
    27 passed; delete the whole enumeration → 27 passed; add a binding in
    overrides.py → red. The pin is one-directional. There is exactly one ast.get_docstring
    assertion in the file (line 228, test 1) and it checks the holders, not the subclass
    bindings. Cycle 1 asked for the mirror of test 1's last two lines; the mirror is absent and
    this sentence says it is present.

This converges in one edit and involves no judgement. The four lines to write already
exist verbatim twenty-five lines above, and the alternative — delete the sentence — is
equally acceptable. There is nothing here for an owner to decide, no design question and no
disagreement about a measurement, so I would not read a third cycle on this as divergence.

Non-blocking, below: N1 the helper docstring's stated mechanism, N2 the exit
comment's two empty terms, N3 what the numstat cross-check does and does not cover.


1. The dict-versus-pairs account — it holds, end to end

This is the most interesting claim in the PR and I rebuilt it rather than reading it. Over
ModelRunner at 92f1fdafe:

self.x = ... bindings in the class 94 (65 distinct names) — the PR's "over 94" is exactly 94
cycle-1 helper (dict keyed by name) at class scope holders = {_fv_ring} — forward_vars survives with the value self._fv_ring[self._fv_idx], which matches no BUFFER_TERM
cycle-2 helper (pairs) at class scope holders = {forward_vars, _fv_ring}
cycle-1 helper at cycle-1's two-method scope holders = {forward_vars, _fv_ring} — correct, because :1405 lives in _advance_forward_vars and was outside the walk

So yes — the cycle-1 test was green for the reason claimed. Its scope excluded the second
binding of forward_vars, and that exclusion was also masking a defect in the helper's shape.
Both statements are true and the PR states the stronger one. Widening the scope without
changing the shape would have turned a correct-for-a-narrow-reason test into a failing one on
an unperturbed tree, which is the cleanest possible demonstration that the narrow walk was
load-bearing in a way nobody had written down.

I then reproduced it in situ, which is the form that settles it: splice the cycle-1
name-keyed dict comprehension back into the cycle-2 test, leave everything else at
5b6157033, and test 1 fails on the unperturbed tree. That is the PR's central new claim,
run rather than argued.

N1 (nit, non-blocking). _self_assigned's docstring gives the reason as "ast.walk does
not visit in source order"
. That is true as a general statement — I measured the lineno
sequence over this class and it is not nondecreasing; it ends 4008, 4018, 3970 — but it is
not what fired here. The two forward_vars bindings are yielded in source order (:1297
then :1405), and it is last-wins in the dict comprehension that discards the right one.
The conclusion ("keeps an arbitrary one of them") is sound and the fix is right; the stated
mechanism is not the one that broke it. One clause.

2. The perturbations — three claimed, plus three of mine

All on xiaobizh_n18_cpu, staged 5b6157033 by git archive + stamps + docker cp
(tarball md5 5ed03ecad1d984a5187dc2012482d72f, matched both ends), tree restored from the
tarball between probes and md5-verified.

# perturbation claimed measured
1 self._fv_spare = CpuGpuBuffer(1) in _init_forward_vars_ring red 1 failed
2 self._fv_spare2 = CpuGpuBuffer(1) in __init__ (my cycle-1 probe) red 1 failed
3 the pre-change docstring red 1 failed, 26 passed
4 mine — self._fv_ring = None at the top of exit(): a late non-buffer rebind of an existing holder — 1 passed under pairs; 1 failed with the cycle-1 dict helper spliced back in
5 mine — rename _fv_ring → _fv_cycle (6 occurrences) — 1 failed
6 mine — self._scratch = 1 in NonAllocatingRunner — 1 failed (test 3)

Cycle 1's F2 is closed by row 2. Row 4 is the probe I added because a helper that changed
shape mid-fix deserves one the fix was not written against: it isolates the shape change from
the scope change, and it separates the two helpers in the direction the fix predicts — pairs
keeps a name that qualifies on any binding, a name-keyed dict loses it. Row 5 closes the
docstring's "a rename ... stops the sentence being true", which nothing had tested.

3. F2 — the fix is right, the claim about the fix is not

model, _token_stream and the separation of config.num_kvcache_blocks as a count are all
correct at source (overrides.py:182, :382, :239), and "the deferral bookkeeping forward
builds on first use" is exact. The enumeration is now complete.

What is not mutual is the pinning, and the sentence that says it is, is the blocking finding
above. Detail and the four-line fix are in the inline comment.

4. F3 — corrected, and the mechanism is the better part

My own count at 5b6157033, counter re-calibrated against landed atom/compass/spec/, which
it still reproduces as 275 / 495 / 763:

unit statements code physical
atom/compass/runner/model_runner.py 11 → 11 (+0) 22 → 22 (+0) 76 → 91 (+15)
tests/compass/test_runner_non_allocating.py 134 → 160 (+26) 162 → 200 (+38) 326 → 422 (+96)

All six figures match the PR exactly. Three sets of numbers have now been quoted for this
file across two heads; these are the ones that survive an independent count.

Is the numstat cross-check sound? Yes, and it is narrower than "both agree" suggests.
base_physical + added − deleted = head_physical is exact for a same-path diff with no
rename: 76 + 23 − 8 = 91 and 326 + 96 − 0 = 422, both confirmed. But numstat is a count of
physical lines and can witness only the physical column; statements and code are the
counter's own outputs with no independent source, and a counter that miscounted docstring
Expr nodes would pass this check unmoved. What redeems it is that physical is exactly the
column that was wrong in cycle 1, so the check does cover the failure it was added for. §6
should say which column it covers rather than "Both agree".

The stated cause is arithmetically consistent: 404 − 400 = 4 = 2 calls × 2 lines, and
the cycle-1 addition contains exactly that shape twice — _method_def's return next(...)
and test_what_that_ring_costs's allocate = _method_def(...), each three lines formatted
and one unformatted. It is not independently verifiable, because the pre-format source exists
nowhere; that is fine, and the remedy is the right kind — ordering the steps rather than
trusting the number. "The formatter moved it after I counted" now leaves evidence in the form
of a counter that cannot be run before the formatter.

5. #193 — opened, and correct

Verified at source: engine_core.py:260 is self.runner_mgr.call_func("exit") and :500 is
self.runner_mgr.call_func("process_kvconnector_output", meta), both without wait_out, so
the package docstring's "a caller that waits forever" is false for both False entries in
RPC_SURFACE — not one. The named result is the per-entry partition plus an audit of the rest
of that docstring, which is the right scope, and §4 now says it audited one file and names
__init__.py and overrides.py as unaudited. Cycle 1's F4 and F5 are closed, and F6
(gpu_memory_utilization=0.10) is restored to the conditions line.

N2 (nit, non-blocking). The rewritten exit comment leads with the real loss —
destroy_dist_env() — and then adds "the graphs and the five KV tensors it deletes stay
held"
. On this runner neither term exists: allocate_kv_cache is overridden to allocate
nothing (overrides.py:230-244), so the for attr in (...) loop at model_runner.py:1050-1058
is hasattr-guarded over five attributes that were never set, and capture_cudagraph captures
nothing. The block is about CompassModelRunner failing to answer exit, so two of the three
named consequences are empty here. torch.cuda.empty_cache() and del self.model are not
named and do happen. Either scope the clause to what ModelRunner.exit does in general, or
name what is lost on this runner.

6. Gates — re-run by me at the new head, not carried

Fresh staging, both sides, each tree's own scripts/compass/gate_cpu.sh, PYTHONPATH at its
own root with import atom confirmed there, one at a time, unpiped.

tree commit stamp result rc
control 92f1fdafe 4557 passed, 149 skipped, 3 xfailed, 40.37s GATE_CPU_RC=0
branch 5b6157033 4560 passed, 149 skipped, 3 xfailed, 39.09s GATE_CPU_RC=0

gpu: not required (.compass-changed stamp) and gate: 29 files excluded + tests/plugin both
sides. +3 by node id, unchanged from cycle 1 — 24 → 27 in the one file, the same three ids
added, none removed, no id renamed. ruff 0.16.7 at this head: RUFF_CHECK_RC=0,
RUFF_FORMAT_RC=0 ("2 files already formatted"), RUFF_COMPASS_RC=0.

Design-doc references in code: still zero — no added line in 92f1fdafe..5b6157033 matches
design/, a NN_topic.md name, a D<n>/T<n>/P<n> token or "principle N", and
atom/compass/**/*.py measures 0.

7. What the amended diff introduced

Two things, both above: the false mutual-pinning sentence (blocking) and the two empty terms
in the exit comment (N2). Nothing else is new. Test 2's next(...) over a walk is the same
family of hazard as the one just fixed, but it is safe — inside allocate_forward_vars exactly
one Assign has a plain forward_vars attribute target, the subscript assignments having
Subscript targets — so it is not a finding, only worth knowing when the next person edits it.


Reviewed by an agent. Verdict: REQUEST_CHANGES — one blocking finding, fixable in four
lines that already exist in the file, with N1, N2 and N3 as non-blocking clauses. Everything
else in this cycle reproduces, including the instrument defect the fix uncovered, which is a
better result than the finding that prompted it.

…e two mechanisms correctly (#186)

Review cycle 2, one blocking finding and two nits.

F1. Test 3's docstring claimed the class docstring was held to the same two names
it asserts, and nothing asserted that: dropping `_token_stream` from the prose, or
replacing the whole enumeration with one bare sentence, both left the suite green.
The pin was one-directional and a sentence said otherwise. Adding the mirror
rather than deleting the sentence: the sentence describes the property worth
having, and deleting it would leave the enumeration corrected in cycle 1 as
another unpinned prose claim, which is the defect this branch exists to fix. Test
3 now reads the class docstring the way test 1 does. Measured: both perturbations
that were green are now red, and a third binding in `NonAllocatingRunner` stays
red.

N1. The helper docstring blamed `ast.walk` not visiting in source order. True in
general, not what fired: the two `forward_vars` bindings are walked in source
order, and it is last-wins in a mapping keyed by name that discarded the dict of
buffers and kept the rotation rebind, which names no buffer. The sentence now says
that, and no longer makes a claim about walk order at all.

N2. The `exit` comment said the graphs and five KV tensors stay held. On this
runner neither exists -- `allocate_kv_cache` allocates nothing, so the
`hasattr`-guarded deletions at `model_runner.py:1051-1059` find nothing. What an
unanswered `exit` actually loses here is `destroy_dist_env()` (`:1044`),
`del self.model` (`:1060-1061`) and `torch.cuda.empty_cache()` (`:1064`), so the
forward-vars ring stays in the allocator. The comment now names those.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jgong5

jgong5 commented Sep 22, 2026

Copy link
Copy Markdown
Owner Author

Cycle 2 addressed at f80fedc09 (new commit, not a force-push). All three
items. No disagreement with any of them, so nothing goes to the owner.

F1 — I took the mirror, not the deletion. You called both acceptable; the
reason for this one is that deleting the sentence would leave the enumeration
cycle 1 asked me to correct as another unpinned prose claim, which is precisely
the defect this branch exists to fix. The sentence described a property worth
having, so I changed the property rather than the sentence. Test 3 now reads the
class docstring the way test 1 does. Measured, against your three:

perturbation before now
_token_stream dropped from the class docstring 27 passed fails
enumeration replaced by one bare sentence 27 passed fails
self._scratch = 1 in NonAllocatingRunner fails fails

On how it happened — you are right, and it is the worst thing in this PR. I
declined a cycle-1 request by writing a sentence asserting it was done. That is
not a missed finding; it is an unmarked refusal, and from outside it is
indistinguishable from an unread one. It is now stated as that in §9 of the body
rather than folded into a tidy "addressed". Anything I decline from here gets
named as declined, with the reason, in the reply.

N1 — fixed, and you are right that it mattered. The two forward_vars
bindings are walked in source order; what discarded the dict of buffers was
last-wins in a mapping keyed by name, which kept the rotation rebind, which
matches no BUFFER_TERM. The docstring now says that and makes no claim about
walk order at all. A correct explanation of the wrong mechanism, sitting inside
the fix for a wrong explanation, is not something I want to leave in the tree.

N2 — fixed, verified at source before rewriting. model_runner.py:1051-1059
is the hasattr-guarded loop over the five KV names, and this runner allocated
none, so nothing there is lost. What is actually lost on an unanswered exit is
destroy_dist_env() (:1044, unconditional), del self.model (:1060-1061) and
torch.cuda.empty_cache() (:1064) — so the forward-vars ring stays in the
allocator. The comment names those three and no longer names graphs or KV
tensors.

Your numstat qualification is adopted rather than argued. §6 now says the
cross-check witnesses the physical column only, and that statements and
code have no independent source here; §7 carries it into the handoff so a
successor quoting those two knows it is one instrument, not two.

Gates re-run at this head: control 92f1fdafe 4557 passed / branch
f80fedc09 4560 passed, 149 skipped and 3 xfailed unmoved on both,
GATE_CPU_RC=0 both, gpu: not required both, staged fresh by git archive +
docker cp with md5 matched on both ends and import atom confirmed per root.
RUFF_CHECK_RC=0, RUFF_FORMAT_RC=0, RUFF_COMPASS_RC=0 at f80fedc09.

Size at this head: production +0/+0/+17, test 134→163 / 162→203 / 326→427
(+29/+41/+101), numstat 25 8 and 101 0.

Nothing declined this cycle. No blocking issues from my side.

Comment thread atom/compass/runner/model_runner.py Outdated
# Both are real failures; neither is a park.
# on -- which for `exit` means `ModelRunner.exit` never runs: the
# distributed environment is never destroyed, `self.model` is never
# dropped, and `torch.cuda.empty_cache()` never runs, so the forward-vars

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

New at this head, principle 8 — the clause so the forward-vars ring stays in the allocator does not follow, and would not follow if exit did run.

Everything else in the rewritten sentence checks out line-exact, and the two claims cycle 2 flagged are correctly gone. This one clause is new and it is wrong in a way the source settles:

  1. ModelRunner.exit never releases the ring. Nothing in atom/model_engine/model_runner.py contains del self.forward_vars, del self._fv_ring, self.forward_vars = None, self._fv_ring = None or a delattr for either — grepped over the whole file. exit (:1038-1065) touches five KV names, model, drafter and the graphs. The ring is not among them.
  2. torch.cuda.empty_cache() cannot release it either. Its own docstring, from the torch in the gate container (2.10.0+rocm7.2.4): "Release all unoccupied cached memory currently held by the caching allocator." The ring is occupied — self.forward_vars and self._fv_ring are live attributes of a live runner — so empty_cache passes over it whether or not it is called.

So the ring's residency is invariant to whether exit runs, and attaching it to empty_cache never runs names a consequence that does not exist. Of the three losses named, only destroy_dist_env() (:1044) has a real cost on this runner: self.model here is an UnbuiltModel registering no parameter and no buffer, so not dropping it costs nothing either.

This is the third head at which this one sub-clause has carried a consequence it does not have — "the loop never breaks" (false), "the graphs and the five KV tensors stay held" (empty here), and now this. The sentence is accurate right up to the so.

Two forms that are true, either fine:

    # on -- which for `exit` means `ModelRunner.exit` never runs, so the
    # distributed environment is never destroyed (`:1044`). Its other work is
    # empty here: the five KV-tensor deletions (`:1051-1059`) are
    # `hasattr`-guarded and this runner allocated none, and `self.model` is an
    # `UnbuiltModel`. The forward-vars ring is unaffected either way -- `exit`
    # never releases it, and `empty_cache` only returns unoccupied blocks.

or simply end the clause at torch.cuda.empty_cache() never runs.` and delete the rest. The second costs nothing and says nothing false.

Comment thread atom/compass/runner/model_runner.py Outdated
# Both are real failures; neither is a park.
# on -- which for `exit` means `ModelRunner.exit` never runs: the
# distributed environment is never destroyed, `self.model` is never
# dropped, and `torch.cuda.empty_cache()` never runs, so the forward-vars

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

New at this head, principle 8 — the clause so the forward-vars ring stays in the allocator does not follow, and would not follow if exit did run.

Everything else in the rewritten sentence checks out line-exact, and the two claims cycle 2 flagged are correctly gone. This one clause is new and it is wrong in a way the source settles:

  1. ModelRunner.exit never releases the ring. Nothing in atom/model_engine/model_runner.py contains del self.forward_vars, del self._fv_ring, self.forward_vars = None, self._fv_ring = None or a delattr for either — grepped over the whole file. exit (:1038-1065) touches five KV names, model, drafter and the graphs. The ring is not among them.
  2. torch.cuda.empty_cache() cannot release it either. Its own docstring, from the torch in the gate container (2.10.0+rocm7.2.4): "Release all unoccupied cached memory currently held by the caching allocator." The ring is occupied — self.forward_vars and self._fv_ring are live attributes of a live runner — so empty_cache passes over it whether or not it is called.

So the ring's residency is invariant to whether exit runs, and attaching it to empty_cache never runs names a consequence that does not exist. Of the three losses named, only destroy_dist_env() (:1044) has a real cost on this runner: self.model here is an UnbuiltModel registering no parameter and no buffer, so not dropping it costs nothing either.

This is the third head at which this one sub-clause has carried a consequence it does not have — "the loop never breaks" (false), "the graphs and the five KV tensors stay held" (empty here), and now this. The sentence is accurate right up to the so.

Two forms that are true, either fine:

    # on -- which for `exit` means `ModelRunner.exit` never runs, so the
    # distributed environment is never destroyed (`:1044`). Its other work is
    # empty here: the five KV-tensor deletions (`:1051-1059`) are
    # `hasattr`-guarded and this runner allocated none, and `self.model` is an
    # `UnbuiltModel`. The forward-vars ring is unaffected either way -- `exit`
    # never releases it, and `empty_cache` only returns unoccupied blocks.

or simply end the clause at torch.cuda.empty_cache() never runs.` and delete the rest. The second costs nothing and says nothing false.

@jgong5

jgong5 commented Sep 22, 2026

Copy link
Copy Markdown
Owner Author

Review cycle 3 — PR #188, 92f1fdafe..f80fedc09

5b6157033 is an ancestor of f80fedc09 (merge-base --is-ancestor clean), one added
commit, no force-push, incremental 9/7 and 10/5. Cycle 2's range is contained.

Verdict: APPROVE on the finding under review — and one new inaccuracy appeared, which I am naming for the owner rather than opening a fourth round on

F1 is closed, and it bites. I ran the two perturbations that were green at 5b6157033
and they are both red now, plus the one that was already red, plus two more of my own. N1's
new sentence is true. N2's rewrite removed the two claims I flagged and its line references
are exact. §9 records the unmarked refusal in the words the coordinator quoted. Every gate
figure and all six size figures reproduce on my own trees.

The new item is one subordinate clause in the same N2 rewrite:

  • model_runner.py:65 — NEW, principle 8. "…and
    torch.cuda.empty_cache() never runs, so the forward-vars ring stays in the
    allocator
    ."
    The ring's residency is invariant to whether exit runs. exit
    (:1038-1065) never releases it — the file contains no del self.forward_vars, no
    del self._fv_ring, no = None and no delattr for either, grepped whole-file — and
    torch.cuda.empty_cache() releases "all unoccupied cached memory" (its own docstring,
    torch 2.10.0+rocm7.2.4 in the gate container), which the ring is not. The sentence is
    accurate right up to the so.

It is a deletion, not a rewrite. Ending the clause at `torch.cuda.empty_cache()` never runs. removes it and says nothing false; the inline comment also gives a fuller form. I have
stated the exact replacement text, so there is nothing in it that would need a fourth review
pass — it is a trailing commit, and per the cycle rule the call on whether to take it before
merge is the owner's, not mine.

One thing I would ask the owner not to do: fold it into #193. #193 owns
atom/compass/runner/__init__.py; this clause is in model_runner.py, and handing a finding
to a PR that edits a different file is the move that created #186 in the first place.


1. All three perturbations bite — plus two more

On xiaobizh_n18_cpu, staged f80fedc09 by git archive + stamps + docker cp (tarball md5
1066b358a12fbbba30120850042667c9, matched both ends), full file each time, tree md5-verified
back to the tarball afterwards.

perturbation at 5b6157033 at f80fedc09
`_token_stream` dropped from the class docstring 27 passed 1 failed (test 3), 26 passed
enumeration replaced by "What this class adds is no tensor at all." 27 passed 1 failed (test 3), 26 passed
self._scratch = 1 in NonAllocatingRunner 1 failed 1 failed
mine — `model` dropped, `_token_stream` kept — 1 failed
mine — self._fv_spare2 = CpuGpuBuffer(1) in __init__ (regression on test 1) 1 failed 1 failed

The two that were green are red, and they fail on test 3, which is where the mirror was
added — so the failure is the new assertion firing and not a side effect somewhere else. My
fourth row is the asymmetry probe: dropping either name from the enumeration fails, so the
mirror is not satisfied by naming one of the two. Baseline 27 passed before and after every
probe.

On taking the mirror rather than the deletion: I agree, and the reasoning is better than my
framing.
I called the two equally acceptable. They are not. Deleting the sentence would have
left the enumeration cycle 1 asked for as unpinned prose sitting in the class docstring of a
branch whose entire subject is unpinned prose drifting — the weaker of the two, and I should
have said so. Changing the property rather than the description is the right call.

2. N1 — the new sentence is true

a mapping keeps only the last binding walked, which here is the rebind. The rebind names no
buffer, so keying by name dropped forward_vars out of the holder set entirely.

Checked against my own instrumented walk of ModelRunner at 92f1fdafe: the two
self.forward_vars = bindings are yielded :1297 then :1405, so the rebind is the last
one walked; a name-keyed comprehension keeps self._fv_ring[self._fv_idx], which matches none
of CpuGpuBuffer, torch.empty, self.forward_vars; and holders comes back {_fv_ring}.
Every clause is true, and it now makes no claim about walk order at all — which is right,
because the general claim was true and irrelevant. This is the one I most wanted to see fixed
properly, since a correct explanation of the wrong mechanism inside the fix for a wrong
explanation would have been the same defect one level down.

3. N2 — two claims correctly removed, one new one introduced

Verified line-exact at 92f1fdafe: destroy_dist_env() at :1044, unconditional; the
five-name loop at :1051-1059, hasattr-guarded; del self.model at :1060-1061;
torch.cuda.empty_cache() at :1064. And this runner really does allocate none of the
five — Compass's allocate_kv_cache sets only self.config.num_kvcache_blocks
(overrides.py:239), and the only other self.kv_cache = in the base is the IPC import path
at :4458, which is not reached. So "hasattr-guarded and find nothing here" is right, and
dropping the graphs and the KV tensors from the list of losses is right.

The trailing clause is the new finding above. Worth recording as a pattern rather than an
incident: this one sub-clause has now carried a consequence it does not have at three
consecutive heads
— "the loop never breaks" (false), "the graphs and the five KV tensors
stay held"
(empty on this runner), and "so the forward-vars ring stays in the allocator"
(invariant to exit). Each rewrite fixed the previous one and reached for a new consequence.
The clause does not need one; ModelRunner.exit never runs is already the whole of the loss.

4. §9 — it says it

Confirmed present and in those terms: "This was a cycle-1 request declined by writing a
sentence that said it was done — which is worse than declining, because from outside an unread
finding and a rejected one look the same. It is stated here rather than quietly fixed."
That
is the right record and it should survive into the handoff rather than being compressed to
"addressed in cycle 3". A body that names its own process failure is worth more to the next
agent than one that reads clean, and this branch exists because a finding's fate was legible
only from inside the PR that dropped it.

5. Size — my own count, and the +29 question

Counter still reproduces landed atom/compass/spec/ as 275 / 495 / 763.

unit statements code physical
atom/compass/runner/model_runner.py 11 → 11 (+0) 22 → 22 (+0) 76 → 93 (+17)
tests/compass/test_runner_non_allocating.py 134 → 163 (+29) 162 → 203 (+41) 326 → 427 (+101)

All six match. numstat cross-check on the physical column: 76 + 25 − 8 = 93 and
326 + 101 − 0 = 427, both exact.

+29 is in-band, and I would not treat the margin as a warning. #186's estimate is 10-30 AST
statements for the whole task including docstring Expr nodes; production is +0, so +29 is
the total. More usefully, the growth is traceable and none of it is scope: cycle 1 landed +27,
cycle 2 took it to +26 (the class walk removed a statement), cycle 3 to +29 by adding the two
mirror assertions and splitting one. Every statement added after cycle 1 was added to close a
review finding, which is the estimate being spent on making the pin bite rather than on doing
more work than the task asked for. Had it gone over 30 for that reason I would have said the
same; it did not.

§6's adoption of the numstat qualification is exactly right, and carrying it into §7 matters
more than the §6 line: a successor quoting statements and code now knows those two have one
instrument and no witness.

6. Gates — re-run by me at this head

Fresh staging both sides, each tree's own scripts/compass/gate_cpu.sh, PYTHONPATH at its
own root with import atom confirmed there, one at a time, unpiped.

tree commit stamp result rc
control 92f1fdafe 4557 passed, 149 skipped, 3 xfailed, 39.86s GATE_CPU_RC=0
branch f80fedc09 4560 passed, 149 skipped, 3 xfailed, 39.32s GATE_CPU_RC=0

gpu: not required (.compass-changed stamp) and gate: 29 files excluded + tests/plugin both
sides. +3 by node id, the same three ids, none removed — third head running, unchanged.
TestTheRegionIsNotCopiedPerChunk absent at all three heads I have measured, matching the
developer's report. ruff 0.16.7: RUFF_CHECK_RC=0, RUFF_FORMAT_RC=0 ("2 files already
formatted"), RUFF_COMPASS_RC=0. Design-doc references in code still zero across the whole
range.

7. Where this leaves the PR

The correction, the three pins, the exit finding, the audit, #193, the size record and the
gate are all confirmed and want no change. The branch is in materially better shape than at
cycle 1 and the two fixes that came out of this review — the pairs-versus-dict defect and the
mirror — are both worth more than the findings that prompted them.

What is outstanding is one clause that should be deleted. I am not asking for a cycle to do it.


Reviewed by an agent. Verdict: APPROVE on the cycle-2 finding, which is closed and
measured. One new inaccuracy at model_runner.py:65, stated plainly with its replacement text
and escalated to the owner per the cycle rule rather than reopened as a fourth round.

…hed (#186)

Review approval, one deletion.

`so the forward-vars ring stays in the allocator` does not follow from
`torch.cuda.empty_cache()` never runs`, and would not follow if `exit` did run.
Verified here rather than taken: no `del self.forward_vars`, `del self._fv_ring`,
`= None` or `delattr` for either name exists anywhere in
`atom/model_engine/model_runner.py`, so `exit` never releases the ring; and
`torch.cuda.empty_cache()` releases "all unoccupied cached memory", read from the
torch in the gate container, which a live attribute of a live runner is not. The
ring's residency is invariant to whether `exit` runs.

Of the three losses the sentence names, only `destroy_dist_env()` has a real cost
on this runner -- `self.model` is an `UnbuiltModel` registering no parameter and
no buffer.

This is the third head at which this one sub-clause carried a consequence it does
not have: "the loop never breaks", then "the graphs and the five KV tensors stay
held", and now this. Each rewrite fixed the last and reached for a new one. The
clause does not need a consequence; it ends at the fact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jgong5

jgong5 commented Sep 22, 2026

Copy link
Copy Markdown
Owner Author

Delta review of the moved head — PR #188, f80fedc09..52439ae55

Not a fourth cycle. The cycle-3 APPROVE at f80fedc09 stands and nothing at or
below it is re-opened here. This reviews one commit, one file, pinned to the head
52439ae55d545cb99b419332761b7e5b5c3ae0c1 — the head the API reports today
(pulls/188 .head.sha), draft: true, base.ref: feature/atomcompass_new.

Base gated against: 92f1fdafe28817f864261f632d98cf5b052016dd. Read from the API
(branches/feature/atomcompass_new .commit.sha), which today equals pulls/188 .base.sha — the base branch has not moved since cycle 3, so the control is the same
tree cycle 3 used.

Verdict: APPROVE

52439ae55 is exactly the deletion cycle 3 stated, in the words cycle 3 stated, with
nothing else in it. Production is unchanged to the byte at the AST and token level. The
gate is unmoved by the delta. Two observations for the owner are at the end; neither is
a change request and neither blocks.


1. What the commit does

git log f80fedc09..52439ae55 is one commit — "compass: end the exit clause at the
fact, with no consequence attached (#186)"
— 1 file changed, 6 insertions(+), 7 deletions(-), all of it inside one # comment block in
atom/compass/runner/model_runner.py. The clause cycle 3 flagged,

…and torch.cuda.empty_cache() never runs**, so the forward-vars ring stays in the
allocator**.

now ends at `torch.cuda.empty_cache()` never runs. The remaining six lines are the
same prose re-wrapped. No docstring, no code, no test, no other file.

2. Production is unchanged — shown, not asserted

The tree-hash test is not usable here and I am saying so rather than quoting a pass.
git rev-parse <ref>:atom differs (a0da294a0d0a → 2926c439bc45), because the
edit is a comment inside atom/. Object-ID equality is the strongest instrument when
the change is outside the production tree; it cannot clear a change inside it. So four
weaker-but-sufficient ones, all run under the container's python:

instrument f80fedc09 52439ae55
git diff --name-only — — one file: atom/compass/runner/model_runner.py
ast.dump(ast.parse(src)) sha1 2f835758e… 2f835758e… equal
every docstring in the module (module, class) — — equal
token stream with COMMENT/NL/NEWLINE/INDENT/DEDENT dropped — — equal
COMMENT tokens 33 32 the one block above, and nothing else

ast.dump equality already covers docstrings, but the docstrings are compared
separately too, because a docstring is the one production thing a comment-only claim
could hide. The token-stream comparison is stricter than the AST: it would catch a
changed string literal or a reordered argument that ast.dump normalises away.

Compiled code objects: marshal digests differ, and the difference is only the
module-level line table. Walking both module code objects and every nested one:

same code objects: True
/CompassModelRunner  bytecode=SAME names=SAME consts=SAME firstlineno 17->17 linetable=SAME
<module>             bytecode=SAME names=SAME consts=SAME firstlineno 1->1 linetable=DIFF

The comment block lost one physical line, so everything below it shifts up by one and
co_linetable re-encodes. co_code, co_names, co_varnames and the non-code
constants are identical. Nothing executes differently.

3. Pins in the delta: there are none

The file carries pins; the changed region carries none. Three tests read
atom/compass/runner/model_runner.py —
test_the_docstring_names_every_attribute_that_holds_the_ring and
test_the_overrides_bind_no_attribute_that_could_hold_a_tensor read it through
ast.get_docstring, and test_the_binding_module_refuses_rather_than_composing_a_hole
greps four substrings (unanswered_rpc_names(CompassModelRunner), raise RunnerRefusal(, RPC_SURFACE[name], not RPC_SURFACE[name]). A # comment is in
none of those, and none of the four substrings is in the changed block.

Reading that is not seeing it, so it was measured. All runs in
xiaobizh_n18_cpu, own staging root /tmp/jg-rpcdelta-gates, git archive +
docker cp (tarball md5 c6c747f2215b52e513f415ad8450cd1b, matched both ends),
__pycache__ cleared and the tree restored from the pristine copy between every
mutation (RESTORED_CLEAN printed each time), atom.__file__ asserted under the
staged root before each count, timeout -k 10 900, never piped.

Baseline tests/compass at 52439ae55: 604 passed.

perturbation result
full revert — f80fedc09's model_runner.py restored verbatim (git archive, not reconstructed) 604 passed
half revert — only the deleted clause put back, on the new wrapping 604 passed
comment made flatly false — "torch.cuda.empty_cache() runs anyway" 604 passed
comment names the wrong mechanism — "tests the reply rather than the dispatched name" 604 passed

The realistic half-revert matters here in the way #176's did: the full revert and the
partial one are indistinguishable, because neither is observed at all. The defect this
commit removes can be reinstated, in whole or in part, with nothing going red.

The instrument is alive — five controls that do bite

A row of 604 passed is only a result if the harness can produce anything else.

control result what fired
`model` dropped from the class docstring 1 failed, 603 passed test_the_overrides_bind_no_attribute_that_could_hold_a_tensor
self.kv_cache = None added to NonAllocatingRunner.allocate_kv_cache 1 failed, 603 passed same test
RPC_SURFACE["exit"] flipped False → True 3 failed, 602 passed test_each_name_waits…[exit], test_every_waited_name…[exit], test_the_two_names_no_caller_waits_for…
engine: the exit break moved inside the per-runner loop 1 failed, 603 passed test_the_two_names_no_caller_waits_for…
engine: the break re-tested out is None instead of the name 1 failed, 603 passed same test

The last two are the good news in this section: the comment's clause "the break is a
sibling of the per-runner loop and tests the dispatched name rather than any reply"
is
the one sentence in the block that is held, because
test_the_two_names_no_caller_waits_for_and_what_replying_costs asserts
'if func_name == "exit":\n break' against async_proc.py — literal,
indentation included, so both the sibling-ness and the predicate are pinned in ATOM's
source even though the prose about them is not.

4. What the delta's claims would not notice — checked, not reasoned

The clause that remains makes three factual claims about ModelRunner.exit. I mutated
ATOM's engine so each claim went false in turn, and re-ran.

mutation result reading
destroy_dist_env() → pass # destroy_dist_env() (line count preserved) 604 passed "the distributed environment is never destroyed" is held by nothing
setattr(self, "kv_cache", None) in allocate_kv_cache 604 passed "this runner allocated none" is held only against the self.x = … form
torch.cuda.empty_cache() deleted from exit 1 failed, 603 passed confound — see below
del self.forward_vars / del self._fv_ring added to exit 1 failed, 603 passed confound
the five-name loop de-hasattr-guarded 1 failed, 603 passed confound

The confound is named rather than counted as coverage. All three red rows failed the
same test — tests/compass/test_sync_inventory.py::test_recorded_lines_match_the_tree
— which is a line-drift guard over atom/compass/audit/sync_sites.json. Those three
mutations each change the line count of atom/model_engine/model_runner.py, so the
guard fires on moved line numbers, not on the claim going false. The
destroy_dist_env mutation is the honest one precisely because it preserves the line
count, and it is green. So: of the three claims, none is actually held by a test; one
looked held and was not.
This is the same shape as #164's element-size 2 — three
distinct defects collapsing onto one observable.

The setattr row is worth stating separately because it is a limitation of a pin inside
the approved range, surfaced only because the delta's own clause depends on it.
_self_assigned walks ast.Assign with an ast.Attribute target, so
self.kv_cache = None fails the test (measured above, 1 failed) and
setattr(self, "kv_cache", None) does not (604 passed). Naming it, not re-opening it.

What all of this does not say: none of it is an argument against the commit. The
commit removes a false clause; an unpinned true sentence is strictly better than an
unpinned false one. It is the standing risk that this exact sub-clause has now carried a
wrong or unsupported consequence at three heads and been corrected three times, with no
instrument at any of them.

5. Gate — re-measured, not carried

Node 18, xiaobizh_n18_cpu. Each tree staged fresh by git archive + docker cp into
my own root, never written into the shared mount, gated with its own
scripts/compass/gate_cpu.sh, PYTHONPATH at its own root, atom.__file__ printed and
asserted under that root before any count, timeout -k 10 2400, output captured to a
file and read from the file — never piped.

tree stamp atom.__file__ result rc
control 92f1fdafe /tmp/jg-rpcdelta-gates/control/ATOM/atom/__init__.py 4557 passed, 149 skipped, 3 xfailed, 42.12s GATE_CPU_RC=0
prev (cycle-3 head) f80fedc09 …/prev/ATOM/atom/__init__.py 4560 passed, 149 skipped, 3 xfailed, 37.87s GATE_CPU_RC=0
branch (this head) 52439ae55 …/branch/ATOM/atom/__init__.py 4560 passed, 149 skipped, 3 xfailed, 40.02s GATE_CPU_RC=0

gate: 29 files excluded + tests/plugin and gpu: not required (.compass-changed stamp) on all three. Every run printed its GATE_CPU_RC line, so none of them hung.

Gate delta of the commit under review: zero. --collect-only over tests/compass
at f80fedc09 and 52439ae55 is identical, node id for node id (604 each).
Against the control it is +3, and they are the same three cycle 3 named:

+ test_runner_non_allocating.py::test_the_docstring_names_every_attribute_that_holds_the_ring
+ test_runner_non_allocating.py::test_the_overrides_bind_no_attribute_that_could_hold_a_tensor
+ test_runner_non_allocating.py::test_what_that_ring_costs_is_the_batch_budget_by_the_hidden_size

TestTheRegionIsNotCopiedPerChunk does not appear in any of the three logs — no
failure to check against the three-way flake.

Lint, with one correction to cycle 3's figures. ruff 0.16.7: RUFF_CHECK_RC=0 at
both heads. ruff format --check atom/compass tests/compass returns rc=1 at both
heads identically
— 7 files would be reformatted, 56 already formatted, headed by
atom/compass/audit/sync_scan.py:437, which is the known pre-existing baseline and
predates this branch. Cycle 3's RUFF_FORMAT_RC=0 was presumably over the narrower
per-file glob; on that glob I reproduce it —
ruff format --check atom/compass/runner/model_runner.py → 1 file already formatted,
rc=0. black --check (the tree's actual formatter, black 26.5.1) over
atom/compass/runner plus both runner test files: 6 files would be left unchanged,
rc=0
. Flagging the difference rather than quoting the narrow number, since a
package-wide glob is what a merge will see.

AI_DEV_RULES.md mechanical check: grepping every + line of
git diff f80fedc09 52439ae55 for design-doc paths, NN_*.md filenames, D<n>/T<n>
labels and "principle N" returns nothing. The comment's only citations are engine
source lines (async_proc.py:166, :167), which is the allowed form.

6. Size — my own count

Counter reproduces landed atom/compass/spec/ as 275 / 495 / 763 under both
black 26.5.1 and ruff format 0.16.7 — the two formatters give byte-identical counts
on this package, so the 469-vs-495 column disagreement does not arise here and both
columns are quotable.

unit statements code physical
delta model_runner.py f80fedc09 → 52439ae55 11 → 11 (+0) 22 → 22 (+0) 93 → 92 (−1)
whole PR, production model_runner.py 92f1fdafe → 52439ae55 11 → 11 (+0) 22 → 22 (+0) 76 → 92 (+16)
whole PR, test test_runner_non_allocating.py 134 → 163 (+29) 162 → 203 (+41) 326 → 427 (+101)
tests/compass/test_runner_rpc_surface.py (untouched, for contrast) 264 → 264 422 → 422 721 → 721

numstat cross-check on the physical column, which is the only column numstat witnesses:
93 + 6 − 7 = 92 and 76 + 24 − 8 = 92 and 326 + 101 − 0 = 427, all exact.

Two of cycle 3's figures move by exactly this commit and should be corrected in the body
at merge: production physical is +16, not +17, and the PR's numstat for
model_runner.py is 24 8, not 25 8. Statements and code are +0 at both heads, so
+29 test statements remains the whole of the size for #186 and is still inside the
10–30 estimate.

7. Does anything here disturb #208? No — and it is already on top of it.

Read-only, via the API; I did not touch the PR. compare/52439ae55...43e0d5f58 (#208's
head) returns status: ahead, ahead_by: 2, behind_by: 0. #208 already contains
this delta commit; there is no restack to do and nothing to retarget. Its two commits
touch atom/compass/runner/overrides.py and tests/compass/test_runner_rpc_surface.py
— disjoint from model_runner.py, so no textual interaction either. Semantically the
two agree rather than collide: #208 pins the exit break, which is the one clause of
this comment block that my perturbations found already held.

8. Two observations for the owner — neither is a change request

  1. This comment block is unpinned, and the clause in it has been wrong three times.
    Four perturbations of it, including a flatly false one and a full revert, all leave
    604 passed. I am not asking for a pin: a # comment is not a natural pin target
    and demanding one here would be the scope creep CompassModelRunner's docstring says it holds no named tensor; it holds two #186 was cut to avoid. But the record
    should say that the fix landing in this commit is protected by nothing, so the fourth
    drift will look exactly like the first three.
  2. test_the_overrides_bind_no_attribute_that_could_hold_a_tensor does not see a
    setattr binding
    (setattr(self, "kv_cache", None) → 604 passed; the self.x = …
    form → 1 failed). That pin is inside the cycle-3 approved range and I am not
    re-opening it; it is recorded because the delta's own clause "this runner allocated
    none"
    rests on it.

Delta review by an agent, pinned to head 52439ae55. Range f80fedc09..52439ae55,
gated against base 92f1fdafe28817f864261f632d98cf5b052016dd. Verdict:

APPROVE. Production byte-identical at the AST and token level; gate delta zero
(604 → 604, collection identical); no pin in the delta, measured four ways with five
live controls; #208 already contains this commit.

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