Skip to content

Step 4A: a host-bound session-demand instrument, with its discriminating pair taken from srv1 and srv2 - #9321

Merged
briansrls merged 11 commits into
mainfrom
session/warm-tern-755-session-demand
Aug 26, 2026
Merged

briansrls merged 11 commits into
mainfrom
session/warm-tern-755-session-demand

Conversation

@briansrls

@briansrls briansrls commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

What this answers

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 vertical cut, proven on real hosts

The instrument is gunbc.session_demand_observe session_demand_observe_ci_wet, dispatched as fleet-converge.yml -f host=<h> -f mode=session_demand_observe at this branch ref. Re-derive from the runs rather than trusting this page.

The discriminating pair came from production, not fixtures — one code path, two hosts, opposite verdicts:

host managed flat scopes verdict
srv1 (run) 23 0 census COMPLETE — AllSessionsManaged over 23 session(s); an aggregate may be established
srv2 (run) 26 1 census INCOMPLETE — 1 unattributable flat docker scope(s); no aggregate may be established over this host

A live fleet finding, not a constructed control

srv2 currently runs one session container flat under system.slice, outside the managed slice. oomd cannot evict it, and any aggregate over srv2's sessions.slice measures 26 of 27 while naming the whole. That is exactly the defect #9280's four-way classification was built to catch, found in production. The scope is docker-a4e17d4a…scope.

Also worth flagging for the capacity work: the two hosts' sessions.slice MemoryMax differ — srv1 137438953472, srv2 68719476736. A single fleet-wide aggregate reservation cannot be right for both.

The metric semantics are the kernel's, not ours

extdeps.linux.cgroup_v2_memory grounds three kinds in the cgroup-v2 documentation rather than coining them:

kind meaning consequence
Instantaneous true at the moment of the read comparable only to a same-moment read
LifetimeEpochScoped extremum since creation or reset not "the max during my window" — the epoch is a field
CumulativeCounter monotone since creation only start/end yields a delta

memory.max/memory.high hold a count or the literal max, so a limit read has four outcomes; count files have no sentinel, so max there is malformed. A negative body is malformed rather than a magnitude.

review 56217 — two real defects, fixed in 5bca9e4851

An unreadable system.slice was rendering as "no flat sessions". inventory_identities answered [] for an inventory that could not be read, which is indistinguishable from one read and found empty — so a failed 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 turns an unobserved host into a green one. That is the empty-observation narrow, which DESIGN records as strictly worse than the widen it forbids.

It survived twelve green witnesses because [] is a perfectly valid population — the census logic below it was correct, the carriers were correct, and the wrong thing was the denominator, one layer above anything being asserted about. It also survived because the deleted function's own annotation asserted "the entry refuses on it separately", which was true of the managed arm and false of the legacy one.

Repaired by construction: inventory_identities is deleted, no expression turns an unavailable inventory into a population, and session_population either establishes one or returns SessionPopulationUnavailable naming which read failed. Verdict and exit status derive from that one value. The new control is mutation-tested — restoring the old behaviour turns it RED while its sibling stays GREEN.

observation_completed was stamped before the per-session reads. Now assembled after every read and after the verdict (reads at lines 292–295, stamp at 355). Note the two timestamps still read equal on srv1 because the observation completes in under a second — that is consistent with the fix, not evidence for it; the evidence is the let-ordering.

A third finding (coproduct→Bool predicates) was declined with a measurement, in this comment.

Two defects only a live run could find

1. {a}/{b} in an argv is a division, not a path. The first dispatch died with TypeError: unsupported operator Div on String after both ListChildCgroups succeeded. The discriminator is in the pair: ListChildCgroups interpolates one value and worked; ReadInterfaceFile carried two interpolations around a literal slash and was the only failure. It typechecks, because both sides really are String. Fixed by composing the path in .dag and interpolating once.

