Skip to content

compass(spec): explain refuses an unknown term as ADDRESSING and an absent one as TOTALITY - #264

Merged
jgong5 merged 2 commits into
feature/atomcompass_newfrom
compass/explain-absent-term-split
Sep 23, 2026
Merged

jgong5 merged 2 commits into
feature/atomcompass_newfrom
compass/explain-absent-term-split

Conversation

@jgong5

@jgong5 jgong5 commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Closes #259

What changed

explain(spec, term) raised one SpecRefusal(Rule.SHAPE, …) whenever the basis came back empty. Neither case it covered is a document to correct, so neither is SHAPE, and its remedy (a re-ask) is wrong for one of them. It is now split:

case before after
a term that is not a field, a block or a quantity (explain(spec, "device.clock_ceiling")) SHAPE: the document has the shape the schema declares: this spec carries nothing named 'device.clock_ceiling'. name a field or a block of fields by its dotted path, or one of the quantities ['admission', 'collective', 'kv_blocks', 'kv_transfer'] ADDRESSING: a field is asked for by the path of the field itself: `device.clock_ceiling` is not a field, a block of fields or a quantity this schema knows. ask again by the whole dotted path of a field or a block of fields, or by one of the quantities ['admission', 'collective', 'kv_blocks', 'kv_transfer']
a declared optional field the spec does not state (explain(spec, "provenance.notes")) the same SHAPE message, with 'provenance.notes' TOTALITY: a spec resolves every field the document was required to state: `provenance.notes` is declared by this schema as optional, and this spec states no value for it. write it in the document if a reader needs it; a field the document leaves out is left out here rather than invented
a declared required field missing from a spec built with the public MachineSpec(values, tokenizers) constructor (explain(assembled, "host.ipc")) the same SHAPE message TOTALITY: …: `host.ipc.zmq_roundtrip_s` is declared by this schema, and this spec carries no value for it. a spec read with `MachineSpec.from_mapping` resolves every required field, so this one was assembled from parts; merge the fragment that measures this field before asking for it
a field the spec holds that yields no rows: host.tokenizers: [] on a read spec the same SHAPE message no refusal: an empty Basis (host.tokenizers, from spec <digest>), matching spec.value("host.tokenizers") == ()

The change is in atom/compass/spec/explain.py:

  • An empty paths means the schema does not know the term, so that check runs first and raises ADDRESSING.
  • If the term is known but no path under it is in spec.values, it calls refuse_absent_field(paths[0], BY_PATH[paths[0]].required).
  • Otherwise it builds the rows, and zero rows is an answer, not a refusal.

Decisions the brief did not cover

  • TOTALITY reuses refuse_absent_field; there is no second TOTALITY site. It is the refusal MachineSpec.value already gives, which is how compass(spec): name which of the two ways a path failed to resolve #183 split value(). So for a declared field, explain(spec, p) and spec.value(p) give the same answer: byte-identical text when absent (asserted), and an answer from both when present, including the empty tokenizer table (asserted). They do disagree on an undeclared key that is a deployment knob: explain(spec, "block_size") is ADDRESSING, while spec.value("block_size") is SEPARATION ("remove it; EngineArgs.block_size carries it"). explain puts nothing in a document, so "remove it" would be the wrong remedy there. The one new site is the ADDRESSING one in explain.py. The TOTALITY site is the existing optional/required pair in rules.py, and the placement test pins explain's refusal to it.
  • A required field that is absent is handled, not only an optional one. A spec built with the public constructor MachineSpec(values, tokenizers) can lack a required field. test_kv_simulated_connector.py builds one that way. validate's own stand-in spec never leaves validate, so it does not reach explain. The field's declaration decides which of the two messages applies. A message hard-wired to say "optional" would have been false for this case.
  • A field that is present but yields no rows is not refused. This was the review finding. host.tokenizers: [] reads through from_mapping, and value() returns (). Refusing it as absent would claim the spec was assembled and ask for a fragment, which is false on every count. explain returns an empty basis instead: it truthfully says that no tokenizer was measured. The guard checks presence, not the row count, so the field refuse_absent_field names is always really absent.
  • A term naming several fields is refused for its first field. A block's fields follow SCHEMA order; a quantity's follow its own QUANTITIES tuple. On a spec that was read, only provenance.fragments and provenance.notes can be absent, so nothing is hidden there. On an assembled spec, the other absent fields under the term go unnamed. That belongs to the follow-up below.

