Repository navigation
An annotation at scope end names nothing, and it has main red - #10425
Conversation
main has been failing the required-ci parse phase since #10390 (11:48Z). Every pull request that test-merges main fails required-witnesses-floor with: required-ci: FAILED PHASE parse (16 error(s)) required-ci: FAILED PHASE namespace-wave-admission (no head index) All 16 are one file. #10390 deleted the three workflow-subject rows at the end of test.claim.emit_copy_qualification_witness_test and left their explanatory block behind as a TRAILING epilogue at lines 483-500, with no module item after it. DESIGN section 4c admits only a leading block attached to a module-scope declaration: an annotation names the declaration that FOLLOWS it, so at scope end it names nothing. AnnotationAttachmentRefusal::UnattachedAtScopeEnd is exactly that refusal, and it fires once per line of the block. The second failure is not independent. claim_executor pushes "namespace-wave-admission (no head index)" only when the parse phase produced no index, so the wave never ran at all. One root, two reported blockers. WHY THIS SURFACED LATE, since #10390 landed hours before anything went red and a reader will otherwise suspect a different cause. The parse wall is not new and #10325 only changed which receipt arm carries blockers. The last green run on the old tree, #10358's 33869134455, was CREATED at 11:28 -- twenty minutes BEFORE #10390 merged -- so its merge ref predates the breakage and it never parsed these lines. The first runs to test-merge the broken main were the ones after it. THE REPAIR MOVES THE BLOCK TO THE MODULE HEAD, where the imports that follow give it a subject. Every sentence is preserved. The deictic words are not: "stood here" becomes "stood at the END OF THIS MODULE", "the rows above this comment" and "the mutants below" become "in this module", because a relocated pointer that still says "below" is a false citation of the kind this repository files as a_live_authority_name_carries_a_superseded_claim. A trailing paragraph records why the block sits at the head, so the next author does not move it back. Nothing else changes: no row, no assertion, no rung drop. The content already has a typed home at gunbc.rung_drop emit_copy_qualification_without_a_consumer, and this commit does not touch it. EVIDENCE, executed on this tree rather than argued: before required-ci: FAILED PHASE parse (16 error(s)) required-ci: FAILED PHASE namespace-wave-admission (no head index) after parse phase clean, no parse FAIL lines required-ci: namespace-wave-admission ADMITTED -- every delta is auto-admitted or named by a transition admission Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G
briansrls
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES on exact head 46fc682.
The root diagnosis and the one-file remedy are otherwise accepted. Current main carries a standalone leading annotation block after every module item, so it reaches EOF with no following subject and produces UnattachedAtScopeEnd once per line; namespace-wave's no head index is downstream of that parse failure, not an independent admission-roster defect. Moving the block rather than deleting it is correct, and the rewritten deictic wording no longer points "above" or "below" from a new location. The 255 consumed-at-base #10358 admissions are not due in this PR because this PR does not touch their roster.
One source-placement blocker remains. The relocated file currently starts:
module test.claim.emit_copy_qualification_witness_test
// block...
That blank line is load-bearing. std.source_annotation.module_header_gap_subject attaches post-module prose to the module root only when the block begins immediately after the module line; preceded_by_blank_line deliberately disables that arm and sends the block through ordinary nearest-following attachment. On this head the module-wide block therefore attaches to import std.measure { byte_size }, not to the module it repeatedly describes. The parse becomes green, but the annotation acquires the wrong subject—a plausible wrong answer instead of the previous refusal.
Repair narrowly: delete the blank line between the module declaration and the first // line. Keep the blank line after the block before the imports. That makes the existing post-module-header rule bind the whole consecutive block to the module root. No prose redesign, row change, or roster edit is requested.
One body-only evidence correction is also required. GitHub records run 33869134455 as created at 11:41:28Z, not 11:28Z; #10390 merged at 11:48:31Z. The causal conclusion remains correct—the run predates the breaking merge—but the interval is about seven minutes, not twenty. Correct the timestamp/duration without moving the source head.
Run 33884160310 is nonterminal. No merge authorization until the placement is repaired, the body timestamp is truthful, a replacement exact-head review is filed, and exact-head CI succeeds.
The previous head made the parse green and gave the annotation the WRONG
SUBJECT, which is worse than the refusal it replaced -- a plausible answer
standing where a typed refusal used to be is the failure DESIGN section 5
forbids outright, and I shipped it while claiming the fix was verified.
std.source_annotation module_header_gap_subject binds a post-module block
to the MODULE ROOT only when the block opens immediately after the module
line. Its first arm is
if preceded_by_blank_line || preceded_by_annotation_line { none }
so a blank line between `module ...` and the first `//` deliberately
disables the module-root arm and sends the block through ordinary
nearest-following attachment. On the previous head that bound this
module-wide block to `import std.measure { byte_size }` -- an import it
never describes -- instead of to the module it describes throughout.
Removed that blank line. The blank line AFTER the block, before the
imports, is kept: it ends the block. Nothing else changed.
WHAT I HAD AND DID NOT USE. My evidence was "parse clean, wave ADMITTED".
Both were true and neither says anything about WHICH SUBJECT the annotation
acquired. A greener instrument reading is not evidence about the property I
was actually changing, and I generalized from it anyway.
EXECUTED: dag/test/claim/source_annotation_attachment_witness_test 13/13
PASS on this tree, and the parse phase reports zero parse FAIL lines.
COVERAGE GAP, NAMED NOT FIXED HERE. No enrolled witness covers
module_header_gap_subject's blank-line arm -- the exact rule that silently
mis-bound this block. The attachment battery covers general leading,
trailing, body-grain and block-splitting cases, but nothing discriminates
module-root attachment from nearest-following attachment across that one
bit. That witness belongs in its own change; this PR is a fleet unblocker
for a red main and is deliberately staying one file wide.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G
#10425, #10408 and #10428 each deleted the 19-line annotation #10390 orphaned at EOF and each added its own relocation. Every repair was correct alone; landing together they left the same paragraph in the module three times, and no check can see it because all three parse. Copy C also inverted its own claim -- "no row in this module establishes nothing about a running system" -- a double negative asserting the opposite of the intended sentence. Keeps the module-head copy, which is the only one whose deictics were rewritten to name the module rather than point at "here"/"below", the only one with correct polarity, and which already carries the relocation account the others state separately. Deletes the other two and the blank line that would otherwise double up. Declaration set identical; the only non-comment change is that blank. Parsed both arms with the local gunbc against main's own copy as control -- both parse, both return true. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PGkDt1W1m3U28vBpV6BY9Z
Main now carries the annotation repair (#10425, b4dfec3), so this branch stops inheriting the fleet-wide parse breakage. THE GENERATED-ARTIFACT ROUTE IS NOT THE ONE I FOLLOWED ON THE PREVIOUS TWO MERGES. #10383 replaced it: step 1 used to say stage the driver-left ours bytes as provisional. It now says take the BASE side verbatim (git checkout <base-ref> -- docs/design-failure-modes.md) and explicitly NOT to stage the worktree bytes, because those are the ours side and committing them writes this branch's rows over the base's, deleting every row the base added since the merge base. So this merge takes origin/main's projection verbatim. My own row is deliberately ABSENT from it -- the base does not have it yet, and heal derives the merged projection from the merged authorities. VERIFIED BY SET DIFFERENCE, WHICH IS WHAT THE ROUTE DEMANDS AND NOT BY COUNT: every row identity in the base projection is still present after the checkout, difference empty. A count would say a number moved; only the difference names WHICH rows went dark, and the name is the finding. I did NOT check for conflict markers and conclude anything from their absence. The driver GUARANTEES marker-free bytes on a refusal, so a marker count reports that the driver worked, never that its subject is intact. Authority after merge: 105 roster entries. The projection is knowingly behind that until heal derives it. Unrelated observation, not fixed here: .githooks/generated-artifact-merge emits `line 13: ([a-z0-9_]*): command not found` twice while printing the route. The route text still renders and the refusal still fires, so it is cosmetic, but the sed capture group in that message is being evaluated by the shell. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G
… the infer mirror from the merged authority Second integrate-main cycle on this branch. #10350 was CONFLICTING, which is not merely a merge chore: GitHub cannot compose refs/pull/N/merge for a conflicting PR, so no pull_request event is built and ZERO runs are created -- measured, total_count=0 for head 5250ea6. The stale composed tree GitHub kept serving (still carrying #10390's orphaned annotation, long after #10425 repaired it on main) is a SYMPTOM of the same missing ref, not a second defect. Both end here. dag/gunbc/recurring_failure_mode/roster.dag -- UNION, and union is correct here for a reason worth stating, because the same resolution authored a real defect on main tonight. This roster is an append-only SET whose entries are independent rows, so keeping both sides preserves two unrelated appends. The duplicate annotation preambles now sitting in main came from union-resolving a PROSE BLOCK, where "keep both sides" mints a second authority for one statement. Same resolution, opposite correctness, and what decides it is whether the file is a set or a narrative. Verified at row identity rather than by count: HEAD 102 rows, main 105, merged 107 = exactly the union, zero dark from either side, zero invented, and every roster entry has a matching import. src/v1/stage0/src/v1_compiler_infer.rs -- NOT hand-resolved. It is a generated mirror and #10402 landed on it while this branch moved resolved_node_is_kernel_identity_for_name out of the infer_env import block. The authority src/v1/04_infer.dag auto-merged clean, so the mirror was taken base-side and RE-DERIVED from the merged authority by --required-regen (planned=156 executed=156 adjudicated=156, drift reported on exactly lib.rs and this file). The check that a hand merge cannot pass: the derived mirror DIFFERS FROM BOTH PARENTS -- 87 changed lines against this branch, 59 against main. Had either side's delta been dropped it would have come out identical to one of them. dag/test/claim/emit_copy_qualification_witness_test.dag needed no resolution and got none: this branch has no changes to it, and the merged worktree copy is byte-identical to main's 565-line repaired file. Verified on the merged tree: cargo clippy --all-targets -- -D warnings clean, and compiler_tests::a_resource_item_is_not_read_as_a_type_item green against a test binary built AFTER the mirror was re-derived (checked by mtime, since an unchanged cargo metadata hash does not mean an unchanged binary). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vz8ESEVFC7Cox1xAaXudg
…deictics that no gate can see (#10434) `dag/test/claim/emit_copy_qualification_witness_test.dag` on main holds THREE copies of the same block, at lines 2, 74 and 99. Only the first is #10425's repair. The other two are the PRE-REPAIR text, and their deictics are now false at their positions: "Three rows stood HERE ..." the rows were deleted by #10390 "calibration mutants BELOW ..." points at rows that are not below it "the rows ABOVE THIS COMMENT ..." points at rows that do not exist #10425 rewrote exactly those words for exactly this reason, recording that "a relocated pointer that still says below is a false citation of exactly the kind this repository files." Two stale copies then landed beside its corrected one. HOW IT ARRIVED, and it is not #10425's fault. Two PRs cut BEFORE the repair (#10408, #10376) carried their own copy of the block and landed after it. Neither contested a line, so the merge UNIONED rather than refused and both sides' bytes survived. This is `gunbc.recurring_failure_mode` `append_only_carrier_whose_serialization_shares_a_merge_region` on ordinary source rather than on a roster: THE UNIT OF MERGE IS COARSER THAN THE UNIT OF EDIT. WHY NOTHING CAUGHT IT. All three copies are leading blocks attached to declarations, so §4c is satisfied and the parse phase is silent. The defect we spent the afternoon on announced itself in 16 errors; this one is the same class, from the same commit family, and is invisible to every gate we have. A green main is not evidence this did not happen. This deletes copies B (74-91) and C (99-116) and keeps A at the module head — the one whose deictics name the module. Result: one copy, zero false deictics, no unattached block, and the file still ends at its last declaration. Claude-Session: https://claude.ai/code/session_01LSzWg5t7F22xEtbd83fzQ6 Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ith no subject CI refused with 21 parse errors and every one is mine, in this file, lines 376-400: "source annotation names no subject: no module item follows". WHEN I DELETED ray_pressure_monitor_is_factory_selectable I LEFT ITS ANNOTATION BLOCK STRANDED AT END OF FILE. Section 4c admits a standalone leading // block only where it attaches to a module-scope declaration; at scope end there is nothing to attach to, and the parser refuses rather than discarding it. So the deletion that removed a decoration created an unattached block, which is the same class I repaired in someone else's file in #10425. The block now sits ABOVE the factory section it is about, so it attaches to the declaration it describes rather than trailing the module. WHY MY LOCAL RUN DID NOT CATCH IT, WHICH IS THE PART WORTH KEEPING. I verified with claim_batch on the witness entry, which resolves that entry's CLOSURE. The parse phase reads the WHOLE CORPUS. A green closure run is not evidence the corpus parses, and I treated it as though it were -- so my evidence was true and did not cover the claim I made with it. The instrument that does cover it is v1_src_dag_parse, which now reports 4887 file(s) parse-clean, and which I should have run before pushing a module whose diff is mostly annotations. Witnesses 16/16, unchanged: this moves prose and touches no declaration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G
#10484) * Cite Ray's THRESHOLD memory monitor at the revision the fleet executes Third item in the operator's order. It was parked because I had the measured BEHAVIOUR of the incident but not the upstream constants, and writing env spellings and defaults from compacted memory into an extdeps citation is the fabrication section 5 forbids. A peer session read them from Ray's own source and from inside the serving containers, so they are grounded now. NAMED FOR THE IMPLEMENTATION, NOT THE FACTORY. The obvious name, extdeps.ray.memory_monitor, would have been a meaning fork: upstream has no single memory_monitor any more, but three implementations -- threshold, pressure, event -- behind memory_monitor_factory.h. One name over three materially different behaviours is what section 3 forbids, and a reader could not tell which one a cited default belonged to. The second reason is stronger: PressureMemoryMonitor is never returned by Create at all, so a module named for the directory would have implied coverage of a path upstream does not run. THE REVISION PIN IS LOAD-BEARING AND IT ALREADY CAUGHT AN ERROR. The defaults were first read from master, and master carries count_swap_in_memory_monitor -- a field that DOES NOT EXIST at 2.58.0. Written into a row pinned here it would have cited a knob absent from the version we run, in a shape no later reader could detect: real repository, real file, real revision, wrong about one field. The version is established from the running deployment (ray.__version__ inside the serving container), not assumed. The absence is now a ROW rather than an omission, because a field merely missing from a list is indistinguishable from one nobody looked for. It also matters here specifically: the Spark hosts carry 16 GiB of swap and are USING it, so whether the monitor counts swap is live rather than hypothetical -- and my own prior assumption that these hosts had no swap was wrong. THE ENV SPELLING IS DERIVED, NOT LISTED. Ray's pattern is RAY_ prefixed to the config field name verbatim, which is why the variables are lowercase rather than the SCREAMING_CASE an author would guess. Deriving it means a config row cannot drift from its own environment variable, and the documented disable -- a zero refresh interval -- is computed from the same row rather than authored beside it. THE PRINTED PERCENTAGE IS A TRAP AND IS RECORDED AS ONE. The monitor derives the displayed fraction as threshold_bytes / total_bytes, so it is a re-derivation and not the configured fraction echoed back; the two agree only when the byte threshold came from that fraction against the same total, which is exactly what a cgroup limit or an isolated slice changes. THE UNCAPTURABLE LINE IS NOT A MEASUREMENT, and that is the arm that matters for reading our own incidents: a reader who scans for the exceeded-threshold line and finds none has NOT established that memory was below threshold. The monitor may have been announcing it could not see. Absence of an alarm and evidence of safety are different facts. WHAT IS DELIBERATELY NOT MODELLED: where resource_isolation_enabled originates. It is an argument to Create, not a spelling, so its provenance is the caller's fact rather than this citation's, and guessing would put a gunbc-layer decision inside an extdeps row. Witnesses 11/11. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Read the factory's BODY: the zero interval disables the threshold arm, not memory enforcement Three corrections, all from a verbatim read of multi_memory_monitor_factory.cc at ray-2.58.0. Two are defects in what I opened an hour ago, and the second overstates in the direction a refusal must never overstate. 1. SELECTION DEPENDS ON CONFIG THE SIGNATURE DOES NOT NAME. I modelled selection from Create's parameter list -- kill_workers_callback, resource_isolation_enabled, cgroup_manager -- which omits the refresh interval entirely. The interval is read from the RayConfig GLOBAL inside the body: uint64_t monitor_interval_ms = RayConfig::instance().memory_monitor_refresh_ms(); so the function gave a wrong answer for the disable case, the case that matters most. A header's parameter list is not the set of inputs a function reads, and the module now cites the body. 2. A ZERO INTERVAL DOES NOT DISABLE MEMORY ENFORCEMENT. It swaps the THRESHOLD monitor for a NoopMemoryMonitor. The EVENT monitor is pushed BEFORE the interval is read, guarded only by resource_isolation_enabled, so isolation ON plus a zero interval builds Event and Noop together -- a live cgroup-driven monitor still killing workers. I had called that argv "disable the monitor", which would tell a caller a host is unenforced while something is enforcing on it. Renamed to ray_threshold_monitor_disable_env, and ray_built_monitors now returns the POPULATION so the two isolation settings give visibly different answers. 3. THE PLATFORM ARM IS DELETED, AND ITS DELETION IS THE RULE THIS MODULE ALREADY RECORDS. I carried a non-Linux Noop arm from a comment on MASTER's header; the 2.58.0 body contains no platform branch at all. Asserting it would have been a citation to a revision it was not read at -- exactly the failure the count_swap_in_memory_monitor absence row exists to record this module coming close to once. If the behaviour is real it can land later with a reading behind it. THE THREE-CAUSES PROBLEM DISSOLVES RATHER THAN NEEDING A MODEL. I had feared a constructed-but-never- scheduled state, indistinguishable from a live monitor seeing nothing. It does not exist: the zero interval swaps the OBJECT and logs on construction, naming the variable. So a silent threshold log separates into three DECIDABLE causes -- disabled line present, uncapturable line present, or neither -- and the disabled line is now a modelled arm. A constant-valued silence_is_evidence_of_safety predicate written on the way to this is deleted: every arm answered false, which no input can discriminate and a witness could only assert back at itself. Witnesses 15/15. The discriminating pair is the two isolation settings at a zero interval: ON still enforces, OFF enforces nothing. A model answering "disabled" for both fails the first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Partition the citation by source file, and stop presenting defaults as effective configuration Four findings from exact-head review 5119115399, all correct, and the fourth explains the first. 1. A master LITERAL SAT UNDER A 2.58.0 PIN. The ThresholdExceeded format string read "(%.1f%% of %dB limit)"; at the pinned tag it is "(%.1f%% of %dB total)". `limit` is master's spelling, associated with the newer swap-aware implementation. So the pin caught count_swap_in_memory_monitor contamination and then admitted the same class through a different field. 2. THE CITATION DID NOT COVER THE MODULE, WHICH IS WHY (1) SURVIVED FIFTEEN GREEN WITNESSES. One ExternalModelScope pointed at ray_config_def.h with empty further_citations, while the module asserted facts from the factory body and the monitor implementation as well. The witnesses compare authored values against authored expectations; nothing in them establishes external-source fidelity. Only a citation naming the file each fact came from can. The authority is now partitioned: ray_config_def.h for the defaults, multi_memory_monitor_factory.cc for ray_built_monitors, threshold_memory_monitor.cc for ray_monitor_log_format. 3. COMPILED DEFAULTS WERE PRESENTED AS THIS FLEET'S EFFECTIVE CONFIGURATION. I wrote that an empty RAY_ environment established the recorded values were "in force on this fleet" and that there was no deployment-layer choice. That entailment does not hold: Ray reads the environment AND THEN applies a JSON config list through RayConfig::initialize, so an empty environment rules out one input and not the other. The observation was real and the conclusion was larger than it -- the same shape as the other defects here. The fleet reading is removed from this module entirely; which defaults survive into an effective configuration is a join over an observed environment and an observed config list, and both are receipts about our deployment rather than facts about Ray, so they belong in gunbc.spark where an unobserved config list is a refusable coverage obligation rather than a silence that reads as zero. Also in (3): min_memory_free_bytes was enumerated as a config and consumed by the factory's threshold calculation while its default was absent from the defaults record -- a partial surface presented as a complete one. Its -1 is upstream's DISABLED marker, not a quantity, so it is an ARM: carried as a signed byte count, "no floor configured" and "a floor of minus one byte" would be the same value. 4. THE THREE-CAUSES CLAIM WAS STRUCTURALLY UNREPRESENTABLE, not merely overstated. The function takes a LINE while the safe case is the ABSENCE of any line, so a function over present texts can never represent "nothing was printed". Renamed to ray_line_explains_absent_threshold_alarm and narrowed to what it can decide: which lines, WHEN PRESENT, explain an absent alarm. Deciding BelowThreshold against ObservationFailed against Disabled needs a carrier for an observation OUTCOME -- a reader's fact about a captured log, not a property of a line -- so it belongs to whatever consumes the capture, and this module does not fabricate it. The isolated path's own diagnostic (UserSliceSnapshotFailed) is added, because the type claimed to enumerate the ways of seeing nothing and was missing one. Witnesses 17/17. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * State the Linux scope, and delete the third constant-shaped decoration Two things, both anticipating findings rather than waiting for them, and the second is a rule this module has now had to apply three times. THE SCOPE WAS LEFT IMPLICIT WHEN THE PLATFORM ARM WENT. Deleting the guessed non-Linux arm was right -- it came from a comment on master's header and the 2.58.0 body has no platform branch, so asserting it would have been a citation to a revision it was not read at. But the deletion left a reader of ray_built_monitors taking it for the whole story on every platform, when it is the LINUX story. What the factory does elsewhere is UNREAD, not known-to-be-the-same, and that distinction is the whole difference between a citation and a guess. Now said in the module. THE PRESSURE PREDICATE WAS A CHANGE DETECTOR AND IS DELETED. ray_pressure_monitor_is_factory_selectable took no arguments and returned false; its witness asserted the negation. No input discriminates a zero-argument constant, so that witness could only go red if someone edited the constant it reads. Worse than absent, because it would have been cited as coverage for a fact the TYPE already guarantees: RayBuiltMonitor has no pressure constructor, so a pressure monitor cannot be written into a built population at all. The state is unrepresentable, not merely refused -- construction over validation, and the check was the weaker restatement of the stronger guarantee. That is the third constant-shaped decoration removed from this module. The first was silence_is_evidence_of_safety, which answered false on every arm. The second was the disconnected vllm_observed_kv_pool_line in the neighbouring PR, which returned the enum value it was written to return. The rule that catches all three is one question asked before writing a check: is the check's RED authorable? For a constant it never is. Witnesses 16/16 -- one fewer than before, because a row was deleted rather than fixed, which is the correct direction when the row asserted something construction already holds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Restore the format slot, cite the macro and the master end of the contrast, evict the fleet facts Three residuals from exact-head review 5119153361. 1. THE ISOLATED-PATH STRING WAS NOT THE FORMAT. It read "Failed to take user and system slice memory snapshot." where the tagged source has "...snapshot due to: %s." The function is named ray_monitor_log_format, so it must carry the FORMAT; dropping the slot silently turned it into a prefix while keeping the name that promises otherwise. Restored rather than renamed, because the format is what a consumer parsing captured stdout needs. 2. THE CITATION PARTITION WAS STILL SHORT BY TWO. ray_config_env_spelling asserts an upstream proposition -- that the environment name is RAY_ prefixed to the config field name -- which comes from the macro in ray_config.h and had no scope at all. And ray_config_absences asserts a claim about TWO revisions: present on master, absent at the tag. Citing only the tag leaves half of it ungrounded, which is a poor state for the one row whose entire job is to record a master-versus-tag contamination this module has now suffered twice. Both ends are named: the absence row cites the tagged header first and a master authority as a further citation. 3. FLEET FACTS ARE OUT OF EXTDEPS. Three claims about OUR deployment were still sitting in the citation -- that the Spark hosts carry 16 GiB of swap and are using it, that the version was read inside a serving container, and that the hosts have no Ray. Every one is true and none belongs here. An extdeps row states which revision it cites; it does not state who observed it, where, or what that observer's hardware looks like. Those are receipts about our fleet and their home is gunbc.spark, where an unobserved input is a refusable coverage obligation rather than a sentence in a comment. The absence row now says why the distinction between "checked and missing" and "nobody looked" matters, and leaves why it matters TO US to the consumer. Witnesses 16/16. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Reattach the scope annotation: deleting the function left its prose with no subject CI refused with 21 parse errors and every one is mine, in this file, lines 376-400: "source annotation names no subject: no module item follows". WHEN I DELETED ray_pressure_monitor_is_factory_selectable I LEFT ITS ANNOTATION BLOCK STRANDED AT END OF FILE. Section 4c admits a standalone leading // block only where it attaches to a module-scope declaration; at scope end there is nothing to attach to, and the parser refuses rather than discarding it. So the deletion that removed a decoration created an unattached block, which is the same class I repaired in someone else's file in #10425. The block now sits ABOVE the factory section it is about, so it attaches to the declaration it describes rather than trailing the module. WHY MY LOCAL RUN DID NOT CATCH IT, WHICH IS THE PART WORTH KEEPING. I verified with claim_batch on the witness entry, which resolves that entry's CLOSURE. The parse phase reads the WHOLE CORPUS. A green closure run is not evidence the corpus parses, and I treated it as though it were -- so my evidence was true and did not cover the claim I made with it. The instrument that does cover it is v1_src_dag_parse, which now reports 4887 file(s) parse-clean, and which I should have run before pushing a module whose diff is mostly annotations. Witnesses 16/16, unchanged: this moves prose and touches no declaration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Put the Linux scope in the name, not in the paragraph that forbids the misreading Exact-head review 5119153361 held four residuals. Three were already repaired at this head -- the snapshot log line carries "due to: %s.", ray_config_env_spelling and ray_config_absences have their own ExternalModelScope rows, and the self-attesting pressure-monitor constant and its witness are deleted. The fourth stands and is fixed here. ray_built_monitors WAS A TOTAL NAME OVER A BODY READ ONLY FROM THE LINUX FACTORY. I had already noticed this and answered it with a paragraph saying "It is the Linux story". That is the same defect I argued against on the GB10 nameplate one hour earlier: a comment cannot forbid a use that the name invites, because the name is what every consumer spells and the comment is what no consumer reads. The declaration is now ray_linux_built_monitors and its scope row's decl_name follows, so the restriction is carried by the thing a caller cannot avoid typing. The paragraph is rewritten rather than deleted, because after the rename its old sentence was FALSE -- it warned that a reader would take the name for every platform, which the name no longer says. It now records why the scope moved from prose into the name, which is the part a later reader needs. ray_built_monitor_enforces keeps its total name deliberately: it is a property of the three monitor kinds, not of the platform-specific selection among them. Witnesses 16/16. v1_src_dag_parse: 4887 file(s) parse-clean -- run before pushing this time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Delete the floating master citation, and repair the pointer my own relocation falsified Three blockers from exact-head review 5119521858, all correct. 1. THE MASTER CONTRAST HAD NO IMMUTABLE SUBJECT. ray_config_absences asserted a field is present on master AND absent at the tag, with a second authority pointing at blob/master/... to ground the positive half. But master is not a revision -- it is whichever commit occupies the branch when a reader follows the link -- so the cited end could move while this authored row stayed fixed. That is the same undetectable wrongness the tag pin exists to prevent, re-entering through the CITATION instead of through the defaults, in the one module that had already been bitten by it twice. I took the delete arm rather than pinning a master commit, because the commit I actually read is not recoverable now and reconstructing a plausible one would be a fabricated citation, not a repair. The row keeps only the half grounded at a pinned revision -- absent from ray_config_def.h at ray-2.58.0 -- which is also the only half a consumer needs. further_citations is now empty. 2. MOVING THE ANNOTATION FIXED ATTACHMENT AND BROKE DEIXIS. The block I relocated to repair the parse failure opens "EVERYTHING ABOVE IS READ FROM THE LINUX FACTORY PATH". That was true where it was written and FALSE where I put it: above it now sit the ray_config_def.h defaults, the ray_config.h env spelling, and the threshold_memory_monitor.cc diagnostics, none of them factory material. So my parse repair traded a refused program for a silently wrong sentence. It now names the two declarations it governs instead of a region, which is the DESIGN section 3 rule about citing symbols and not positions -- a region pointer is invalidated by any edit that moves either end, and that is exactly what my own edit did. 3. THE WITNESS ANNOTATION CLAIMED MORE THAN THE FUNCTION DECIDES. It said a silent threshold log "separates into three decidable causes". The function consumes a RayMonitorLogLine, so it classifies only lines that are PRESENT; the no-line case is not one of its inputs and cannot be one of its verdicts. Narrowed to the executable claim: among the modeled lines, exactly the three non-measurements explain an absent alarm. Deciding what total silence means needs evidence the monitor was consulted at all, which is a fleet receipt and stays downstream. Witnesses 16/16. v1_src_dag_parse: 4887 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Two witness annotations: one kept a deleted fact alive, one miscounted its own arms Both from exact-head review 5119561850, both annotation-only, both real. 1. THE DELETED MASTER PROPOSITION SURVIVED IN WITNESS PROSE. The previous commit removed ray_config_master_authority and the "present on master" half of the contrast from the model, on the ground that master has no immutable subject. The witness annotation above w_the_swap_counting_knob_is_recorded_as_absent_at_this_revision went on asserting exactly that sentence. So the deletion moved the fact rather than removing it, and left it in the layer with the weakest citation discipline -- which is the failure the deletion was meant to close, reappearing one file over. Section 4c is explicit that an annotation is never evidence a machine claim holds, and the converse bites here: prose the compiler cannot read is also prose no gate will catch drifting. The annotation is now tag-only, matching the row it sits above: verified absent from ray_config_def.h at ray-2.58.0, prompted by an earlier unpinned read, claiming nothing about what master carries. 2. THE NARROWED ANNOTATION MISCOUNTED THE ARMS. I wrote "the two measurement lines do not" while ray_line_explains_absent_threshold_alarm returns false on THREE: ThresholdExceeded, NodeMemoryAboveThreshold, UserSliceMemoryAboveThreshold. The witness negates all three. Corrected to three. Worth noting the shape: I introduced this number in the same commit that narrowed the annotation for overclaiming, which is a transcribed count sitting next to the function that derives it -- the thing DESIGN section 6 says to name rather than copy. Neither change touches the model, the citation partition, the factory construction, the default carriers, or any witness body. Witnesses 16/16. v1_src_dag_parse: 4887 file(s) parse-clean -- re-run because these comments participate in parsing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Consume the Ray citation where the serving decision is made, and delete the flag that disagreed with it The operator asked what consumes this module. Nothing did. The only importer of extdeps.ray.threshold_memory_monitor was its own witness test; every other hit was the module citing itself. That is specification-without-execution: the witnesses execute, but they exercise the module against itself, so no serving decision changed if the citation was wrong and none broke if it were deleted. The brief said "extdeps/ray/memory_monitor WITH THE REFUSAL" and I had landed only the citation, then spent five review rounds making it more correct without making it load-bearing. IT WAS ALSO A MEANING FORK THAT DISAGREED. serving_engine decided whether Ray contributes an enforcer from ray_monitor_disabled: Bool -- a parameter produced by nobody -- while the cited module answers the same question from pinned ray-2.58.0 source. The two agreed only by accident, and on one configuration they disagreed in the direction that ADMITS: "disabled" is spelled as a zero refresh interval, and at a zero interval the factory swaps only the THRESHOLD monitor for a Noop while the EVENT monitor, pushed before the interval is ever read, keeps enforcing whenever resource isolation is on. The Bool answered "no enforcer" for a host that was being enforced, so a unified-pool Ray host with the monitor apparently off admitted silently -- the exact class this refusal exists to stop. WHAT CHANGED. ray_implied_enforcers and spark_memory_enforcer_admits no longer take a Bool asserting Ray's state; they take the two arguments Ray's factory actually selects on, resource_isolation_enabled and monitor_refresh, and derive the implied enforcer from ray_linux_built_monitors filtered by ray_built_monitor_enforces. A caller can no longer assert whether Ray is enforcing; it supplies the configuration and the citation decides. THE JOIN IS any, NOT A MAP, AND THAT IS LOAD-BEARING. With isolation on and a live interval the factory builds TWO enforcing monitors, event and threshold. One member per monitor would hand MoreThanOneEnforcer { count: 2 } to every ordinary Ray host, turning a correct citation into a refusal of the normal case. The population this module counts is ENFORCING SUBSYSTEMS over one pool, and Ray is one of those however many objects it builds internally. EVIDENCE, AND THE FALSIFICATION. New RED row w_a_zero_refresh_with_isolation_on_is_still_a_live_ray_monitor covers the configuration the old shape could not express. I falsified it rather than trusting a green: reverting the derivation to the old "zero refresh means no monitor" semantics fails that row AND NOTHING ELSE, so it discriminates exactly the behavior that changed and the other rows are insensitive to it by construction. serving_engine 15/15, ray monitor 16/16, v1_src_dag_parse 4887 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Name the monitor that is actually running, and refuse the one whose accounting we never established Exact-head review 5119848537, blocker 1. My previous commit fixed a wrong answer and introduced a fabricated cause. WHAT WAS WRONG. ray_implied_enforcers answered [RayThresholdMonitor] whenever anything enforced. At a zero refresh with isolation on the factory builds [Event, Noop] -- THERE IS NO THRESHOLD MONITOR ON THAT HOST. So the derivation relabelled the event monitor as the threshold monitor, and the next function inherited the threshold monitor's device-reservation accounting for it. The refusal was right about the host and invented its mechanism. That is worse than not refusing, because a named cause gets read as a finding. MY WITNESS COULD NOT SEE IT. enforcer_refused_with matched DeviceReservationCountingEnforcerOnUnifiedPool { kind: _ } and discarded the kind, so it proved that SOME counting refusal fired and never that the refusal named the mechanism present. A wildcard in an oracle is the oracle declining to check the field. The helper now compares cause AND kind. THE THIRD ANSWER IS NOT A false. Ray's event monitor fires off a cgroup event counter rather than a byte comparison, and nothing read for this citation settles whether that accounting includes a device reservation on a unified pool. A Bool has nowhere to put that: false asserts safety on the strength of not having checked -- the fabricated-plausible-answer failure -- and true invents the measurement. memory_enforcer_counts_device_reservation is replaced by memory_enforcer_device_reservation_accounting returning CountsReservation | ExcludesReservation | AccountingUnestablished, and on a unified pool the third arm REFUSES under its own cause, UnestablishedReservationAccountingOnUnifiedPool. An enforcer whose accounting is unknown is exactly as unsafe to admit as one known to double-count; the difference belongs in the cause, not in the verdict. MemoryEnforcerKind gains RayEventMonitor, because two mechanisms that measure different things are two kinds. Whether anything enforces is still read from ray_built_monitor_enforces; WHICH enforcer it is now comes from the composition, so the cardinality question and the identity question stay separate. Ray still contributes at most one member. FALSIFIED AGAIN: reintroducing the relabelling -- always answering [RayThresholdMonitor] -- fails w_a_zero_refresh_with_isolation_on_is_still_a_live_ray_monitor AND NOTHING ELSE, so the repaired oracle detects exactly the defect it was blind to. serving_engine 15/15, v1_src_dag_parse 4887 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Read the count the refusal carries, and stop calling every Ray monitor the threshold one Two findings from the in-flight review, both mine. THE SECOND WILDCARD. Having just fixed an oracle that discarded the refusal's `kind`, I left the one that discards its `count`. `enforcer_refused_with` matched MoreThanOneEnforcer { count: _ } and answered on the cause alone, so it would have accepted a refusal reporting ANY number -- including one, which is not "more than one" and would mean the population arithmetic had broken while the row stayed green. The count is the entire content of that cause. Rows that mean two enforcers now say two, through enforcer_refused_as_many, and the cause arm in the string oracle answers false so the count cannot be asserted without reading it. Falsified: reporting a wrong count fails exactly the two rows that assert it, and nothing else. STALE THRESHOLD-SPECIFIC PROSE. Three sentences still said "the threshold monitor" where the model now admits two Ray kinds -- most load-bearingly "a Ray-backed executor brings the threshold monitor with it", which is FALSE: it brings a monitor, and which one is read from the citation rather than assumed. Left standing, that prose is a second authority disagreeing with the code beside it, and it is the half a reader trusts. serving_engine 15/15, v1_src_dag_parse 4887 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Cite the classifier that decides admission, first-hand, and drop the mechanism story I had borrowed BLOCKER 2, CLOSED WITH A FIRST-HAND READ. ray_built_monitor_enforces selects whether a serving host counts as having a live memory enforcer, so once serving_engine consumed it the mapping sat on the decisive branch while being verified only against expectations this repository also authored. It now has its own ExternalModelScope citing the three implementation headers at ray-2.58.0. THE CRITERION IS STATED BECAUSE THE OBVIOUS METHOD IS WRONG. It is CAN IT INVOKE THE KILL-WORKERS CALLBACK, not IsEnabled(). NoopMemoryMonitor overrides IsEnabled() to `return true` while its Enable and Disable bodies are empty and its own doc says it "does not perform any monitoring" -- so a classifier keyed on IsEnabled would have reported the disabled configuration as enforcing, which is the direction that admits a host nothing is watching. Read at the tag: ThresholdMemoryMonitor "checks the memory usage periodically and invokes the callback" EventMemoryMonitor monitors events "when the memory.high is reached or exceeded and trigger the kill workers callback" NoopMemoryMonitor "does not perform any monitoring" I READ THESE MYSELF RATHER THAN RELAYING THEM. The same read confirmed, first-hand, three things this module had asserted on weaker evidence: the factory pushes the event monitor BEFORE reading the interval and then selects threshold-versus-noop on it; the disabled log line matches the modeled text byte for byte; and no pressure monitor is constructible from this factory, which is what makes RayBuiltMonitor's closed set closed. AND I DELETED THE PART I COULD NOT SOURCE. My previous commit explained the unestablished accounting by saying Ray's event monitor "fires off a cgroup event counter rather than a byte comparison". I got that from a peer's report, not from the source, and asserting it in an extdeps citation is precisely the failure this module exists to prevent -- so it is gone from both files. The honest fact needs no mechanism story: THIS REPOSITORY HAS NOT ESTABLISHED whether that accounting includes a device reservation on a unified pool. AccountingUnestablished stands on that alone. Also stale-prose repairs the review named: the non-Ray control no longer speaks of a "monitor flag" that no longer exists. serving_engine 15/15, ray monitor 16/16, v1_src_dag_parse 4887 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * The threshold monitor is two subjects, and the isolation flag was being dropped between them NEW BLOCKER FROM REVIEW 5119954985, AND IT IS THE DEFECT I JUST FIXED FOR EVENT, ONE KIND OVER. The tagged header says it plainly: with resource isolation enabled the threshold monitor "will only monitor user application memory usage"; with it disabled it reads node memory. Those observe different populations. The measurement this whole module was built around -- a node-memory reading that includes the GPU reservation -- is about the NON-isolated path, and nothing established that a resource-isolated user slice charges that reservation the same way. WHAT WAS WRONG. resource_isolation_enabled was consumed only to decide WHICH monitors the factory builds and then discarded, so every threshold monitor received CountsReservation whichever population it was actually watching. That extends a measured fact to an unmeasured subject -- exactly what AccountingUnestablished exists to refuse -- and my witness made the unsupported extension executable by asserting counts for the isolated configuration. REPAIRED BY SPLITTING THE KIND rather than adding a parameter to the accounting function. MemoryEnforcerKind now carries RayHostThresholdMonitor and RayUserSliceThresholdMonitor, so accounting stays a TOTAL FUNCTION OF THE KIND -- the property this module already argued for when it deleted the declared counts_device_reservation field -- and the refusal names which of the two threshold subjects is live. The isolation flag is threaded through the selection to the member instead of dying at it. The non-isolated node path keeps CountsReservation because that is the measured subject; the isolated path returns AccountingUnestablished until separately grounded. Two rows now, where there was one: isolation off with a live interval is the counting refusal, isolation on with a live interval is the unknown-accounting refusal. Falsified by collapsing the fork back to a single threshold kind, which fails the isolated row AND NOTHING ELSE. TWO STALE EVIDENCE IDENTITIES, both real. w_only_rays_monitor_counts_the_device_reservation had stopped describing its body, which is now a partition over five kinds and three accounting answers rather than a claim that one monitor counts. It is w_the_accounting_partition_over_every_enforcer_kind. w_the_same_configuration_admits_once_the_monitor_is_off was not the same configuration as any refusing row -- it changes the isolation setting AND supplies a declared guard -- and its annotation pointed at "the row above", which moved when a row was inserted. It is now named for what it proves, that a declared guard admits when the factory yields only a Noop, and the positional pointer is gone. A positional pointer in evidence decays exactly like a positional citation in source, which is the same lesson this branch already learned once when a relocated annotation falsified its own deixis. serving_engine 16/16, v1_src_dag_parse 4887 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * The prose re-fused the two threshold subjects the type had just been split into Both items from exact-head review 5120004532, annotation and body only. No type, function, refusal or witness body changes. THE ANNOTATION DESCRIBED THE PRE-SPLIT PARTITION. It spoke of "the threshold monitor" as one counting subject and then named the EVENT monitor as the sole unknown. After the split both sentences are false at the current grain: only the non-isolated host threshold monitor has an established counting result, and the isolated user-slice threshold monitor is unknown alongside the event monitor. So the prose re-fused in words exactly the two subjects the type had just been separated into, and the annotation disagreed with the executable partition about the very distinction the split existed to make. It now states the established side as one kind and the unestablished side as two, and records that naming only one unknown was itself the defect. THE BODY NAMED A DELETED CONSTRUCTOR. It described the split correctly in one section and then went on, in the present tense, to say MemoryEnforcerKind carries RayThresholdMonitor -- a constructor this head removed. The historical paragraph about the earlier single-kind version stays, because that is narration of what changed; the present-tense sentence now names the three current Ray identities and which of them has an established accounting answer. This is the third time on this branch that prose outlived the model it described: EVERYTHING ABOVE after a relocation, a witness annotation asserting a deleted master proposition, and now a partition sentence surviving the partition. The common shape is that annotations are invisible to every instrument -- the parse checks that they ATTACH, never that they are TRUE -- so each one is only ever caught by a reader. serving_engine 16/16, v1_src_dag_parse 4887 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
mainis red for every PR right now. One-file repair that unblocks the fleet; not part of my own line of work.Symptom
Every PR that test-merges current
mainfailsrequired-witnesses-floor:The floor itself is clean in the same job —
verdict=FloorClean unexpected_failures=0 claims_failed=0, 3621/3621 terminal. No witness is failing.Root cause — one file, one block
All 16 errors are
dag/test/claim/emit_copy_qualification_witness_test.dag:483-500:#10390 deleted the three workflow-subject rows at the end of that module and left their explanatory block as a trailing epilogue at EOF. DESIGN §4c admits only a leading block attached to a module-scope declaration — an annotation names the declaration that follows it, so at scope end it names nothing (
AnnotationAttachmentRefusal::UnattachedAtScopeEnd, once per line).The second blocker is not independent.
claim_executorpushesnamespace-wave-admission (no head index)only when the parse phase produced no index, so the wave never ran. One root, two reported blockers.Why it surfaced after #10390 landed
The parse wall is not new, and #10325 only changed which receipt arm carries blockers — not whether parse fails.
The timing is the answer. Run
33869134455(#10358's last green run on the old tree) was created 11:41:28Z; #10390 merged 11:48:31Z. The run's merge ref predates the breaking merge by about seven minutes, so it never parsed these lines. The first runs to actually test-merge the brokenmainare the ones now failing.(An earlier revision of this body said 11:28Z and "twenty minutes". That was a transcription of a different run —
33868113495, created 11:28:30Z, on a superseded head. The causal conclusion is unchanged; the interval was wrong and is corrected here.)The repair
Moves the block to the module head, where the imports that follow give it a subject. Every sentence preserved. Deictic words rewritten —
"stood here"→"stood at the END OF THIS MODULE","the rows above"/"the mutants below"→"in this module"— because a relocated pointer that still says "below" is a false citation of the kind this repo files asa_live_authority_name_carries_a_superseded_claim.The blank line placement is load-bearing, and the first revision of this PR got it wrong.
std.source_annotationmodule_header_gap_subjectbinds a post-module block to the module root only when the block opens immediately after the module line:A blank line between
module …and the first//disables that arm and falls through to ordinary nearest-following attachment — which bound this module-wide block toimport std.measure { byte_size }. That made the parse green while the annotation acquired the wrong subject: a plausible answer standing where a typed refusal used to be. The blank line is now removed; the one after the block is kept, because it ends the block.Nothing else changes — no row, no assertion, no rung drop. The content already has a typed home at
gunbc.rung_dropemit_copy_qualification_without_a_consumer, untouched here.Evidence
FAILED PHASE parse (16 error(s))+namespace-wave-admission (no head index)parse FAILlinessource_annotation_attachment_witness_test13/13 PASSRun locally with
claim_executor --required-ci --source-root dag --source-root src/v2 --required-lane witnesses.Known coverage gap — named, not fixed here
No enrolled witness covers
module_header_gap_subject's blank-line arm — the exact rule that silently mis-bound this block. The attachment battery covers leading, trailing, body-grain and block-splitting cases, but nothing discriminates module-root attachment from nearest-following attachment across that one bit. That witness belongs in its own change; this PR is a fleet unblocker for a redmainand stays one file wide.The gate cannot pass until this merges
namespace-wave-admissionnow reports:The head parses; the base does not, because this PR is the fix. The wave needs base-side declarations, so a repair for a main parse breakage cannot satisfy the gate the breakage disables. That refusal is correct fail-closed behaviour, not a defect — but it does mean this PR needs an operator merge rather than a green required lane.
Note for whoever next touches the admission roster
With the wave running again it reports the 255
#10358rows as consumed at base — deletion owed on the roster's next touch. This PR does not touchnamespace_wave_admission.rs, so they are not due here.🤖 Generated with Claude Code
https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G