2. A systemd unit is not an unclassifiable session — it is not a session at all. The producer fed every system.slice child into the legacy arm. srv1 has 37 such children and zero are containers (cron.service, containerd.service, …), so it would have marked 37 systemd units as unclassifiable sessions and refused the census of a fully converged host.

That is DESIGN's not-applicable rendered as malformed, exactly: one symbol carrying both "cannot be attributed to a session" and "is not a container at all", which have opposite remedies. Nothing in the carriers could see it — both states genuinely are absent-of-classification, every witness was green, and the census logic was correct. The population was wrong. Only a real system.slice could show that.

This is the argument for the vertical-cut rule: a carrier-plus-fixtures PR would have merged green carrying this, and it would have surfaced later as "the instrument refuses on every host" — reading as a broken instrument rather than a wrong denominator.

Identities first, counts projected

#9280's SliceMembershipSignal carries two Nats — sufficient for its classification kernel, not sufficient as a measurement denominator, because two counts cannot say which containers they counted. The census refuses to project when any identity is unclassifiable and carries the offenders, so the remedy names containers rather than a number.

NoConcurrentSessionDemandObserved (read ran, found none) stays distinct from SessionDemandObservationUnavailable (producer did not run). They grant capacity on opposite grounds.

Why this workflow rather than a new one

GitHub only accepts workflow_dispatch for a workflow on the default branch — but it runs the version from whatever --ref is dispatched. session_demand_observe does not exist in main's copy, and GitHub accepted it anyway, which is what made a pre-merge session-host receipt possible at all.

It cannot actuate. Every actuating step in that job is individually mode-gated, so this mode runs none of plan, apply, or any spark_*. The step reads the host's own cgroups over LocalShell — find and cat under /sys/fs/cgroup, no ssh, no fleet key, no new credential surface.

Verification

  • 13 witnesses PASS under claim_batch --hermetic.
  • The wall is mutation-tested: making the count parser accept the max sentinel turns count_parse_has_no_sentinel_arm RED while limit_parse_discriminates stays GREEN.
  • Witness fixtures use names srv1 actually reported, not invented ones.
  • 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. The producer serializes its own receipt, the workflow prints it, and the runs are addressable.

Not absorbed

Regen repeatedly offered src/v1/stage0/src/partition_crate_boundary_host.rs !merge for .gitattributes. That is main's drift — the .rs has existed since #9244, main's committed file 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 each time; it wants its own cut.

…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>
@gunbai-bot gunbai-bot Bot changed the title srv4 memory upgrade Step 4A (first half, DRAFT): cited cgroup-v2 authorities and an identity-first session-demand carrier Aug 26, 2026
Brian Searls and others added 5 commits August 26, 2026 08:02
…here 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>
…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>
…ot 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>
…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>
…uce 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>
@gunbai-bot gunbai-bot Bot changed the title Step 4A (first half, DRAFT): cited cgroup-v2 authorities and an identity-first session-demand carrier Step 4A: a host-bound session-demand instrument, with its discriminating pair taken from srv1 and srv2 Aug 26, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 26, 2026 10:07
…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>
@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

All three verified against the code. Findings 1 and 2 are correct and fixed in 5bca9e4. Finding 3 is declined, with the measurement below.

Finding 1 — correct, and it was the serious one

You are right, and the direction is what makes it severe. inventory_identities answered [] for an inventory that could not be read, which is indistinguishable from one that was read and found empty. A failed system.slice enumeration therefore 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 flat side is the side that refuses the aggregate. Losing it silently is exactly what turns an unobserved host into a green one — the empty-observation narrow, which DESIGN records as strictly worse than the widen it forbids, because a widen is merely expensive and a narrow is silently uncovered.

Worth stating how it survived twelve green witnesses: [] is a perfectly valid population. The census logic below it was correct, the carriers were correct, every assertion stayed green. The wrong thing was the denominator, one layer above anything being asserted about. No amount of testing the classifier finds that.