Partition

  • The site left ELSEWHERE. test_every_site_that_declines_a_document_is_driven_here still drives only SHAPE sites, and now guarantees that neither new case can drift back to SHAPE (M4 below).
  • New: test_explain_declines_an_unknown_term_and_an_absent_one_at_two_sites. It places the unknown-term site in explain.py under ADDRESSING, and the absent-term site at the accessor's own site under TOTALITY. Neither may be under SHAPE.
  • The resolver, loose-mention and allowlist guards pass unchanged.

Named result: each case has its own test with its own rule and remedy, and each test bites

The runs were on node 18 (xiaobizh_n18_cpu), on the merged tree 2a1435be8 (tip 175739f87 + head 47e40e05d), over tests/compass/test_spec_schema.py and tests/compass/test_spec_verbs.py. Every tree was staged by git archive + docker exec -i tar (md5 matched on both ends), and atom.__file__ resolved under each staged root. Every mutation keeps the line counts: explain.py 209, rules.py 176.

variant mutation result failing node ids (tests/compass/…), and the assertion each failed on
tip 175739f87 none 281 passed none
N1 none (merged tree) 285 passed none
N2 docstring word 285 passed none
M1 swap both rules, messages kept 4 failed test_spec_schema.py::test_explain_declines_an_unknown_term_and_an_absent_one_at_two_sites (unknown in _sites_naming("ADDRESSING")); test_spec_verbs.py::test_a_term_the_schema_does_not_know_is_asked_for_again_by_path (rule is Rule.ADDRESSING); test_spec_verbs.py::test_an_optional_field_the_document_left_out_is_refused_as_absent and test_spec_verbs.py::test_a_block_an_assembled_spec_holds_nothing_under_names_its_first_field (rule is Rule.TOTALITY)
M1a unknown term → TOTALITY only 2 failed the placement test; test_a_term_the_schema_does_not_know_is_asked_for_again_by_path
M1b rules.py absent-field rule → ADDRESSING (both branches) 7 failed the placement test (absent in _sites_naming("TOTALITY")); both absent-case verbs tests; and four #183 tests: test_a_declared_field_a_spec_lacks_is_never_called_undeclared, test_the_three_refusals_are_not_interchangeable, test_an_optional_field_the_document_omits_is_refused_as_optional, test_the_other_accessors_decline_on_a_fragment_rather_than_raise
M2 swap the two remedies, rules kept 4 failed the placement test (the site moved); the three verbs tests on "ask again by the whole dotted path", "write it in the document" and "merge the fragment"
M3f .required → False 1 failed test_a_block_an_assembled_spec_holds_nothing_under_names_its_first_field ("merge the fragment")
M3t .required → True 2 failed the placement test (site rules.py:144 ≠ :152); test_an_optional_field_the_document_left_out_is_refused_as_absent ("as optional")
M4 unknown term → SHAPE 3 failed test_spec_schema.py::test_every_site_that_declines_a_document_is_driven_here, the placement test, test_a_term_the_schema_does_not_know_is_asked_for_again_by_path
M5 the guard refuses on zero rows again: if not any(_rows(...) for p in paths if p in spec.values) 1 failed test_spec_verbs.py::test_an_empty_tokenizer_table_explains_to_no_rows_rather_than_refusing (raises SpecRefusal)

Gates

Gate 1 (scripts/compass/gate_cpu.sh, node 18, one run at a time, timeout -k 10 1500, output not piped):

tree passed / skipped / xfailed GATE_CPU_RC
control: tip 175739f87 5107 / 149 / 3 0
merged 2a1435be8402fa2e8f6255df38d7aa0f93f1b326 = git merge-tree --write-tree 175739f87 47e40e05d (rc=0) 5111 / 149 / 3 0
  • Net +4, from the junit XML of both runs:
    • removed: tests/compass/test_spec_verbs.py::test_a_term_this_spec_carries_nothing_for_is_refused
    • added: tests/compass/test_spec_schema.py::test_explain_declines_an_unknown_term_and_an_absent_one_at_two_sites, tests/compass/test_spec_verbs.py::test_a_term_the_schema_does_not_know_is_asked_for_again_by_path, tests/compass/test_spec_verbs.py::test_an_optional_field_the_document_left_out_is_refused_as_absent, tests/compass/test_spec_verbs.py::test_a_block_an_assembled_spec_holds_nothing_under_names_its_first_field, tests/compass/test_spec_verbs.py::test_an_empty_tokenizer_table_explains_to_no_rows_rather_than_refusing
    • outcome changed: none. Neither timing class in test_stream_marker_properties.py fired.
  • Each tree was gated with its own scripts/compass, with .compass-commit and .compass-changed written. The printed commit: stamps were 175739f87 and 47e40e05d. The staging was removed afterwards.
  • The previous round, at head da6f12036 against tip 815a08679, gave 5056 → 5059, RC 0/0.

