Skip to content

cargo build --release fails on main: the derived stage0 partition's generated lib.rs is missing a module its own included file imports, and its stated producer was deleted - #9244

Merged
gunbai-bot[bot] merged 18 commits into
mainfrom
session/neat-pike-374-partition-phase
Aug 26, 2026

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Auto-opened by session-dashboard for session neat-pike-374.
Pushing to session/neat-pike-374-partition-phase advances this PR.

Worker attestation

Before flipping this PR to ready for review, confirm each item:

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.

Test plan

  • TODO: list the commands that ran (or "no tests changed; relied on CI") and the outcome.

gunbc-ci-auto-heal and others added 16 commits August 25, 2026 22:10
… inverted crate tier

`cargo build --release` at the workspace root -- the first item under
DESIGN's own Building & checks bullet -- fails on main. Two module rosters
stand over one file set: `src/v1/stage0/src/lib.rs` (complete, 160 mods) and
the seven partition-crate `lib.rs` files, which share those same sources
through `#[path]` includes. Nothing decides that the two agree, so a module
added to the monolith is invisible to the split crates until someone runs a
build the required lanes never run.

MEASURED, not inferred. The reported specimen was one module; cargo stopped
at the first failing crate and never reached the one above it. A per-crate
visibility scan (own `pub mod` plus the transitive `pub use <pkg>::*` chain,
against every included file's `crate::X` references) finds three:

  stage0_extdeps_languages   extdeps_languages_rust_capabilities
  stage0_v1_infer            v1_compiler_coercion, v1_compiler_type_head_exposure

The prediction was confirmed by execution before this change was written: a
build carrying only the first fix failed on exactly the second crate.

The second half is not two more roster lines. `v1_compiler_infer_emit_info`
imports `crate::v1_compiler_artifact`, owned by `stage0_v1_artifact` -- which
DEPENDED ON `stage0_v1_infer`. `v1_compiler_coercion` reaches artifact too, so
it could not join infer while artifact sat above it. Measured deps show the
edge pointed the wrong way: `v1_compiler_artifact` needs only
{std_types, v1_rt, v1_std_core} and `v1_compiler_languages` needs no infer
module at all. The tiers are swapped -- artifact BELOW infer -- in the
authority rows, the generated mirror, the manifests, the `pub use` chains and
the workspace member order, and the four real import edges the module DAG was
missing are added.

WHY NOTHING CAUGHT IT. `claim_executor` is a bin of the monolith, whose roster
is complete, so every required phase builds a crate that compiles. The
partition crates are workspace members nothing in CI builds; the Rust suite
and clippy left CI by operator ruling in July.

WHAT THIS DOES NOT FIX, stated so it is not mistaken for closed. The producer
exists and has no caller: `v1.compiler.stage0_crates` renders both the
partition `lib.rs` and its `Cargo.toml` from the authority rows, and
`stage0_crate_boundary_emit_outcome` is named by nothing outside its own
module, its mirror, and `workspace_members`. `--required-regen` does not cover
these crates. So this repairs the instance and leaves the class: the same
omission lands again with the next module.

RUNG: unchanged, and low. The rosters stay hand-authored and no mechanism
refuses a fourth omission -- previous rung mitigatable, current rung
mitigatable, bounded residue the three modules and one tier edge repaired
here. NEXT-RUNG TRIGGER: give the producer a live entry point AND a required
phase that FAILS on a partition crate that does not compile, landed together
-- a producer with no route is the defect this commit diagnoses, so producing
without routing would relocate it rather than close it. Deriving the
assignment itself (a new module rostered with no human edit) is blocked on
the declared `stage0_module_dag_interface_grounding` debt, dissolving on
`v2.compiler.source_authority` `ModuleStorageIndex`; that is this class's
own next trigger, not effort withheld.

EVIDENCE. RED: `cargo build --release`, remote, main efc880a --
E0433/E0432 on `extdeps_languages_rust_capabilities`. Intermediate, with only
that fix: E0433/E0432 on `v1_compiler_artifact`, `v1_compiler_coercion`,
`v1_compiler_type_head_exposure`, as predicted. GREEN: `Finished release
profile in 6m 01s`, exit 0, one remote dispatch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
`--required-regen` refuses this branch:

  required-regen: first_generation_equal=false planned=135 executed=135 declared_divergent=1
  required-ci: regen FAIL generated surface drift: gunbc_stage0_crate_partition_generated.rs

The parent commit edits `dag/gunbc/stage0_crate_partition_generated.dag` and did
not carry that file's own emitted Rust mirror. So the branch INTRODUCED a
disagreement between a generated authority and its projection, which is worse
than the state it inherited: on main the two agree while both being stale, here
they disagreed. This installs the mirror the emitter produces, from a
`--required-regen` candidate tree rather than by hand -- hand-editing a
generated file is the sin that produced this whole class.

The diff is 26 lines and every one is this branch's own authority change
projected: `extdeps_languages_rust_capabilities` into the extdeps roster, the
`v1-stage0-v1-artifact` and `v1-stage0-v1-infer` rows swapped, the two new
modules into infer, and the reexport chains flipped. No unrelated drift rides
along, which is what made it safe to install.

HOW IT WAS FOUND, because the route matters more than the fix. Not from the red
-- from the follow-up work. The partition-boundary producer entry point was run
read-only against the tree: 14 files, 9 BYTE-FOR-BYTE MATCHES, 5 drifted, the 5
being exactly the files the parent commit hand-edits. The 9 matches are the
positive control: they establish the producer is faithful, so the 5 are a
finding rather than a broken renderer. Running it with `--write` then REVERTED
the parent commit's repair -- because the binary's embedded rows come from the
stale mirror. Two independent instruments then named the same file: that local
run, and this branch's CI build lane twenty minutes later. Had they disagreed,
that would have been the larger finding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
…-partition-phase

# Conflicts:
#	src/v1/stage0/src/bin/claim_executor.rs
The tier swap in this branch's first commit reordered `stage0_module_bearing_crate_rows`.
The emit-shell crate's path dependencies are rendered by iterating those rows IN
ORDER, so the authority now derives `v1-stage0-v1-artifact` before
`v1-stage0-v1-infer` while the committed manifest still carried the old order.
One line, and cargo does not care -- dependency order is cosmetic to it -- but
the derived file disagreed with the authority it is derived from.

WHY IT IS HERE AND NOT IN THE FOLLOW-UP. This branch changes the authority, so
it owes every projection of that authority. Landing the projection separately
would leave main holding exactly the authority-versus-mirror disagreement this
branch's second commit exists to remove, which is the state this whole change
argues is worse than either coherent one.

WHY IT WAS INVISIBLE UNTIL NOW, which is the part worth recording. The
pre-mirror-fix run of the producer reported this file as MATCHING, because the
binary was rendering from the STALE mirror and therefore reproduced the old row
order on both sides. The staleness was masking its own downstream consequence.
It became visible only once the regenerated mirror was in the tree being
measured -- so this file is a consequence of the previous commit's fix, not an
omission it should have caught.

AND IT COMPLETES A SYMMETRY THE FOLLOW-UP DEPENDS ON. The defect that opened
this branch had drift ZERO across four projection links and a crate that did
not compile: a drift check cannot see a stale authority. This file is the
mirror image -- real drift, and a compile that passes: a compile cannot see a
drifted projection. Neither check subsumes the other, and there is now an
executed specimen of each failing where the other succeeds.

Bytes installed from the producer's own `--write` output, diffed before
committing: the two path dependencies swap position and nothing else moves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
`witness_v1_infer_unit_members_holds` asserts `(unit.members |> count) == 7`.
This branch takes `stage0_v1_infer` to nine members -- `v1_compiler_coercion`
and `v1_compiler_type_head_exposure` -- so the witness goes red BY
CONSTRUCTION on this head. One number, not a witness rewrite.

AUDITED RATHER THAN ASSUMED, because a count assertion is rarely alone. Every
other pin in that file is unaffected and each for a stated reason:
`witness_runtime_unit_members_holds` (== 1) and
`witness_v1_artifact_unit_members_holds` (== 2) pin units this change does not
resize -- artifact keeps both members THROUGH the tier swap, since the swap
moves the row's position and not its contents.
`witness_resolve_partition_explicit_returns_assignment_holds` compares a count
to another measured count, so it is self-relative and cannot pin a size.
`witness_partition_physical_crate_count_holds` and
`..._module_bearing_unit_count_holds` pin CRATE and UNIT totals, which are
unchanged: no crate is added or removed here. No assertion in the file is
order-sensitive, which was the other thing worth checking given the swap
touches authority rows, manifests, `pub use` chains and workspace member order.

NOT RE-DERIVED BY CI, DELIBERATELY. This was decidable by reading, the fleet
has been saturated, and spending a runner to confirm an assertion whose subject
is a literal in the diff would measure nothing the source does not already say.

WHAT THIS DOES NOT CHANGE: the residue this branch already declares. The
producer still has no caller, `--required-regen` still does not reach the
partition crates, and this repairs the INSTANCE while leaving the CLASS -- which
regressed once already in this same crate three weeks ago. The follow-up carries
the phase; this commit only stops a stale literal from failing a correct change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
    stage0_module_dag_edge(from: "v1_compiler_artifact", to: "v1_compiler_languages")

asserts the reverse of the source. `partition_unit_dep_from_edge_fold`
(`v2.std.compilers.compilation_unit`) reads an edge as: if `edge.from` is in the
unit and `edge.to` is outside it, the unit depends on `edge.to`'s unit -- so
FROM IS THE CONSUMER AND TO IS THE DEPENDENCY. The row therefore claimed
artifact depends on languages.

The source says the opposite, and the two files are unambiguous:
`v1.compiler.languages` opens with `import v1.compiler.artifact { RenderTarget }`,
while `v1.compiler.artifact` imports `v1.std.core` alone and names languages
nowhere. Direction verified by reading the fold rather than inferred from the
field names, because `from`/`to` do not say which way dependency runs.

INERT TODAY, AND THAT IS THE ARGUMENT FOR FIXING IT HERE RATHER THAN AGAINST.
Both modules sit in one crate, so no build can observe the lie -- the same shape
as the emit-shell manifest earlier in this branch, where cargo does not care and
the authority is wrong anyway. A branch whose subject is repairing this
authority should not leave a false edge in it for a later derived partition to
trust. The four edges this branch ADDS were checked by the same rule and all
point consumer to dependency: infer_emit_info -> {infer_items, artifact,
coercion}, infer_env -> type_head_exposure, coercion -> artifact, each matching
its module's own imports.

A NARROWER STATEMENT OF WHAT THE TIER SWAP ESTABLISHES, which is the accurate
one: the CURRENT SOURCE DEPENDENCY GRAPH requires the artifact partition to
precede the infer partition. That is not a claim that artifact semantics belong
permanently below inference. Extracting `RenderTarget` into a smaller
target-vocabulary authority, so inference need not acquire the whole
artifact/languages package, remains open as a partition-GRANULARITY question --
and the strong phrasing would have foreclosed it rhetorically while adding
nothing this branch needs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
An empty blob at path `cargo` (e69de29, the empty blob) was committed in this
branch's wip checkpoint. Nothing in this PR's subject wants a root-level file
with that name; it is the residue of a shell redirect landing on the word
`cargo` rather than after it.

I cannot attribute it to a specific command with certainty and am not going to
invent one. What IS decidable is the question that matters: the blob is EMPTY,
so whatever redirect created it captured nothing, which means no command's
stderr was diverted into it and no measurement published from this branch lost
output to it. The dispatches that produced numbers wrote to /tmp paths and were
read back in full.

Same family as tonight's pipeline audit, arriving through redirection rather
than through a pipe: the command ran, exited, reported, and left a side effect
at a path nobody intended. The build could not notice. Git did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
…t suspect, and the layout authority has two Rust mirrors

Two defects in the partition-crates phase, found by running it rather than by reading it.

PLACEMENT. The compile conjunct sat inside the drift branch's `Rendered` arm, so a
`CarrierRefused` returned before it ran. The two conjuncts are independent in both
directions -- drift is blind to an incomplete authority, compile is blind to a drifted
projection -- so neither may gate the other, and gating the completeness check on the
render succeeding inverted it: the state where the authority is least trustworthy was
the state that skipped the check. Hoisted out of the match; it now runs whatever the
first conjunct answered.

THE SECOND MIRROR. `stage0_crate_layout.dag` projects into TWO emitted Rust mirrors,
`bootstrap_stage0_crate_layout_generated.rs` and `gunbc_stage0_crate_layout_generated.rs`.
Registering `partition_crate_boundary_host` updated only the first, which CI caught as
`regen FAIL generated surface drift: gunbc_stage0_crate_layout_generated.rs`. Inserted at
the matching ordinal; --required-regen adjudicates the edit rather than the author.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
`summary()` is the string that lands in `FAILED PHASE`, and it read
`Completed status=101 packages=7` -- an author learns that one of seven crates did not
compile and re-runs the build by hand to find out which. The cargo tail a dozen lines
further up the log already held the answer; the refusal itself did not carry it, and the
refusal is what gates.

`Completed` now carries `failed_packages`, read from cargo's own
`could not compile `NAME`` lines. We do not decide which crate failed -- cargo does --
and this only carries that attribution up to where the stop prints:

    Completed status=101 packages=7 failed=[v1-stage0-extdeps-languages]

Reported BESIDE the status rather than instead of it, because the list is legitimately
empty when cargo fails without attributing to a package (a resolver or manifest error),
and an empty list must never read as "nothing failed".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
…ated next

DESIGN.md is a generated projection, so the conflict is resolved in the AUTHORITY
(`dag/gunbc/design_document.dag`) and the projection is regenerated from it, per the
generated-artifact driver. Neither side's DESIGN.md bytes are the projection of the merged
authorities, so picking a side drops the other side's authority-derived content with nothing
in the tree to say so.

THE TWO EDITS DO NOT OVERLAP; git grouped them only because the rows are adjacent:
  - the CI row: changed by this branch, byte-identical to base on main -> take ours
  - the measurement-bankruptcy row: changed on main, byte-identical to base here -> take theirs
Verified by hashing each row on all three sides (base, ours, theirs) rather than by reading the
hunk, because ADJACENCY IS NOT OVERLAP and the conflict presentation cannot tell them apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
The merge commit left DESIGN.md carrying the driver's ours-verbatim bytes, which is not the
projection of the merged authorities: it held the BASE version of the measurement-bankruptcy
row while the authority now carries main's.

Regenerated by running the artifact gate with a gunbc built FROM THIS TREE. The image's
`gunbc` refuses on this corpus with unresolved symbols in files this branch never touched --
the binary predates the tree -- so the earlier gate failure was the compiler's vintage, not
the source. Built from source, GATE_EXIT=0.

The regenerated row is byte-identical to main's rendering of the same authority row, checked
rather than assumed, so exactly one line changes and the CI row this branch authored is
untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WJNiTYqhzCcWJM6o5L5fQ
@briansrls
briansrls marked this pull request as ready for review August 26, 2026 01:15
…-partition-phase

# Conflicts:
#	DESIGN.md
#	dag/gunbc/stage0_crate_layout_generated.dag
#	src/v1/stage0/src/bootstrap_stage0_crate_layout_generated.rs
#	src/v1/stage0/src/gunbc_stage0_crate_layout_generated.rs
@gunbai-bot
gunbai-bot Bot merged commit 25bf535 into main Aug 26, 2026
3 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/neat-pike-374-partition-phase branch August 26, 2026 04:37
@briansrls
briansrls restored the session/neat-pike-374-partition-phase branch August 26, 2026 05:24
briansrls pushed a commit that referenced this pull request Aug 26, 2026
…w, so the live receipt can be taken at this branch ref before merge

WHY THIS WORKFLOW RATHER THAN A NEW ONE. GitHub only accepts a workflow_dispatch event for a
workflow present on the DEFAULT branch, so a new observation workflow authored on this branch
could never be dispatched and the receipt could not exist until after merge. It does, however,
run the version of an ALREADY-PUBLISHED workflow from whatever ref is dispatched. fleet-converge
is already published, already has workflow_dispatch, already takes an exact host, and already
pins its second job to that host's runner label -- so extending it is the only route that gets a
real session-host receipt BEFORE merge, which is what the vertical-cut rule requires.

WHY THIS CANNOT ACTUATE ANYTHING. Every actuating step in that job is individually mode-gated
(plan, apply, spark_linger, spark_grants, spark_observe, spark_reboot, spark_durability), so
dispatching session_demand_observe executes NONE of them. The step itself runs on a runner pinned
to the dispatched host and reads that host's own cgroups over LocalShell: find and cat under
/sys/fs/cgroup, no ssh, no fleet key, no new credential surface. It mutates nothing on the host
and writes only its own receipt inside the job workspace.

THE HOST IS OBSERVED, NOT SUPPLIED. The receipt binds to the hostname the machine itself answers
via the already-modeled HostnameReadShort, not to the dispatch input -- otherwise a receipt could
name a machine it did not read.

THE BACKSTOP TIMEOUT NOTE IS UPDATED BECAUSE IT SAYS TO BE. gunbc_ci_fleet_job_backstop_timeout_note
declares itself "the single authority to update alongside any change to that job's step
composition, if_condition gating, or mode set", and this is a mode-set change. The worst case does
NOT move: this is one aux-shaped step under a spark-tier budget, strictly below the spark_grants
aux+spark 65m that sets the bound, so the 105m single-run total stands.

ONE WITNESS LEGITIMATELY CAUGHT THIS. workflow_dispatch_choice_input_projects_options_list pins
the mode options at seven and re-enumerates them, so the eighth mode turned it RED -- which is the
witness doing its job. Updated to eight. Worth recording without acting on it here: that
enumeration is a second representation of fleet_converge_mode_options and could be DERIVED from
that authority, which would make it self-maintaining and a genuine projection-fidelity check
rather than a list needing an edit per mode. That is a change to the epistemics of another lane's
witness and does not belong under this title.

ONE PIECE OF DRIFT DELIBERATELY NOT ABSORBED. The regen also wanted to add
`src/v1/stage0/src/partition_crate_boundary_host.rs !merge` to .gitattributes. That is MAIN's
drift, not this branch's -- the .rs has existed since #9244, main's committed .gitattributes lacks
the row, and nothing here touches stage0. It is unguarded because the generated-artifact drift
gates are on DESIGN's post-floor-cut unguarded list. Reverted rather than swept in under an
unrelated title; it wants its own cut off main.

VALIDATION: 4/4 workflow-dispatch witnesses PASS and the producer entry resolves and typechecks
under claim_batch --hermetic. The regen ran clean at exit 0 with ROADMAP.md untouched, which is
the projected-arm precondition established before letting a wet actuator write.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Aug 26, 2026
…ing pair taken from srv1 and srv2 (#9321)

* Step 4A, first half: the cited cgroup-v2 authorities and an identity-first session-demand carrier

WHAT THIS ANSWERS, AND WHAT IT DELIBERATELY DOES NOT. One semantic question: can the fleet
produce a complete, host-bound concurrent-session-demand observation. It answers nothing about
what the session capacity should be -- no MemoryHigh, no MemoryMax, no concurrency policy, no
operator margin, and no replacement of the authored 20 GiB aggregate reservation.

THE METRIC SEMANTICS ARE THE KERNEL'S, NOT OURS. cgroup-v2 already documents each memory
interface file with a distinct temporal meaning, so extdeps.linux.cgroup_v2_memory grounds the
three kinds there rather than coining them downstream: Instantaneous, LifetimeEpochScoped (an
extremum since cgroup creation or reset -- NOT the maximum during an observation window, which
is why the epoch is a field), and CumulativeCounter (only a start/end pair yields a delta).

TWO PARSE FACTS THAT A BARE ByteSize FIELD CANNOT CARRY, both taken from the authority:
memory.max and memory.high hold either a byte count or the literal "max", so a limit read has
FOUR outcomes -- rendering the sentinel as a huge number would fabricate a limit the host does
not have. Count files have no sentinel, so they have two, and "max" in a count file is
MALFORMED. A negative body is malformed rather than a magnitude; nat_magnitude is applied only
after the sign is excluded, where it is the identity, because using it as the crossing would
silently turn "-1" into 1.

IDENTITIES FIRST, COUNTS PROJECTED. #9280's SliceMembershipSignal carries two Nats, which is
sufficient for its classification kernel and NOT sufficient as a measurement denominator: two
counts cannot say which containers they counted, so an unclassifiable container has nowhere to
go except into one of the counts or out of the population. Both fabricate. The census therefore
REFUSES to project when any identity is unclassifiable, and carries the offenders so the remedy
names containers rather than a number.

A COUNTED ZERO IS NOT AN ABSENCE OF EVIDENCE. NoConcurrentSessionDemandObserved (the read ran
and found none) stays distinct from SessionDemandObservationUnavailable (the producer did not
run, or the denominator could not be established). They grant capacity on opposite grounds.

VERIFIED BY EXECUTION, not by typecheck. All three modules compile with 0 blocking. Eight
witnesses PASS under claim_batch --hermetic. The wall is mutation-tested rather than assumed:
making the count parser accept the "max" sentinel turns count_parse_has_no_sentinel_arm RED
while its sibling limit_parse_discriminates stays GREEN, so the witness catches that exact
defect and is not a blanket break.

The -type d child enumeration is also execution-grounded: a plain listing of a real cgroup
returns 92 entries, ALL of them interface files and NONE a child, so a name filter would have
been wrong there and -type d correctly returns 0.

NOT YET PRESENT, and this is why no PR is opened: the live host receipt. There is no route to a
session host from a session container (ssh refused, no docker context), and the classifier's
cgroup naming authority is unobservable from inside a namespaced cgroup. Inventing it would be
fabrication. Sequencing is with the operator.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* The host-bound producer: a child of the managed slice is managed by where it was found, so no naming authority is needed

THE BLOCKER I REPORTED DISSOLVED ON INSPECTION, and the reason is worth recording because it
changes what the cut needs. I had said the classifier was unwritable without observing real
cgroup naming from a host I cannot reach. That is true of only ONE HALF of it.

  MANAGED  a child of sessions.slice IS a managed session BY VIRTUE OF WHERE IT WAS FOUND.
           The cgroup hierarchy is the classifier. No docker-scope naming convention has to be
           invented, guessed, or observed to read it.

  LEGACY   a flat scope under system.slice may be an agent session or an unrelated container,
           and cgroup structure alone cannot tell them apart.

So the legacy arm is SessionPlacementUnclassifiable rather than pattern-matched, which makes the
census REFUSE on a host carrying flat scopes instead of reporting a population it guessed. On a
fully-converged host the legacy set is empty and the census completes. Inventing a naming rule to
make it complete on a mixed host is exactly the fabrication the carrier exists to prevent, and it
is now recorded as a note rather than left as a silent gap.

AN EMPTY LISTING IS A COUNTED ZERO, NOT A FAILURE, and the distinction is load-bearing at the
read boundary: find exits 0 with empty stdout on a leaf cgroup, so success-with-no-lines reaches
the caller as an observed empty population. A FAILED read becomes Unavailable instead. Collapsing
those would be the empty-observation narrow at the one place it grants capacity.

A COUNTER THAT COULD NOT BE READ IS NOT A ZERO. Each per-session read carries its own parse
outcome through to the receipt rather than defaulting, because an unreadable counter and an idle
container are the same number and opposite facts -- and an aggregate absorbing the first would
read as smaller, more attractive demand than the host actually has.

VALIDATION, and why it is claim_batch rather than a full entry compile. 12 witnesses PASS under
claim_batch --hermetic, which resolves and typechecks session_demand_observe and, through its
imports, the two new HostOperation variants in host_operation_exec. The full `compile --entry`
emit was attempted three times and its gunbc process was KILLED mid-emit every time while this
container's own cgroup reported oom_kill=0 at 19.3 GiB against a 31.27 GiB cap -- the kill comes
from the parent slice, which is invisible from inside. Resolve+typecheck is the check that
validates .dag correctness; the Rust emit adds nothing to it here and cannot complete in this
container. That failure is not incidental to this branch: it is the instrument's own
justification, observed three times while building the instrument.

STILL MISSING: the live session-host receipt. Per the operator ruling the route is to add a
read-only mode to the EXISTING fleet-converge workflow -- which already has workflow_dispatch,
already accepts an exact host, and already pins its second job to that host's runner label -- and
dispatch it AT THIS BRANCH REF before merge. Every actuating step in that job is individually
mode-gated, so a new read-only mode runs none of them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* A read-only session_demand_observe mode on the existing fleet workflow, so the live receipt can be taken at this branch ref before merge

WHY THIS WORKFLOW RATHER THAN A NEW ONE. GitHub only accepts a workflow_dispatch event for a
workflow present on the DEFAULT branch, so a new observation workflow authored on this branch
could never be dispatched and the receipt could not exist until after merge. It does, however,
run the version of an ALREADY-PUBLISHED workflow from whatever ref is dispatched. fleet-converge
is already published, already has workflow_dispatch, already takes an exact host, and already
pins its second job to that host's runner label -- so extending it is the only route that gets a
real session-host receipt BEFORE merge, which is what the vertical-cut rule requires.

WHY THIS CANNOT ACTUATE ANYTHING. Every actuating step in that job is individually mode-gated
(plan, apply, spark_linger, spark_grants, spark_observe, spark_reboot, spark_durability), so
dispatching session_demand_observe executes NONE of them. The step itself runs on a runner pinned
to the dispatched host and reads that host's own cgroups over LocalShell: find and cat under
/sys/fs/cgroup, no ssh, no fleet key, no new credential surface. It mutates nothing on the host
and writes only its own receipt inside the job workspace.

THE HOST IS OBSERVED, NOT SUPPLIED. The receipt binds to the hostname the machine itself answers
via the already-modeled HostnameReadShort, not to the dispatch input -- otherwise a receipt could
name a machine it did not read.

THE BACKSTOP TIMEOUT NOTE IS UPDATED BECAUSE IT SAYS TO BE. gunbc_ci_fleet_job_backstop_timeout_note
declares itself "the single authority to update alongside any change to that job's step
composition, if_condition gating, or mode set", and this is a mode-set change. The worst case does
NOT move: this is one aux-shaped step under a spark-tier budget, strictly below the spark_grants
aux+spark 65m that sets the bound, so the 105m single-run total stands.

ONE WITNESS LEGITIMATELY CAUGHT THIS. workflow_dispatch_choice_input_projects_options_list pins
the mode options at seven and re-enumerates them, so the eighth mode turned it RED -- which is the
witness doing its job. Updated to eight. Worth recording without acting on it here: that
enumeration is a second representation of fleet_converge_mode_options and could be DERIVED from
that authority, which would make it self-maintaining and a genuine projection-fidelity check
rather than a list needing an edit per mode. That is a change to the epistemics of another lane's
witness and does not belong under this title.

ONE PIECE OF DRIFT DELIBERATELY NOT ABSORBED. The regen also wanted to add
`src/v1/stage0/src/partition_crate_boundary_host.rs !merge` to .gitattributes. That is MAIN's
drift, not this branch's -- the .rs has existed since #9244, main's committed .gitattributes lacks
the row, and nothing here touches stage0. It is unguarded because the generated-artifact drift
gates are on DESIGN's post-floor-cut unguarded list. Reverted rather than swept in under an
unrelated title; it wants its own cut off main.

VALIDATION: 4/4 workflow-dispatch witnesses PASS and the producer entry resolves and typechecks
under claim_batch --hermetic. The regen ran clean at exit 0 with ROADMAP.md untouched, which is
the projected-arm precondition established before letting a wet actuator write.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Two interpolations either side of a slash are parsed as a DIVISION, not a path: give ReadInterfaceFile one composed input

FOUND BY EXECUTION ON A REAL SESSION HOST, which is the whole point of taking the receipt before
merge. Dispatched at this branch ref against srv1; the job landed on srv1-07 and the producer ran:

  Clock.Now                          done in 1ms
  os.Hostname.ReadShort              done in 1ms
  linux.CgroupV2.ListChildCgroups    done in 2ms     (managed slice)
  linux.CgroupV2.ListChildCgroups    done in 3ms     (system.slice)
  error: TypeError { msg: "unsupported operator Div on String" }

THE DISCRIMINATOR IS IN THE PAIR. ListChildCgroups interpolates ONE value into its argv and
executed twice without complaint. ReadInterfaceFile carried TWO interpolations separated by a
literal slash -- argv: ["cat", "{cgroup_path}/{interface_file}"] -- and it is the only operation
that failed. The interpolation grammar reads ACROSS the slash rather than stopping at the closing
brace, so `/` is taken as the division operator applied to two Strings. It typechecks, because
both sides really are Strings; it only dies at evaluation.

THE FIX IS TO COMPOSE THE PATH WHERE THE TYPE SYSTEM CAN SEE IT. ReadInterfaceFile now takes ONE
file_path input and interpolates once; the caller builds the path with cgroup_v2_child_path, which
already exists and is already the single authority for joining a parent to a child. That is
strictly better than escaping the slash: the composition moves out of a transport-DSL string and
into a modeled function, so there is no second place where a path gets assembled.

NOT CLAIMED: that the interpolation grammar is defective. It may be behaving as specified. What is
established by execution is that an argv of the form "{a}/{b}" does not produce a joined path, and
that a single interpolation does work -- so the remedy above holds either way.

WHAT THIS RUN ALREADY PROVED, independent of the defect. The read-only mode is real: the job ran
on a session host, EVERY actuating step was skipped by its own mode gate (plan, apply, spark_*),
and only the observation step executed. The two new cgroup operations are wired end to end through
LocalShell and reached the host's own hierarchy. The host answered its own name, so the receipt
binds to the machine that was read rather than to the dispatch input.

Main's .gitattributes drift was again offered by the regen and again reverted; it is not this
branch's and wants its own cut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* A systemd unit is not an unclassifiable session, it is not a session at all: the live srv1 receipt supplied the naming I refused to invent

THE RUN THAT PRODUCED THIS IS THE CITATION, and the figures below are quoted only because this
message's subject IS the measurement. The instrument is gunbc.session_demand_observe
session_demand_observe_ci_wet, dispatched as fleet-converge.yml -f host=srv1 -f
mode=session_demand_observe at this branch ref; run 32953395735 on srv1-07. Re-derive rather than
trust this paragraph.

WHAT THE FIRST GREEN RUN ESTABLISHED. The producer executed on a real session host and the whole
chain worked: Clock.Now, os.Hostname.ReadShort (so the receipt binds to the machine that answered,
not to the dispatch input), both ListChildCgroups, and every ReadInterfaceFile. Every actuating
step in that job was skipped by its own mode gate. 23 managed sessions enumerated with per-session
memory.current, plus the parent slice's own current, peak and max.

THE NAMING IS NOW OBSERVED RATHER THAN INFERRED. I refused to assert docker-<id>.scope from my own
hostname being a 12-hex short id, and said the first run would supply it. It did: all 23 children
of sessions.slice are docker-<64 hex>.scope.

AND THE RUN IMMEDIATELY FALSIFIED MY LEGACY ARM, which is the part worth recording. system.slice
on srv1 has 37 children and ZERO of them are .scope of any kind -- they are cron.service,
containerd.service, systemd-networkd.service and the rest. My producer fed EVERY system.slice
child into legacy_placements, so it would have marked 37 ordinary systemd units as unclassifiable
SESSIONS and REFUSED THE CENSUS OF A PERFECTLY CONVERGED HOST.

That is the not-applicable-rendered-as-malformed failure DESIGN names, in its exact shape: one
symbol carrying both "this container cannot be attributed to a session" and "this is not a
container at all", which have nothing to do with each other and opposite remedies. Nothing in the
carriers could see it, because both states really are absent-of-classification; only running it
against a real system.slice showed that the population was wrong rather than the classifier.

THE FIX SPLITS THEM AT THE POPULATION BOUNDARY. A candidate flat session is a docker scope;
everything else is not in the session population at all and never reaches the classifier. A docker
scope found FLAT under system.slice stays Unclassifiable and refuses the census, which is the arm
that matters and which measured ZERO on srv1 because that host is fully converged.

THE NEW WITNESS IS BUILT FROM THE RECEIPT, not from invented names: the four unit names it asserts
against are ones srv1 actually reported, and the docker scope it accepts is one of the 23 real
identities. A second witness caught my own change while I made it -- the existing fixture passed
"z" to legacy_placements and expected one placement, which correctly became zero once the filter
landed. Grounded it in a real scope shape rather than weakening the assertion.

NO RECEIPT FILE IS COMMITTED, deliberately. DESIGN's measurement bankruptcy ruling is that a
figure copied into prose is debt whether or not a producer exists for it, and that a measurement is
cited by naming the producer that re-derives it. The producer serializes its own receipt and the
workflow prints it; the run is addressable. Transcribing it into docs/ would recreate exactly the
corpus that was deleted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Exercise the census on the host, so a converged and a mixed host produce visibly different verdicts from one code path

The entry enumerated and stopped, which left the census and the membership gate unexecuted on real
hosts -- so the one thing this cut claims, that a COMPLETE observation can be established, rested
on fixtures while the live runs only proved the reads worked. It now runs placements -> census ->
membership signal -> gate and prints the verdict.

WHY THIS MATTERS RIGHT NOW RATHER THAN AS POLISH: the two hosts already disagree. srv1 carries 23
managed sessions and ZERO flat scopes, so its census completes and the aggregate may be
established. srv2 carries 26 managed sessions AND ONE FLAT DOCKER SCOPE under system.slice, so its
census refuses -- one running session container is outside the cgroup oomd evicts within, and any
aggregate over srv2's sessions.slice is measuring 26 of 27 while naming the whole.

That is the live RED for the incomplete arm, on a real host, without having to construct one. It
is also the exact defect #9280's four-way classification was built to catch, found in production
rather than in a fixture.

inventory_identities keeps the two absences apart: an inventory that could not be READ contributes
no identities and is refused separately, rather than rendering as an empty population that would
read as a clean host.

NOT CLAIMED: any capacity conclusion. The per-host caps differ (srv1 sessions.slice MemoryMax is
twice srv2's) and the authored aggregate reservation does not match either host's measured
current, but establishing what the reservation SHOULD be is 4B and the operator's ruling, not this
cut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* An unreadable system.slice was rendering as "no flat sessions", so a host nobody observed could report AllSessionsManaged and exit success

review 56217 finding 1 is correct and it is the serious one. inventory_identities answered `[]`
for an inventory that COULD NOT BE READ, which is indistinguishable from one that was read and
found empty. So a failed system.slice enumeration contributed no flat candidates, the census
completed, and the entry reported "AllSessionsManaged; an aggregate may be established" and exited
SUCCESS over a host whose flat population was never observed.

THE DIRECTION IS WHAT MAKES IT SEVERE. The flat side is the side that REFUSES the aggregate, so
losing it silently is precisely what turns an unobserved host into a green one. That is the
empty-observation narrow -- bottom-as-answer conflated with bottom-as-ignorance -- and DESIGN
already records it as strictly worse than the widen it forbids, because a widen is merely
expensive and a narrow is silently uncovered.

HOW IT SURVIVED TWELVE GREEN WITNESSES, which is the part worth keeping: `[]` is a PERFECTLY VALID
population. The census logic below it was correct, the carriers were correct, and every assertion
stayed green. The wrong thing was the DENOMINATOR, one layer above anything being asserted about.
No amount of testing the classifier could have found it.

AND MY OWN ANNOTATION HELPED IT SURVIVE. The deleted function carried a comment asserting that
"the entry refuses on it separately". That was true of the managed arm and FALSE of the legacy one,
and the sentence bought exactly the reviewer confidence it had not earned. An annotation claiming
a protection is worse than no annotation when the protection is partial.

REPAIRED BY CONSTRUCTION, NOT BY A SECOND CHECK. inventory_identities is DELETED. There is now no
expression anywhere that turns an unavailable inventory into a population: session_population takes
both inventories and either establishes one or returns SessionPopulationUnavailable naming which
read failed. The verdict line and the exit status are both derived from that ONE value, so they
cannot disagree about whether the population was established.

FINDING 2 IS ALSO CORRECT. observation_completed sat in the header, which is bound BEFORE the
per-session memory.current reads, so the receipt named a completion time that did not bound the
observation it described. The header is now assembled after every read and after the verdict, so
the clock is consulted last. Verified by let-ordering: the reads are lines 292-295 and the stamp is
line 355.

THE NEW CONTROL IS MUTATION-TESTED, not merely added. Restoring the old behaviour --
an unreadable system.slice yielding an established population from the managed ids alone -- turns
unreadable_inventory_never_establishes_a_population RED while systemd_units_are_not_session_candidates
stays GREEN. It asserts BOTH directions, so it cannot pass by guarding only the arm that happens to
be checked, and it asserts the readable case still establishes.

FINDING 3 IS DECLINED, for the second time and on the same grounds. Measured against current
main's DESIGN.md (25bf535) with a known-positive control proving the grep works -- "absorbing
fallback" 5 hits, "not-applicable rendered as malformed" 1, "construction-before-validation" 2 --
the claimed gate returns ZERO across five phrasings: walker, mechanical predicate, dissolution
case, predicate-dissolution, canonical fold/query. A Bool PROJECTED from a coproduct that remains
the single authority is the sanctioned shape, stated outright in gunbc.session_residency
session_residency_is_projected_note, and it appears 340 times in dag/gunbc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* A fabricated body fed to a parser, a cast before its filter, and an Option meaning two things -- plus the in-body annotations that broke the build lane

review 56218's three findings are all correct. The fourth item here is mine: the same push broke
required CI, and the reason is worth more than the fix.

FIRST, THE BUILD-LANE BREAK. The v2-emission phase refused with 9 blocking diagnostics, every one
"source annotation sits inside a declaration body". I had put // comments inside function bodies,
which DESIGN 4c admits only at module-item grain. THE REASON I DID NOT CATCH IT IS THE REASON IT
IS WORTH RECORDING: claim_batch is LENIENT about annotation grain, so all thirteen witnesses ran
green over source that strict preparation refuses. My entire local validation loop is blind to
this class by construction. The cheap local check is structural -- an in-body annotation is an
INDENTED // -- and it is now run over every changed .dag before pushing rather than discovered by
a 16-minute CI lane.

The prose is not deleted; it is hoisted into the enclosing declaration's leading annotation, which
is where 4c puts it.

FINDING 2 IS THE STRONGEST AND IS FIXED AT THE RIGHT LAYER. read_memory_count manufactured a body
-- "<read failed: memory.current>" -- and handed it to the PARSER, which classified this
repository's own invented string as CgroupMemoryCountUnparseable. So "we never obtained a body"
and "the kernel wrote bytes we cannot parse" shared one arm, and the `body` field held something
no kernel ever produced. That is fabricated plausible output feeding a state-space conflation, and
it is the not-applicable-rendered-as-malformed class this module's own commentary cites as its bar
-- committed in the same diff that cited it.

THE SPLIT IS PLACED WHERE THE KNOWLEDGE IS. extdeps.linux.cgroup_v2_memory keeps its two arms
UNCHANGED, because it is an authority on parsing a body it was GIVEN; whether a body was obtained
at all is an observation fact, so the third state is added by the layer that performs reads.
read_interface_file now answers CgroupInterfaceBody | CgroupInterfaceReadFailed, and
ObservedMemoryCount / ObservedMemoryLimit carry Read / Malformed / Unread as distinct arms. The
receipt says UNREAD(cause) where it used to say UNREADABLE(<a string we made up>).

FINDING 1: the filter now precedes the cast. It used to cast every split element to NonEmptyStr
and drop empties afterwards. Worth stating plainly because it changes how the old code should be
read: its safety DEPENDED ON THE CAST NOT BEING ENFORCED. The ragged-input witness passes today
precisely because `"" as NonEmptyStr` does not refuse -- so that code was resting on a substrate
gap and would have begun KILLING THE OBSERVATION on the day that wall landed. Filtering first
makes the empty case unreachable at the cast instead of tolerated past it.

FINDING 3: session_demand_membership_gate returned SessionDemandObservation?, where Absent meant
"the gate admits, proceed" and Present meant "the gate produced a terminal verdict" -- one Option
carrying two unrelated meanings. Nothing about "admits" is an ABSENCE; it is a positive finding
with its own payload, and it was the arm a caller most needed to recognise. It now answers
SessionDemandMembershipGate = MembershipAdmitsAggregate { managed_count } |
MembershipRefusesAggregate { observation }.

THE TYPE CHANGE IMMEDIATELY EARNED ITSELF: three witness matches failed to compile until they
handled both arms. The Option form let those same call sites keep compiling while treating a
missing case as "proceed", which is exactly the defect being removed.

13/13 witnesses PASS. Zero in-body annotations across every .dag this branch touches.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* srv3 and srv4 have no sessions.slice at all, and the observer called that "could not be enumerated"

THE LIVE FLEET FALSIFIED THE CARRIER, WHICH IS WHAT THE LIVE RUNS ARE FOR. Having taken srv1 and
srv2, I dispatched srv3 and srv4 to complete the census the operator's capacity policy needs. Both
answered:

  census  UNAVAILABLE  the managed slice could not be enumerated: cgroup child enumeration
                       exited unsuccessfully for /sys/fs/cgroup/sessions.slice

The host log says what actually happened:

  find: '/sys/fs/cgroup/sessions.slice': No such file or directory

There is no sessions.slice on srv3 or srv4. They run CI runners and host no sessions. Nothing was
wrong with those hosts and nothing was wrong with the read -- and the observer reported blindness.

THIS IS THE not-applicable-rendered-as-malformed CLASS, in the carrier whose whole purpose is to
not do that, one layer out from where review 56218 caught its sibling. DESIGN's recognition rule
fires exactly: the arm sat downstream of an enumeration that found nothing to enumerate. Two
distinct consequences were sharing one symbol.

  ABSENT      this host contributes no session demand and never will -- a positive, structural
              fact about the fleet that a capacity policy may rely on
  UNAVAILABLE we are blind, and blindness must never be spent as though it were a zero

Reported as UNAVAILABLE, half the fleet looked broken. Had the arms been folded the other way,
a genuinely unreadable slice on a real session host would have read as "not a session host" and
silently left its sessions out of the aggregate. The second direction is the dangerous one, and
it is why this is a fix rather than a cosmetic relabel.

EXIT STATUS WAS ALSO WRONG. Both runs FAILED. A runner-only host is not a failure to observe.

WHY THE ENUMERATION COULD NOT ANSWER IT, AND WHY A PROBE IS NOT A SECOND CHECK. `find` on a
missing path and `find` on an unreadable one both exit nonzero. An arm keyed on exit status alone
is structurally incapable of separating them, so no amount of care inside observe_cgroup_children
could have fixed this -- the information was never in its hands. extdeps.linux.cgroup_v2 gains
PathIsDirectory (`test -d`, total: 0 for a directory, 1 for anything else, no output to parse), and
its result SELECTS WHICH INVENTORY ARM EXISTS rather than validating one already built.

THE PROBE ITSELF HAS THREE OUTCOMES, NOT TWO. My first cut returned Bool with
HostOperationRefused mapping to false -- which rebuilds this very defect one level further down: a
probe the TRANSPORT refused would answer "the slice is absent", turning a refusal into a structural
claim about the fleet. CgroupPathProbe is Dir | NotDir | Refused, and refusal routes to Unavailable.

THE NEW ARM IS NOT ALLOWED TO BECOME A SECOND ABSORBING ANSWER. An absent system.slice on a host
that HAS a managed slice is not "not a session host" -- every Linux host has system.slice, so its
absence means the probe is lying and the flat side is unknown. That refuses. The witness asserts
it, so NO_SUCH_SLICE cannot quietly admit whatever it is handed.

The witness drives absence and unreadability through the SAME managed input, so it cannot pass by
keying on anything but the arm: fold the two back together and it goes red.

14/14 PASS. Zero in-body annotations -- the §4c structural check ran before the push this time and
caught 23 of them in these very files, which is the local-loop hole the previous commit declared.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Import the variant the bare-reference closure was resolving for us, and record that it WAS resolving

review 56247 is right that CgroupPathIsDirectory was used in session_demand_observe and absent from
its import list. Fixed by adding it.

THE STATED MECHANISM IS REFUTED BY EXECUTION, AND THAT IS THE PART WORTH KEEPING. The review says
"Name resolution will fail". It did not fail. Two independent executions at commit 3ae20e0 --
the commit WITHOUT the import -- ran the code:

  * 14/14 witnesses PASS under claim_batch --hermetic, which resolves the whole closure
  * fleet-converge run 32969294443, headSha 3ae20e0, on srv3, printed
    census NOT_A_SESSION_HOST -- a verdict reachable ONLY by constructing CgroupPathIsDirectory,
    dispatching it, and matching the CgroupPathIsNotDir arm

A verdict that requires the variant to have been constructed is not compatible with the variant
having failed to resolve. The compiler resolved it through the bare-reference closure -- the
mechanism the required run reports as load_bare_reference_closure / bare_eligible -- which searches
corpus-wide for a name no import binds.

WHY IT IS STILL A DEFECT, ON DIFFERENT GROUNDS THAN THE REVIEW GAVE. Bare resolution is
corpus-wide and therefore not stable under other people's edits: the name binds to whatever the
corpus happens to declare, so a second module declaring CgroupPathIsDirectory would silently
retype this call site with nothing local changing and no diagnostic anywhere. An explicit import
makes the referent a property of THIS file. That is a real fragility and the fix is the same fix --
but it is a hygiene repair against silent future retyping, NOT the hard resolution error the
review described.

The distinction is not pedantry. Landing this as "fixed a resolution error" would put a false claim
about the compiler into the PR record, and the next reader would reasonably infer that an unimported
variant refuses -- and design a check, or trust a green run, on that inference. It does not refuse,
which is exactly why the fragility is worth closing by hand.

Everything else in review 56247 is an endorsement, and I have not treated it as evidence of
anything beyond no-blocking-defect-found.

14/14 PASS. Zero in-body annotations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Three counters said UNREAD on a host whose slice does not exist, beside a verdict that said so

srv4's confirming run is the receipt AND the finding. The census reads correctly --
NOT_A_SESSION_HOST, exit success -- and three rows above it on the same receipt, from the same
run, read:

  parent_memory_current  UNREAD(cat exited unsuccessfully for memory.current)
  parent_memory_peak     UNREAD(cat exited unsuccessfully for memory.peak)
  parent_memory_max      UNREAD(cat exited unsuccessfully for memory.max)

`cat` did exit unsuccessfully. It exited unsuccessfully BECAUSE THE PATH IS NOT THERE, which the
counters have no way to say, so absence was rendered as a failed read -- the same
not-applicable-rendered-as-malformed class, one layer below where it had just been repaired, on
the same host, in the run that was meant to confirm the repair.

That is the more useful half of this commit. The class does not live at one site; it lives wherever
a failure channel is asked to carry a not-applicable answer, and fixing the loudest instance leaves
the quieter ones reading as evidence. A reader who trusts the counters would conclude srv4's slice
exists and is unreadable, which is the opposite of what the verdict beside it says.

THE FIX IS CONSTRUCTION, NOT A THIRD COUNTER WORD (DESIGN 5). Adding a NO_SUCH_SLICE spelling to
ObservedMemoryCount would leave the bad state writable and merely relabel it. ParentSliceObservation
wraps the record instead, so THERE IS NO EXPRESSION that produces per-counter values for a cgroup
that does not exist -- the reads are not attempted, so they cannot report anything about not having
happened. The absent arm prints one line and names no read at all.

A REFUSED PROBE STILL READS, AND THE IDENTICAL ARM IS DELIBERATE. If the probe could not run we do
not know whether the slice is there, so the honest move is to attempt the reads and let the
counters report UNREAD on their own evidence -- a true statement about a read that was tried.
Answering ParentSliceAbsent there would convert not-knowing into a structural claim about the
fleet, which is the direction this carrier exists to refuse. The two arms are written separately
rather than merged so a future third probe outcome cannot inherit either behaviour silently.

THE WITNESS IS A PAIR, BECAUSE THE HALF ALONE IS PASSABLE BY A WEAKER CHANGE. An absent slice must
mention no read, AND an observed slice whose counters genuinely could not be read must still say
UNREAD. Asserting only the first would go green against a projection that had simply stopped
printing UNREAD anywhere, which would delete the honest signal instead of the dishonest one.

15/15 PASS. Zero in-body annotations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Aug 27, 2026
…er it when a new hand-Rust stage0 source was registered (#9244) (#9384)

witness_committed_matches_emit_holds was false. The cause is not an edit to
.gitattributes or to its emitter — neither has been touched since #9284, which
is HEAD. It is an input change one authority away.

The unset ("!merge") rows are a COMPLEMENT, derived from
v2.compiler.self_host.stage0_crate_layout hand_maintained_stage0_filenames()
minus the generated-artifact registry overlap
(gunbc.stage0_rust_source_lifecycle_scaffold
derived_hand_maintained_disposition_repo_paths). #9244 added a
SeedRetainedIntrinsicRegistration row for partition_crate_boundary_host, a new
hand-maintained stage0 source. That row is an emission input, so the committed
projection went stale without either file changing. #9244 and #9284 touch
disjoint paths, so nothing conflicted, and the generated-artifact drift gate is
on the floor cut's unguarded list, so nothing refused either.

Effect while stale: partition_crate_boundary_host.rs matched the
src/v1/stage0/src/*.rs glob with no unset row after it, so a hand-maintained
file silently carried merge=generated-artifact — exactly the transposed-row
hazard the emitted header warns about, arrived at from the other direction.

The bytes are the emitter's own candidate, not a hand-edit: built gunbc and ran
tools.generated_artifact_gate main_wet at 7de77a4 on a clean tree; it wrote one
insertion across the whole registry, reproduced here verbatim.

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

0 participants