And my own annotation helped it survive. The deleted function carried a comment asserting "the entry refuses on it separately" — true of the managed arm, false of the legacy one. That sentence bought precisely the reviewer confidence it had not earned, which is worse than no annotation at all. Thank you for reading past it.

Repaired by construction, not a second check. inventory_identities is deleted. No expression anywhere 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 now derive from that one value, so they cannot disagree about whether the population was established.

The new control is mutation-tested, not merely added: restoring the old behaviour turns unreadable_inventory_never_establishes_a_population RED while systemd_units_are_not_session_candidates stays GREEN. It asserts both inventories failing and the readable case still establishing, so it cannot pass by guarding only the arm that happens to be checked.

Finding 2 — correct, fixed

observation_completed sat in the header, 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. Verified by let-ordering: reads at lines 292–295, stamp at line 355.

Finding 3 — declined, second time, same grounds

I checked against current main's DESIGN.md (25bf535ca1), with a known-positive control so an empty result means something:

control (rules that certainly exist):
  "absorbing fallback"                     5 hits
  "not-applicable rendered as malformed"   1 hit
  "construction-before-validation"         2 hits

the claimed gate:
  "walker"                                 0
  "mechanical predicate"                   0
  "dissolution case"                       0
  "predicate-dissolution"                  0
  "canonical fold/query"                   0

There is no predicate/walker-dissolution gate in DESIGN.md under any of those phrasings, and I could not locate it under others.

What DESIGN.md and the corpus do say points the other way. A Bool projected from a coproduct that remains the single authority is the sanctioned shape — gunbc.session_residency session_residency_is_projected_note states it directly: "THE VERDICT IS A PROJECTION OF THE EVIDENCE, not a second hand-authored table beside it… two representations of one fact (DESIGN section 2)." The failure §2 names is authoring the verdict beside the evidence; deriving it is the repair. The shape appears 340 times in dag/gunbc alone.

I'm not resting on prevalence — you're right that prevalence discharges nothing. The discharge is that SessionPlacement remains the authority and the three predicates are derived from it, each consumed only by a filter inside session_placement_census, which is itself the canonical fold over that type.

If the gate does exist and I'm grepping for the wrong words, quote the sentence and I'll fix it in this PR rather than argue — that offer is genuine, and it's the same one I made when this was raised on #9275.

— sent from warm-tern-755

…ption 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>
@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

All three findings from review 56218 are correct and are fixed at f66060efe0. No refutation this round. There is also a fourth item that is mine rather than the reviewer's, and it is the one worth the most.

The build-lane break was real. The v2-emission phase refused with 9 blocking §4c diagnostics — "source annotation sits inside a declaration body". I had // comments inside function bodies, which §4c admits only at module-item grain. The reason I did not catch it locally is the part worth recording: claim_batch is lenient about annotation grain, so all thirteen witnesses ran green over source that strict preparation refuses. My whole local loop is blind to this class by construction. The cheap structural check is that an in-body annotation is an indented //, and that now runs over every changed .dag before I push. The prose is hoisted into the enclosing declaration's leading annotation, not deleted.

Finding 2 — fabricated body fed to the parser. read_memory_count manufactured "<read failed: memory.current>" and handed it to the parse authority, which duly 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, with a body field holding something no kernel ever produced — fabricated plausible output feeding a state-space conflation, in the same diff whose commentary cites the not-applicable-rendered-as-malformed class as its bar.

The split is placed where the knowledge is. extdeps.linux.cgroup_v2_memory keeps its two arms unchanged: it is an authority on parsing a body it was given, and whether a body was obtained is an observation fact belonging to the layer that performs reads. read_interface_file now answers CgroupInterfaceBody | CgroupInterfaceReadFailed, and ObservedMemoryCount / ObservedMemoryLimit carry Read / Malformed / Unread as distinct arms. The receipt now says UNREAD(cause) where it used to say UNREADABLE(<a string we made up>).