Gate 2: four verbs tests and one placement test, CPU-only. ruff check and black --check give rc=0 on all three files at the merged tree.

Lines (non-blank, against the tip):

estimate actual
production ~40 +19 / −8 in explain.py: 11 lines of code (1 of them the changed import) and 8 lines of docstring
test ~60 +48 / −3: test_spec_verbs.py +41/−2, test_spec_schema.py +7/−1

What surprised me

  • "No rows" is not "no field". _rows returns nothing for a tokenizer table that is present but empty. The first head refused that case with a message whose every clause was false. The reviewer caught it, and M5 now pins it.
  • The only block where a read spec can have nothing present is under provenance: every other block has at least one required field.
  • black in the CPU container uses a line length above 88.

Left undone (out of this brief)

  • A quantity or block that is only partly present on an assembled spec still explains silently, from the rows it has. The reviewer measured it: with device.memory.capacity_bytes dropped, kv_blocks returns 12 rows and no refusal. The same follow-up should decide whether a refusal lists every absent field under the term. The coordinator is filing it.

🤖 Generated with Claude Code

…bsent one as TOTALITY

explain(spec, term) raised one SHAPE refusal whenever the basis came back
empty. That folded two cases together, and neither of them is a document to
correct:

- A term that is not a field, a block or a quantity is now refused under
  ADDRESSING, and the remedy says to ask again by a real dotted path.
- A term the schema declares, with nothing under it that this spec holds,
  is now refused through refuse_absent_field, the same TOTALITY refusal
  MachineSpec.value gives for that field. An optional field the document
  left out is told to be written in the document; a required one missing
  from a spec built from parts is told to merge the fragment that measures
  it. Neither is told to ask again, because any spelling finds the same
  absence.

The site leaves ELSEWHERE in the SHAPE partition, and a new test places the
explain sites under ADDRESSING and TOTALITY.

Closes #259

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment thread atom/compass/spec/explain.py Outdated
"name a field or a block of fields by its dotted path, or one of "
f"the quantities {sorted(QUANTITIES)}",
)
refuse_absent_field(paths[0], BY_PATH[paths[0]].required)

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.

Blocking (principle 6): this refusal fires on a spec that does carry the field, and every clause of its text is then false.

not contributions is not the same test as "no path under the term is in spec.values". A field can be present and still produce zero rows: _rows returns [] for host.tokenizers when the table has no entries. An empty tokenizer list passes check (it is a Sequence) and table(), so MachineSpec.from_mapping reads it.

Measured on node 18 (xiaobizh_n18_cpu), on the head's explain.py, with the spec built by MachineSpec.from_mapping(written("host.tokenizers", [])) from tests/compass/test_spec_schema.py:

read ok; host.tokenizers in values: True ()
value(): ()
[host.tokenizers] TOTALITY: a spec resolves every field the document was required to state: `host.tokenizers` is declared by this schema, and this spec carries no value for it. a spec read with `MachineSpec.from_mapping` resolves every required field, so this one was assembled from parts; merge the fragment that measures this field before asking for it

So:

  • the spec was read with from_mapping, not assembled from parts;
  • it carries a value, and spec.value("host.tokenizers") returns ();
  • the remedy ("merge the fragment") cannot be acted on.

That also breaks the PR's central claim that explain(spec, p) and spec.value(p) cannot disagree about one path. Here one answers and the other refuses.

Before this PR, the site gave the generic SHAPE text for this case. The PR made the message specific, and that specific message is untrue here.

Asked for:

  • Refuse as absent only when nothing under the term is in spec.values, and name a field that really is absent. For example, test if not any(path in spec.values for path in paths). Every path is then absent, so paths[0] is honest.
  • Decide the empty-table case explicitly: either an empty Basis, which says truthfully that no tokenizer was measured, or a refusal under the rule that owns tokenizers.
  • Add one test that drives explain on a read spec whose tokenizer list is empty.

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.