Finding 1 — cast before filter. Fixed by filtering empties before the cast. Worth stating plainly because it changes how the old code reads: 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 rested on a substrate gap and would have started killing the observation on the day that wall landed. Filtering first makes the empty case unreachable at the cast rather than tolerated past it.

Finding 3 — one Option, two meanings. session_demand_membership_gate returned SessionDemandObservation? where Absent meant "the gate admits, proceed" and Present meant "terminal verdict". 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 MembershipAdmitsAggregate { managed_count } | MembershipRefusesAggregate { observation }. The type change earned itself immediately: three witness matches failed to compile until they handled both arms, where the Option form let those same sites keep compiling while treating the missing case as "proceed".

13/13 witnesses PASS; zero in-body annotations across every .dag this branch touches. Live receipts are being re-taken against srv1/srv2 since the carriers changed materially.

— sent from warm-tern-755

@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Live receipts re-taken at f66060efe0, against the changed carriers rather than the ones the reviewer read. Both dispatched at the branch ref through the existing fleet-converge.yml, per the sequencing ruling.

srv1 — run 32962119625, conclusion success:

collector             session-demand-observe-v1
host                  srv1
observation_started   2026-08-26T11:18:19Z
observation_completed 2026-08-26T11:18:20Z
parent_slice          /sys/fs/cgroup/sessions.slice
parent_memory_current 26640707584
parent_memory_peak    55216033792
parent_memory_max     137438953472
managed_population    23
system_slice_children 36
census  COMPLETE  AllSessionsManaged over 23 session(s); an aggregate may be established

srv2 — run 32962874684, conclusion success:

host                  srv2
parent_memory_current 20624510976
parent_memory_peak    48356954112
parent_memory_max     68719476736
managed_population    26
census  INCOMPLETE  1 unattributable flat docker scope(s); no aggregate may be established over this host

Three things worth separating, because they are easy to read as one.

The pair is discriminating, which is what the receipts are for. One host completes and one refuses, on the same code, from a difference in the observed world rather than in the observer. A receipt family where every host answers COMPLETE would establish nothing about whether the refusal arm can fire.

success is the run's conclusion, not the census verdict, and the two are deliberately not the same channel. srv2's observation succeeded — it read the whole population and reached a verdict. That verdict is INCOMPLETE. Conflating "the observer failed" with "the observer established that no aggregate may be taken here" would be exactly the state-space collapse this cut is built to avoid, so the exit status reports whether the observation ran and the receipt reports what it found.

srv2's INCOMPLETE is a real fleet fact, not an artifact. One session container runs flat under system.slice instead of sessions.slice, so it is a session by every other measure and is outside the slice any aggregate would be denominated over. The refusal is correct: an aggregate taken over srv2's sessions.slice today would silently omit a live session. Note also parent_memory_max — srv1 137438953472, srv2 68719476736 — which is a second, independent host divergence and is why step 5 is an operator policy selection rather than something this cut should infer.

system_slice_children 36 on srv1 is the population that falsified my first classifier: they are all systemd units and none is a container, so feeding system.slice wholesale into the flat-session arm would have marked 36 units unclassifiable and refused the census of a fully converged host. The classifier now keys on the observed docker-<64hex>.scope shape, which is why srv2 surfaces exactly one and srv1 exactly zero.

Nothing here touches MemoryHigh, MemoryMax, the 20 GiB reservation, an operator margin, or ctrl actuation.

— sent from warm-tern-755

@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Status at f66060efe0: all three required checks green — build, floor, witnesses. The REQUEST_CHANGES from review 56218 has cleared, since it was head-scoped to 5bca9e485.

This PR is not ready to merge, and the reason is not about this PR. Both scheduled reviews on the current head — 56222 (claude) and 56223 (codex) — are status: failed, with an identical error that never reached a verdict:

Error: Cannot find module '/home/briansrls/ctrl/scripts/api-reviewer/review-with-cli.mjs'

So the readiness summary reads approvals 0/1, and that figure is being produced by nobody could run, not by nobody approved. The two are indistinguishable in the count and have opposite remedies, which is worth stating explicitly rather than leaving as a pending-looking zero.

This is not new to this head. Review 56216, on 01d9083dc at 10:10Z, died with the same missing module — so the outage spans two heads, both providers, and roughly two hours, and 56217/56218 landing in between shows it is intermittent rather than a clean break. The failing path is in the reviewer harness on the host, outside this branch and outside anything this PR touches; nothing here can fix it and no re-push will clear it.

I am not re-pushing to trigger a retry, and I am not treating green checks plus a stranded approval count as merge-ready. Flagging for whoever owns the reviewer harness on srv1.

— sent from warm-tern-755

…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>
@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

The live fleet falsified the carrier, which is what the live runs are for. Having 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. Both runs also exited failure; a runner-only host is not a failure to observe.

This is the not-applicable-rendered-as-malformed class, in the carrier whose entire 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 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. Folded the other way it is far worse: a genuinely unreadable slice on a real session host would read as not a session host, and its sessions would drop out of the aggregate silently. That second direction is why this is a fix and not a relabel.

Why the enumeration could never have answered it. find on a missing path and find on an unreadable one both exit nonzero, so an arm keyed on exit status is structurally incapable of separating them — no amount of care inside observe_cgroup_children would have helped, because the information was never in its hands. extdeps.linux.cgroup_v2 gains PathIsDirectory (test -d; total, 0 for a directory, 1 for anything else, nothing to parse), and its result selects which inventory arm exists rather than validating one already built.

The probe has three outcomes, not two. My first cut returned Bool with HostOperationRefused => false — which rebuilds this exact defect one level down: a probe the transport refused would answer "the slice is absent", converting 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, and the witness asserts it. The witness drives absence and unreadability through the same managed input, so it cannot pass by keying on anything but the arm: fold them back together and it goes red.

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

srv3/srv4 re-dispatched at the branch ref to confirm on the hosts that found it.

— sent from warm-tern-755

…nd 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>
@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

review 56247 is right that CgroupPathIsDirectory was used in session_demand_observe and missing from its import list. Fixed at 44dc0a2029.

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 3ae20e075b — 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 3ae20e075b, 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.

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. Real fragility, same fix — but a hygiene repair against silent future retyping, not the hard resolution error 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 then design a check, or trust a green run, on that inference. It does not refuse, which is precisely why the fragility is worth closing by hand rather than relying on the compiler to catch it.

The rest of review 56247 is endorsement, which I am treating as no blocking defect found and not as evidence the cut does what was asked.

Live receipts now cover all four hosts, and the fourth arm was authored because the fleet produced it rather than because a witness wanted it:

host verdict
srv1 COMPLETE — AllSessionsManaged over 23 sessions; an aggregate may be established
srv2 INCOMPLETE — 1 unattributable flat docker scope; no aggregate may be established
srv3 NOT_A_SESSION_HOST — no sessions.slice; contributes no session demand
srv4 re-running (its last job was cancelled, not failed)

Four hosts, four different arms, each reached from real host state. That is the discriminating evidence the observation cut was asked for.

— sent from warm-tern-755

…de 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>
@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

srv4's confirming run is both the receipt and the next finding, and the second is worth more.

The census reads correctly — NOT_A_SESSION_HOST, exit success. Three rows above it, on the same receipt, from the same run:

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. That is the same not-applicable-rendered-as-malformed class, one layer below where it had just been repaired, on the same host, in the run meant to confirm the repair.

That is the general point. The class does not live at one site; it lives wherever a failure channel is asked to carry a not-applicable answer. Fixing the loudest instance leaves the quieter ones still reading as evidence — and here a reader who trusts the counters would conclude srv4's slice exists and is unreadable, which is the opposite of what the verdict beside them says. Two rows of one receipt contradicting each other, both produced by code that was working as written.

The fix is construction, not a third counter word. 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.

A refused probe still reads, and that 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 say 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 arms are written separately rather than merged so that a future third probe outcome cannot silently inherit either behaviour.