Fixed in 47e40e05d. The guard now reads if not any(path in spec.values for path in paths) and runs before the rows are built. So refuse_absent_field fires only when every path under the term is absent, and the field it names really is absent.

The empty-table case is decided explicitly: an empty Basis, not a refusal. host.tokenizers: [] is held, and value() answers (). explain now answers with zero rows (host.tokenizers, from spec <digest>), which says truthfully that no tokenizer was measured. A refusal under the tokenizer rule would be a new contract for a spec that from_mapping accepts, so I left it out.

The new test tests/compass/test_spec_verbs.py::test_an_empty_tokenizer_table_explains_to_no_rows_rather_than_refusing drives this on a read spec. It asserts value() == (), contributions == () and the printed header. It is pinned by M5, which restores refuse-on-zero-rows in one line: if not any(_rows(...) for p in paths if p in spec.values). M5 reddens that test and nothing else.

@@ -183,15 +190,18 @@ def explain(
for field in SCHEMA
if field.path == term or field.path.startswith(f"{term}.")

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.

Non-blocking (principle 7): naming only the first field is deterministic and true, but it hides how many others are missing.

Deterministic: yes. A block's paths follow SCHEMA order. A quantity's paths follow the order of its own QUANTITIES tuple, not schema order. The PR body says "in schema order", which is correct for blocks only. Please fix that sentence.

Honest: the named field really is absent, since contributions is empty (subject to the tokenizer case on L206). Every field in the schema except provenance.fragments and provenance.notes is required. So on a spec that was read, this line only ever names one of those two single fields, and nothing is hidden there.

What the caller does not learn: on an assembled spec, every field under the term is absent, but the refusal names one. For kv_blocks, the seven fields come from more than one fragment. The caller merges the fragment for field 1 and asks again. explain then returns a partial basis without refusing: this is the "left undone" item. I measured it on node 18 with device.memory.capacity_bytes dropped from a resolved spec: kv_blocks returns 12 rows and no refusal. So the other N−1 absences are never shown, first because only field 1 is named and then because the partial basis is silent.

Keeping refuse_absent_field byte-identical to value() is the right trade here (principle 3). I would not widen the message in this PR. The follow-up for the partial-basis case should also decide whether a refusal lists every absent field under the term.

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.

Agreed, and the PR body is corrected. A block's paths follow SCHEMA order, and a quantity's follow its own QUANTITIES tuple. The code is unchanged here: the message stays byte-identical to value()'s. Whether a refusal should name every absent field under a term goes to the partial-basis follow-up, alongside the silent partial kv_blocks basis you measured.

@jgong5

jgong5 commented Sep 23, 2026

Copy link
Copy Markdown
Owner Author

Review, cycle 1: REQUEST_CHANGES at head da6f12036146e610d9ef8989d7360ba1f3713c96

Verdict: REQUEST_CHANGES. There is one blocking finding, and it is small: about two lines of code and one test. It is inline at explain.py L206: the new TOTALITY text fires on a spec that was read and carries the field (host.tokenizers: []). Every clause of the text is then false (principle 6). Everything else below is accepted. With that fixed, I expect to approve.

I read the eight design principles in atom/compass/design/README.md and AI_DEV_RULES.md first.

Rulings

1. One new site, not two: accepted (principle 3).

  • Reusing refuse_absent_field is the right reading of the compass(spec): the version refusal asks for a reader, not an edit #212 ruling and of compass(spec): name which of the two ways a path failed to resolve #183's value() split. The ruling asks for a TOTALITY refusal whose remedy is not a re-ask. The accessor already builds exactly that, with the optional/required split. A second copy in explain.py would be a second place for the two texts to drift apart.
  • The brief's phrase "two new sites" described the two cases, not two new pieces of code.
  • The partition is right:
    • the explain site left ELSEWHERE;
    • the unknown-term site is explain.py:194 under ADDRESSING;
    • the absent-optional site is rules.py:152 under TOTALITY, which is value()'s own site;
    • neither is under SHAPE.
  • The resolver guard, the loose-mention guard and _built_where_thrown are unaffected. The new call is by the bare name SpecRefusal, and the reused one is an existing site.
  • Does …is_driven_here drive both? No, and it should not. It drives the SHAPE sites only, and neither new case is SHAPE any more. What it now guarantees is that neither case can drift back to SHAPE. I checked that directly with mutation M4 (unknown term → Rule.SHAPE). It reddens test_spec_schema.py::test_every_site_that_declines_a_document_is_driven_here, test_spec_schema.py::test_explain_declines_an_unknown_term_and_an_absent_one_at_two_sites and test_spec_verbs.py::test_a_term_the_schema_does_not_know_is_asked_for_again_by_path. Both new cases are placed by the new placement test.
  • Reservation: the placement test pins only the optional branch (rules.py:152). The required branch (rules.py:144) is pinned by the verbs test alone. M3t shows the placement test still tells the two apart, so this is adequate.

2. Required field on an assembled spec: in scope, and the message is true. Correct the PR body.

  • Handling it is in scope. The old site covered it too, and hard-coding required=False would have printed "declared … as optional" for a required field (principle 6). Passing the declared flag costs one argument.
  • The message is true for the case it names: a spec read by from_mapping cannot lack a required field.
  • The PR body's reason is wrong, though. It says "validate builds one from resolved values". It does, but validate.py:388-391 keeps that stand-in to itself ("it never leaves this function"), so explain never receives it. The case is reachable only through the public constructor MachineSpec(values, tokenizers), as in this test and in test_kv_simulated_connector.py:327. Please fix that sentence in the dev record.
  • The inline L206 finding is the one place where this message is not true.

3. First field only: deterministic and honest, with a reservation (principle 7). Details are inline at L191.

  • It is deterministic. Blocks follow SCHEMA order. Quantities follow their own tuple's order, not schema order as the PR body says.
  • It is honest: the named field really is absent.
  • On a spec that was read, only provenance.fragments and provenance.notes can be named, because every other field is required, so nothing is hidden there.
  • On an assembled spec, the caller learns nothing about the other N−1 absent fields. Once the named field is merged, the partial basis from item 6 hides them silently.
  • Not blocking. Keeping the accessor's text byte-identical is worth more here. The follow-up issue should cover this.

4. Message truth: all three messages, read whole (node 18, merged tree).

  • ADDRESSING: a field is asked for by the path of the field itself: device.clock_ceiling is not a field, a block of fields or a quantity this schema knows. ask again by the whole dotted path of a field or a block of fields, or by one of the quantities ['admission', 'collective', 'kv_blocks', 'kv_transfer']. The action is a re-ask, which matches the rule. The headline says "field" while the remedy also allows a block or a quantity; that is the rule's fixed sentence, and I accept it.
  • TOTALITY, optional: the same text as value(), byte-identical (asserted). The remedy is a document edit, which is correct for an optional field.
  • TOTALITY, required: true for a hand-assembled spec, and false for a read spec with an empty tokenizer list (the blocking finding).
  • Accepted with reservation: explain(spec, "block_size") is ADDRESSING, while spec.value("block_size") is SEPARATION ("remove it; EngineArgs.block_size carries it"). explain puts nothing in a document, so "remove it" would be wrong there, and I agree with the choice. It does mean the PR body's "explain and value cannot disagree about one path" holds for declared fields only. Please scope that sentence.

6. Left undone: yes, file it as a follow-up issue. Not this PR (principle 5).

  • Measured: dropping device.memory.capacity_bytes from a resolved spec leaves explain(spec, "kv_blocks") returning 12 rows with no refusal. That is a basis silently missing a term, which is a principle 6 fallback.
  • It predates this PR (compass(spec): merge, validate and explain over the machine spec (SPEC-2) #86), and changing it changes explain's contract beyond this brief.
  • One correction: the PR body calls it "out of this file set". It is not: explain.py is the file set. It is out of the brief.
  • The follow-up should also cover item 3, whether a refusal lists every absent field under the term.

7. Design-doc references: none, in code, comments, test names or messages.

Removed test, test_a_term_this_spec_carries_nothing_for_is_refused: no coverage is lost. Its call (explain(resolved(), "device.clock_ceiling")) and its "kv_blocks" in remedy assertion survive in test_a_term_the_schema_does_not_know_is_asked_for_again_by_path, which also asserts the rule, the what and the remedy. The removed ELSEWHERE entry's call (explain(read(), "no_such_term")) is now driven by the placement test.

Pins, reproduced (node 18, xiaobizh_n18_cpu)

Setup:

  • Staged from the merged tree bc801abae by git archive and docker exec -i tar.
  • Staged content hash ff081b28… on both ends.
  • atom.__file__ resolved under each staged root.
  • explain.py stayed at 207 lines and rules.py at 176 lines in every variant.

The runs cover tests/compass/test_spec_schema.py and tests/compass/test_spec_verbs.py, 284 tests.

variant mutation result failing node ids (tests/compass/…)
N1 none 284 passed none
N2 docstring word, L38 284 passed none
M1 L195 ADDRESSING→TOTALITY; L206 → inline SpecRefusal(Rule.ADDRESSING, <optional text>) 4 failed test_spec_schema.py::test_explain_declines_an_unknown_term_and_an_absent_one_at_two_sites, test_spec_verbs.py::test_a_term_the_schema_does_not_know_is_asked_for_again_by_path (TOTALITY is ADDRESSING), test_spec_verbs.py::test_an_optional_field_the_document_left_out_is_refused_as_absent (ADDRESSING is TOTALITY), test_spec_verbs.py::test_a_block_an_assembled_spec_holds_nothing_under_names_its_first_field
M1a only L195 swapped 2 failed the placement test; test_a_term_the_schema_does_not_know_is_asked_for_again_by_path
M1b only the absent half: rules.py L145/L153 TOTALITY→ADDRESSING, texts kept 7 failed the placement test, both new absent-case verbs tests, and four #183 tests (test_a_declared_field_a_spec_lacks_is_never_called_undeclared, test_the_three_refusals_are_not_interchangeable, test_an_optional_field_the_document_omits_is_refused_as_optional, test_the_other_accessors_decline_on_a_fragment_rather_than_raise)
M2 L198 remedy → the optional remedy; L206 → inline SpecRefusal(Rule.TOTALITY, <optional what>, "ask again by…") 4 failed the placement test (('explain.py', 206) == ('rules.py', 152)), plus the three verbs tests on their remedy assertions: 'ask again by the whole dotted path' in …, 'write it in the document' in … and 'merge the fragment' in …
M3f L206 .required→False 1 failed test_spec_verbs.py::test_a_block_an_assembled_spec_holds_nothing_under_names_its_first_field ('merge the fragment' in …)
M3t L206 .required→True 2 failed the placement test (('rules.py', 144) == ('rules.py', 152)), test_spec_verbs.py::test_an_optional_field_the_document_left_out_is_refused_as_absent ('is declared by this schema as optional' in …)
M4 L195 ADDRESSING→SHAPE 3 failed test_spec_schema.py::test_every_site_that_declines_a_document_is_driven_here, the placement test, test_a_term_the_schema_does_not_know_is_asked_for_again_by_path

This matches the developer's M1, M2 and M3. M3t adds that the optional branch is pinned too.

Gate 1: the tree that will land

  • The integration tip, re-read at the start of this review, is 815a0867981a07156a3bfffcc162246e4844bc2c, unmoved.
  • git merge-tree --write-tree 815a0867981a07156a3bfffcc162246e4844bc2c da6f12036146e610d9ef8989d7360ba1f3713c96 gave bc801abaef5303382eb78e78b6e214a33027ac3d, rc=0. That equals the head tree, so it agrees with the developer.
  • scripts/compass/gate_cpu.sh was run once on that tree, on node 18 (xiaobizh_n18_cpu):
    • it used the tree's own scripts;
    • stamps: .compass-commit = the head, .compass-changed = the three changed files;
    • atom.__file__ = /tmp/pr264rev/n1/ATOM/atom/__init__.py;
    • bounded by timeout -k 10 1500, unpiped.
  • The gate printed commit: da6f12036 (stamp) and then 5059 passed, 149 skipped, 3 xfailed, GATE_CPU_RC=0, in 175 s. The load average on the box was about 96.
  • This agrees with the developer's branch run: +3 against the control's 5056.
  • The junit XML confirms that the four new node ids are present and the removed one is absent. No test_stream_marker_properties.py flake fired.
  • Staging was removed afterwards.

For the next task in this area

  • explain's "no rows" is not "no field". _rows can return nothing for a field that is present, and that is the case to test whenever this refusal is touched again.
  • The follow-up issue (partial basis, and naming every absent field) is recommended above. I have not filed it; that is the coordinator's call.

No other blocking issues.

… term is held

The absent-field refusal fired whenever a term produced no rows. A field
that is present can produce none: a tokenizer table read with no entries.
There explain called a spec that was read "assembled from parts" and
disagreed with MachineSpec.value, which answers ().

The refusal now fires only when no path under the term is in the spec, so
the field it names really is absent. A held field with nothing in it
explains to an empty basis, and a test drives that on a read spec.

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

jgong5 commented Sep 23, 2026

Copy link
Copy Markdown
Owner Author

Developer round 1: response to REQUEST_CHANGES at da6f12036

New head: 47e40e05d. It is one commit on top of da6f12036, with no force-push.

Blocking finding (explain.py L206): fixed.

  • refuse_absent_field now fires only when no path under the term is in spec.values.
  • A held field that yields no rows, such as host.tokenizers: [], now explains to an empty Basis. That agrees with value(), which returns ().
  • New test: tests/compass/test_spec_verbs.py::test_an_empty_tokenizer_table_explains_to_no_rows_rather_than_refusing.
  • Details are in the inline reply.

PR body corrections: all four made.

  1. The required-field case is reachable only through the public MachineSpec(values, tokenizers) constructor. validate's stand-in never leaves validate.
  2. The claim "explain and value agree" is now scoped to declared fields, and the block_size ADDRESSING/SEPARATION difference is stated.
  3. Quantities follow their own tuple order; blocks follow schema order.
  4. The partial-basis item is out of the brief, not the file set. It is left for the coordinator's follow-up.

Pins. Run on node 18 against the merged tree 2a1435be8 (tip 175739f87 + head 47e40e05d), over the two spec test files. Line counts were preserved: explain.py 209, rules.py 176.

variant result failing node ids
tip 281 passed none
N1, N2 (null controls) 285 passed each none
M1 4 failed the placement test and the three case tests, each on its rule is assertion
M1a 2 failed the placement test; test_a_term_the_schema_does_not_know_is_asked_for_again_by_path
M1b 7 failed the placement test, both absent-case verbs tests, and the four #183 tests
M2 4 failed the placement test (site moved), and the three remedy assertions
M3f 1 failed test_a_block_an_assembled_spec_holds_nothing_under_names_its_first_field
M3t 2 failed the placement test; test_an_optional_field_the_document_left_out_is_refused_as_absent
M4 3 failed test_every_site_that_declines_a_document_is_driven_here, the placement test, test_a_term_the_schema_does_not_know_is_asked_for_again_by_path
M5 (refuse on zero rows again) 1 failed test_an_empty_tokenizer_table_explains_to_no_rows_rather_than_refusing

Full node ids and the assertion each failed on are in the PR body.

Gate 1 (node 18, stamped, the tree's own scripts, atom.__file__ under each staged root, one run at a time, unpiped):

  • The tip 175739f87 gave 5107 / 149 / 3, GATE_CPU_RC=0.
  • The merged tree 2a1435be8402fa2e8f6255df38d7aa0f93f1b326 gave 5111 / 149 / 3, GATE_CPU_RC=0.
  • Node ids: 1 removed (test_a_term_this_spec_carries_nothing_for_is_refused), 5 added, 0 outcome changes. No timing flake fired.
  • ruff and black rc=0.

Lines vs estimate:

  • production +19/−8 against ~40;
  • test +48/−3 against ~60.

No other blocking issues known. Ready for review cycle 2.

@jgong5

jgong5 commented Sep 23, 2026

Copy link
Copy Markdown
Owner Author

Review, cycle 2: APPROVE at head 47e40e05d1819c85ce9ee1a1bed9f6aa6575bbc2

Verdict: APPROVE. No blocking issues. This cycle reviews the delta da6f12036..47e40e05d only. The eight design principles were read first.

1. The fix closes the cycle-1 finding (principle 6)

The guard if not any(path in spec.values for path in paths) now runs before the rows are built. So refuse_absent_field fires only when every path under the term is absent, and the field it names, paths[0], really is absent. The text "this spec carries no value for it" is therefore true whenever it prints.

Does the new predicate have its own gap? I measured this on node 18, on the merged tree.

  • Which fields can be present and still yield zero rows? Only the TOKENIZERS field and the two WIDTH_TABLE fields (device.runtime_constants.driver_and_collective_reserve_bytes and …allocator_retained_after_load_bytes).
    • check refuses an empty width table, so a read spec cannot hold one.
    • An assembled spec can hold {}. It explains to 0 rows, and value() returns {} for it, so the two still agree.
  • A block whose fields are all present but empty: none exists. Every block holding a zero-row-capable field also holds ordinary fields. So on a spec that was read, the only term that can explain to zero rows is host.tokenizers itself.
  • A quantity mixing present and absent fields: it still answers from the part it has. That is compass(spec): explain answers from part of a quantity when the rest is absent #265's scope, as agreed, and not this PR's.

The empty-table ruling: honest, not a silent answer.

  • Principle 6 forbids a guessed answer. The empty Basis states exactly what the spec holds: a tokenizer table with no entries, which is also what value() returns (()).
  • It fabricates no value.
  • Absence now always refuses, so zero rows can only mean "present, and nothing measured". The empty Basis is not ambiguous.
  • A consumer that needs a rate still gets refused at tokenizer_for.
  • A refusal under the tokenizer rule would add a new contract for a spec that from_mapping already accepts. Declining to add it is the right call under principle 3.

Reservation, for #265 rather than here. On that same spec, explain(spec, "admission") prints 3 rows and no line at all for host.tokenizers. A reader of the quantity cannot tell that the tokenizer contribution was empty rather than left out. That is the partial-quantity question, so I suggest adding this case to #265.

2. PR body corrections: all four verified

  1. The required-field case is described as reachable through the public MachineSpec(values, tokenizers) constructor, and the body says validate's stand-in never leaves validate.
  2. The claim that explain and value agree is scoped to declared fields, and the block_size ADDRESSING / SEPARATION difference is stated.
  3. The body says a block's fields follow SCHEMA order and a quantity's follow its own tuple.
  4. The partial basis is described as "out of this brief". compass(spec): explain answers from part of a quantity when the rest is absent #265 is open for it.

3. Pins, reproduced (node 18, xiaobizh_n18_cpu)

  • The trees were staged from the merged tree 2a1435be8 by git archive and docker exec -i tar. The content hash was 859f26ed… on both ends.
  • explain.py stayed at 209 lines in every variant, and atom.__file__ resolved under each staged root.
  • Each run covered tests/compass/test_spec_schema.py and tests/compass/test_spec_verbs.py, 285 tests.
variant mutation (L203 unless stated) result failing node ids
N1 none 285 passed none
N2 docstring word, L38 285 passed none
M5 if not any(_rows(spec, path, tp_width, origin) for path in paths if path in spec.values): (refuse on zero rows again) 1 failed tests/compass/test_spec_verbs.py::test_an_empty_tokenizer_table_explains_to_no_rows_rather_than_refusing, raising the cycle-1 false TOTALITY text
M6 if False: (guard removed) 3 failed tests/compass/test_spec_schema.py::test_explain_declines_an_unknown_term_and_an_absent_one_at_two_sites, tests/compass/test_spec_verbs.py::test_an_optional_field_the_document_left_out_is_refused_as_absent, tests/compass/test_spec_verbs.py::test_a_block_an_assembled_spec_holds_nothing_under_names_its_first_field

M5 matches the developer's result: that one test fails and nothing else does. M6 is my addition. It shows the new guard is pinned in the other direction too: without it, an absent term would come back as an empty basis.

Gate 1: the tree that will land

  • The tip was re-read before and after the gate: 175739f87fc53bf24190315641f00b4a07535400, unmoved.
  • git merge-tree --write-tree 175739f87 47e40e05d1819c85ce9ee1a1bed9f6aa6575bbc2 gave 2a1435be8402fa2e8f6255df38d7aa0f93f1b326, rc=0. This is the same as the developer's.
  • One run of scripts/compass/gate_cpu.sh with these settings:
    • the tree's own scripts;
    • stamps written;
    • atom.__file__ = /tmp/pr264rev2/n1/ATOM/atom/__init__.py;
    • bounded by timeout -k 10 1500;
    • unpiped.
  • The gate printed commit: 47e40e05d (stamp) and then 5111 passed, 149 skipped, 3 xfailed, GATE_CPU_RC=0, in 391 s, at a load average of about 150.
  • This agrees with the developer's 5111, which is +4 over the tip's 5107.
  • The junit XML shows test_an_empty_tokenizer_table_explains_to_no_rows_rather_than_refusing present and test_a_term_this_spec_carries_nothing_for_is_refused absent.
  • No test_stream_marker_properties.py flake fired.
  • Staging was removed afterwards.

Both inline threads from cycle 1 are addressed by 47e40e05d.

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