The witness is a pair, because either 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 — deleting the honest signal instead of the dishonest one.

15/15 PASS, zero in-body annotations, pushed at f34d74522d. srv4 re-dispatched to confirm the counters agree with the verdict.

— sent from warm-tern-755

@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

srv4 confirms the repair on the host, and what confirms it is the absence of rows. Run 32972072207, conclusion success, at f34d74522d:

parent_slice           /sys/fs/cgroup/sessions.slice  NO_SUCH_SLICE
managed_population     NO_SUCH_SLICE  /sys/fs/cgroup/sessions.slice
census                 NOT_A_SESSION_HOST  /sys/fs/cgroup/sessions.slice does not exist; this
                       host runs no sessions, so it contributes no session demand and no
                       aggregate is owed over it

There are no parent_memory_current / _peak / _max lines. Not lines saying "not applicable" — no lines. That is the difference between the construction and the third-counter-word alternative I rejected: the reads were never attempted, so there is nothing that could report on not having happened, and the receipt can no longer contradict its own verdict. Three rows previously said the slice was unreadable directly above a verdict saying it does not exist.

Full fleet, four hosts, four arms, each reached from real host state:

host verdict
srv1 COMPLETE — AllSessionsManaged over 23 sessions; an aggregate may be established
srv2 INCOMPLETE — 1 unattributable flat docker scope; no aggregate may be established
srv3 NOT_A_SESSION_HOST — no sessions.slice
srv4 NOT_A_SESSION_HOST — no sessions.slice, counters silent rather than fabricated

On merge readiness, one correction to the summary rather than to the code. dashboard-ops reviews reports approval_count: 1, which reads as the tally floor cleared. That field is not head-joined. Review 56252 approved 44dc0a202; the head is f34d74522, which adds 75 lines across three files — a new ParentSliceObservation type, a changed parent_slice_lines signature, and a new witness. That is precisely the material an approval is meant to have read, so I am not counting it as an approval of this head. Zero on the head, no open REQUEST_CHANGES, MERGEABLE, checks running.

Not merging, and not re-pushing to nudge anything.

— sent from warm-tern-755

@briansrls
briansrls merged commit a851463 into main Aug 26, 2026
5 checks passed
@briansrls
briansrls deleted the session/warm-tern-755-session-demand branch August 26, 2026 15:07
@briansrls
briansrls restored the session/warm-tern-755-session-demand branch August 26, 2026 15:11
briansrls pushed a commit that referenced this pull request Aug 26, 2026
… be UNREAD and the aggregate was still granted (#9347)

* The verdict was a function of identities alone, so every metric could be UNREAD and the aggregate was still granted

A side-chat review raised six findings against #9321. I never saw it -- the dashboard was down
for hours and the PR merged in the meantime -- so these are follow-ups on main rather than
pre-merge repairs. Three are verified here by reading the merged code; the worst is a fail-open.

THE FAIL-OPEN. session_placement_census filters placements on CLASSIFICATION and the verdict is a
function of its result alone. The per-session memory.current reads and the parent slice's three
counters were rendered into the receipt and never consulted. So a host on which EVERY read failed
still printed

  census COMPLETE AllSessionsManaged over N session(s); an aggregate may be established

and exited success. I repaired the DENOMINATOR narrow -- an unreadable inventory cannot establish a
population -- and left the NUMERATOR narrow standing, in the one arm that GRANTS the aggregate.

WHY NO WITNESS CAUGHT IT, which is the part worth carrying forward: all fifteen asserted about
POPULATION and none about METRICS. The suite was exhaustive over the axis I was thinking about and
silent on the axis beside it. Being total at the level examined is not coverage of the level below.

SessionDemandObserved is now constructible only with a complete population AND zero metric
deficits. A deficit names its cgroup and its interface file, because a consumer repairing a refusal
needs to know WHICH read to fix; a count tells nobody where to look. MemoryLimitUnlimited is
deliberately not a deficit -- it is a real parsed kernel answer, and folding it in with unread
would make an answered question look unanswered, which is this carrier's own failure class.

THE PARALLEL REPRESENTATION. SessionDemandReceipt and SessionDemandSample had NO production
constructor anywhere -- they existed only as pattern arms and fixture values -- while
session_demand_observe_ci_wet assembled a tab-separated String directly and derived its exit status
from SessionPopulation separately. A typed model standing beside an operational serializer: two
representations of one fact, in the cut whose entire subject is modeling an observation. The
producer now returns SessionDemandObservation; render_session_demand_observation projects the text
from it and has no other input; process_exit_for_session_demand_observation derives the status from
the same value. The receipt and the exit can no longer disagree because they no longer have two
sources.

THE UNJOINED SUBJECTS. session_population matched host: _ in every arm, so an srv1-managed plus
srv2-legacy pair minted one SessionPopulationEstablished. Production reads both locally under one
observed host so this never fired -- but a type that permits cross-subject evidence will eventually
be handed it, and the witness now drives exactly that pair to a typed HostMismatch.

THE OVERCLAIM, WHICH WAS MINE AND REACHED THE OPERATOR. I wrote that srv2 "runs one session
container flat" and that "the aggregate measures 26 of 27". The producer establishes neither. It
observes a docker scope under system.slice that it CANNOT attribute to an agent session, which is
why it refuses. The scope may well be the missing 27th session; the code declines to assert it and
the prose should not have. The receipt now says unattributed, names each identity, and says the
denominator is unestablished until each is joined to a session-runtime identity or ruled unrelated.

17/17 PASS. Zero in-body annotations.

Not addressed here, deliberately: the read-only mode still inherits WIF auth, fleet-key
materialization and ssh-agent from unconditional job steps, and the receipt still carries a schema
name rather than the executing revision. Both are real; both are separate cuts.

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

* A duplicate match arm compiles clean, and mine shadowed the variant the cut exists to introduce

review 56318 is right on both counts and both were mine.

THE SHADOW ARM. an_unread_metric_refuses_a_complete_population matched
SessionDemandMetricsIncomplete twice: once binding ds and returning count(ds) == 1, then again
with every field wildcarded returning false. The second is unreachable. It made the witness LOOK
like it distinguished two cases when only one was live, in the witness whose entire subject is
that variant.

THE REVIEWER LEFT A CONDITIONAL AND IT RESOLVES THE BAD WAY: "if the .dag compiler doesn't refuse
duplicate variant arms". It does not. The witness ran 17/17 PASS with the shadow arm present, so
this is confirmed by execution rather than by reading -- a duplicate variant arm is accepted, and
the dead one is invisible. That is a real substrate gap and it is exactly the shape the
exhaustiveness check is supposed to cover: the compiler REFUSES a missing arm and ACCEPTS a
duplicated one, so a match can be verified complete while carrying unreachable code the author
believes is live.

HOW IT GOT THERE, because the mechanism matters more than the instance. The type change forced
exhaustiveness failures at nine existing call sites, and I closed them with a blanket text
substitution that appended the four new arms after SessionDemandObserved. My witness had already
written those arms by hand, so the substitution appended a second copy. A dedupe pass in the same
script was written against one exact ordering and silently did not match this one. Automated
editing that cannot fail loudly produced dead code that the compiler then declined to notice --
two silent mechanisms composing, which is why it survived to review.

THE DUPLICATE IMPORTS are the same substitution's other residue: ManagedSession,
SessionDemandPopulationIncomplete, NoConcurrentSessionDemandObserved,
SessionDemandObservationUnavailable and SessionDemandObserved were each imported twice from
gunbc.session_demand, my new head-of-block additions colliding with the pre-existing tail. Deduped
in the PR that introduced the collision rather than left for a later sweep.

17/17 PASS, unchanged, which is the point: deleting unreachable code changes no verdict. The
witness is now what it claimed to be.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant