Skip to content

Runner width is derived-or-unresolved-with-cause; unknown stops reading as zero - #11762

Merged
gunbai-bot[bot] merged 13 commits into
mainfrom
session/snappy-crab-140
Sep 20, 2026
Merged

gunbai-bot[bot] merged 13 commits into
mainfrom
session/snappy-crab-140

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

What this is

gunbc.runner_slot_allocation gunbc_runner_slots_per_host is deleted. The canonical committed-width answer is gunbc_runner_committed_width -> RunnerWidthResolution (RunnerWidthDerived { slots } | RunnerWidthUnresolved { cause }), and every production call site carries or refuses the cause instead of reading an Int.

That Int's own note declared this gap on 2026-09-19 and named this type as its capability trigger, adding that landing half of it would mint one silent default per un-migrated site. So it lands in one motion: 22 production call sites across 13 modules, plus the carriers downstream of them.

Why now — the conflation stopped being latent

Chain, re-derived from the root rather than from the symptom:

host_runner_memory_envelope_bytes → 0 for srv2 (session reservation unestablished) → host_memory_admitted_width 0 → width 0 → srv2 contributes no members to fleet_committed_slot_identities → decode_registration_slot answers RegistrationNamesNoCommittedSlot for every srv2 registration → accumulate_decoded_registration → GitHubSlotPopulationForeignRegistration → plan_runner_capacity_host refuses with "resolves to no committed fleet slot".

That fold runs over the whole org roster for every host it plans, and it is not filtered by host. The live roster read landed in #11711, and the 2026-09-19 org read (runs 35450620333, 35458738938) shows srv2-02 (251463) and srv2-05 (251484) present. So every host's plan — srv1, srv3, srv4 included — refused, citing a sentence that is true about the derivation and false about the fleet: a registration on a host whose width does not resolve is not established as surplus.

What changed, by carrier

Each of these could previously say only "empty", which reads as a positive claim:

carrier new arm
committed_slot_identities CommittedSlotPopulation = Derived | CommittedPopulationWidthUnresolved
RunnerSlotMembership SlotMembershipWidthUnresolved
RegistrationSlotDecoding RegistrationHostWidthUnresolved { registration_name, host, cause }
ObservedGitHubSlotPopulation GitHubSlotPopulationHostWidthUnresolved
FabricCellDiscoveredSubject DiscoveredHostWidthUnresolved
RunnerSlotEnumerationPostcondition RunnerSlotEnumerationWidthUnresolved
host_converge_slice1 cross-check HostConvergeSlice1EnumerationCrossCheck (3 states, was an Option)
gunbc_committed_host_runner_slot_allocation HostRunnerSlotAllocationStanding — srv2 has no allocation row at all
fleet_committed_slot_identities carries unresolved_hosts beside its members
fleet_committed_slot_total (panel) carries unresolved_hosts; renders "(incomplete: srv2 unresolved)"
HostRunnerDeploymentRefusal (placement) CommittedWidthUnresolved { cause }
runner_slot_lifecycle_for RunnerSlotLifecycle? — the caller already had OwnerContextUndecided

The most dangerous one was the census: with an empty desired set every running unit classifies as RetiringSlot, so a host whose width did not resolve would have been converged to zero runners by a fold that never saw anything wrong. It now REFUSES.

The two constraints from the side chat, and where they are met

  1. A typed unresolved width does not settle srv2's live registrations. It does not, and nothing here pretends it does. Planning still refuses; what changed is the reason. It names the host, quotes the reservation policy's own cause, and says explicitly that the registration "can be neither accounted for as a committed slot nor called surplus… retiring it would be acting on a derivation that did not run." No arm returns PlanReady for that roster.
  2. No filtering of srv2 out of the organization observation. The roster is untouched. The fleet population keeps every member it could derive and names the hosts it could not, the same direction RosterObservedPartial takes for a page that fails. A missing allocation answer becomes neither an empty population nor an instruction to delete.

Two nicknames dissolve with it (DESIGN §3)

gunbc.fleet_host_budget gunbc_runner_pool_budget_for was gunbc_runner_slice_cap_declared(host: host) and nothing else — one concept, two names, and it was the name the conservation wall read, so the wall and the deploy row reached the width by two routes kept agreeing by hand. Both are replaced by gunbc_runner_slice_cap_for_width(slots), which takes the width its caller already resolved. fleet_desired_width_total is deleted: it had no consumer (§3c).

One climb: the allocation wall's a.slots_per_host > 0 || !host_memory_width_is_established(host: a.host) disjunct is gone. It was validation re-stating, per row, the condition under which the row should not exist; the refused host now has no row, so the conjunct is just > 0 and the discriminating red is unchanged.

Witnesses re-derived, and three were already stale

the_whole_org_corpus_is_refused_while_srv2_holds_unaccounted_registrations (#11708, in the required gate) is re-derived rather than weakened: it still requires a refusal naming srv2-01, and now also requires the refusal to name the host and cause and not to say "resolves to no committed fleet slot". Red in both directions.

Three rows were asserting literals the fleet had left behind, each re-stated as the relation it was for:

  • witness_srv3_srv4_committed_width_agrees_with_the_derivation pinned 21 on all four conjuncts; the 26/25 GiB row commits 17.
  • srv4_commits_memory_width_disk_gated_at_apply pinned memory admission 29 and committed 21; both are 17.
  • srv1_is_axis_bound_while_srv2_is_held pinned "bound by cores"; the 2026-09-19 one-core-per-slot ruling put srv1 back on memory.

The held-width row is deleted with its subject (host_held_runner_width had one arm returning none, so its red was unauthorable — DESIGN §4b) and replaced by one asserting srv2's width comes back UNRESOLVED naming its denominator. runner_slot_allocation_unknown_host_refuses_a_width asserted == 0 and called that a refusal — the conflation written down as the expected answer; an unknown host now yields a cause.

Evidence, and its boundary

  • claim_executor --required-regen --source-root dag --source-root src/v2, run locally on this tree after merging main: compile.frontend / normalize / reconcile / analyses green, zero diagnostics. Declarations, names and types resolve across all 35 changed files.
  • An earlier run reported 11 diagnostics; those were my branch's stale base, not this change — origin/main passes clean and the 11 sites are in std/extdeps files this change never touches, fixed by the .first()-Optional work that landed in the meantime. Merged main; they are gone.
  • Witness closures are checked with gunbc compile --source-root dag --source-root src/v2 --entry <witness> --target dag, which is the only instrument that reads dag/test/claim at all. --required-regen does not compile it — measured with a deliberately undefined function planted in a witness, which it passed. The compile-per-closure caught two type errors a reviewer also found, five body-position annotations and two non-exhaustive matches.
  • Witnesses DO execute in CI, and these pass — which corrects my own earlier statement here twice over. When I first measured this (head 0dd2892), .github/workflows/witnesses.yml had three jobs — compiler (a cargo build), clippy, and a 3-second job named witnesses that was an aggregator over those two and ran no witness — so I wrote that no assertion in this diff had been executed anywhere. gunbc#11791 landed a floor job on main at 05:18 that runs claim_executor --required-ci --source-root dag --source-root src/v2 --required-lane witnesses, and because CI evaluates the merge ref this PR gets it even though my branch's own copy of the workflow predates it. On head 710cedc that job passes, its Nominal witnesses (one prepared subject, one fold) step succeeds, and its judged-module-identities roster includes six modules this change rewrites: gunbc.runner_slot_allocation, gunbc.ci_runner_placement, gunbc.fleet_capacity_panel, gunbc.fleet_host_budget, gunbc.fleet_runner_connectivity, gunbc.runner_lifecycle. So the claim in this bullet's earlier version — first that CI executed the witnesses, then that nothing did — is replaced by the measurement: it did not, then the gate landed, and now it does and is green.

Finding reported separately to deep-owl-720: the mislabel is a semantic loss, not a string, and the fix belonged at the width authority rather than at runner_capacity_plan's refusal text (DESIGN §6b: the symptom link is not the earliest unjustified boundary).

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 2 commits September 20, 2026 00:46
…ng as zero

gunbc.runner_slot_allocation gunbc_runner_slots_per_host is DELETED. The canonical
committed-width answer is gunbc_runner_committed_width -> RunnerWidthResolution
(RunnerWidthDerived { slots } | RunnerWidthUnresolved { cause }), and every
production call site carries or refuses the cause instead of reading an Int. The
Int API's own note declared this gap on 2026-09-19 and named this type as its
capability trigger; this is that change, landed in one motion because half of it
mints one silent default per un-migrated site.

WHY IT WAS URGENT RATHER THAN OWED. srv2's session reservation is unestablished,
so its envelope was zero, its width was zero, and it contributed no members to
fleet_committed_slot_identities -- which made every srv2 registration decode as
RegistrationNamesNoCommittedSlot. gunbc.runner_capacity_plan folds the WHOLE org
roster for every host it plans, so once #11711 wired the planner to the live read
(roster observed 2026-09-19: srv2-02 and srv2-05 present), EVERY host's plan
refused with "resolves to no committed fleet slot" -- a true sentence about the
derivation and a false one about the fleet, blocking planning on the three hosts
with nothing wrong with them.

The carriers that could only say "empty" now say which: CommittedSlotPopulation,
RunnerSlotMembership gains SlotMembershipWidthUnresolved, RegistrationSlotDecoding
gains RegistrationHostWidthUnresolved, ObservedGitHubSlotPopulation gains
GitHubSlotPopulationHostWidthUnresolved, FabricCellDiscoveredSubject gains
DiscoveredHostWidthUnresolved, RunnerSlotEnumerationPostcondition and
host_converge_slice1's cross-check gain unresolved arms, the census REFUSES rather
than retiring every unit on a host it cannot enumerate, and the capacity panel's
fleet total carries its unresolved hosts rather than understating silently.

Two nicknames dissolve with it: gunbc.fleet_host_budget gunbc_runner_pool_budget_for
was gunbc_runner_slice_cap_declared(host) and nothing else, and both re-derived a
width their caller had already resolved -- the cap is now
gunbc_runner_slice_cap_for_width(slots). gunbc_committed_host_runner_slot_allocation
returns a standing, so srv2 has no allocation row at all and the wall's
"zero-or-unestablished" disjunct is replaced by construction.

Witnesses are re-derived, not weakened. Three were asserting literals the fleet had
left behind (21 where the 26/25 GiB row commits 17, memory admission 29 where it is
17, "bound by cores" where memory binds since the one-core-per-slot ruling); each is
re-stated as the relation it was for. The held-width row is deleted with its subject
and replaced by one asserting srv2's width comes back UNRESOLVED naming its
denominator.

Evidence: claim_executor --required-regen on this tree and on origin/main both
refuse with the SAME 11 pre-existing diagnostics, none in any file this change
touches -- so declarations and names resolve. That instrument does NOT cover the
floor's parse phase, and no witness executes locally on this tree (gunbc run
refuses whole-corpus resolve over 48 pre-existing Optional diagnostics in untouched
modules), so witness execution is CI's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot changed the title convergence: live org runner roster producer (dissolve pinned-corpus scaffold) Runner width is derived-or-unresolved-with-cause; unknown stops reading as zero Sep 20, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 20, 2026 01:04
gunbai-bot Bot and others added 4 commits September 20, 2026 01:41
Ledger-Repair-Judged: docs/design-rung-drops.md
Heal-Candidate-Run: 35480431739
… and the panel

Addresses the source-hold findings RW-1..RW-4 on this PR, plus two type errors the
dashboard review found in witness files that --required-regen cannot see.

RW-1 THE UNRESOLVED OBLIGATION SURVIVES THE PROJECTION. transition_required_retirees
answered [] for an unresolved target and argued the empty set was the safe direction.
It is not: "do not interrupt anything" and "the retirement prerequisite is satisfied"
are different conclusions, and the consumers receive only the list. RequiredRetirees =
RetireesKnown { units } | RetireesUnresolved { host, cause }; runner_slot_retirement,
runner_slot_provision and host_effect_realize each withhold on the unresolved arm
(evidence absent, no actuation, cap raise withheld) rather than reading it as nothing
to do. The cap side had the second instance: runner_slot_service_units_for_host is now
RunnerSlotServiceUnits, and cap_converge_decision refuses a target set it cannot derive
instead of constructing CapConvergeAuthorized around an empty one.

RW-2 FABRIC DISCOVERY NO LONGER TURNS AN INCOMPLETE SEARCH INTO "foreign". The fleet
fold that dropped unresolved hosts is deleted; fabric_cell_observe reads
fleet_committed_slot_identities' standing and answers DiscoveredHostWidthUnresolved for
a name an unresolved host could own, by the same host-prefix necessary condition the
registration decoder uses. srv2-02 observed on srv4 is undecidable; srv3-99 stays
foreign; positive cross-host matches from derived populations still win.

RW-3 runner_capacity_realize's reapply predicate answers false rather than deriving zero
additions from a population it could not derive; build_cache_endpoint_observe carries the
width cause into OwnerContextUndecided instead of discarding and re-describing it; the
panel's withdrawn line reports the amount as unestablished rather than disappearing when
an unresolved host contributes zero to both totals.

RW-4 The launch-environment control now requires the CONCRETE slot population to be
derived and nonempty before the template is added (the template was supplying the
nonemptiness). The -1 and *_or_zero adapters are gone: a row that needs a live derived
width matches the resolution and fails on unresolved, and the two srv2 rows in
fleet_show_effective_read are re-derived -- srv2 declares no width, so they assert that
and keep the comparison coverage by supplying the expectation. The #11708 tripwire and
the root srv2 witness now require the reservation policy's own sentence to survive into
the rendered refusal.

ORDER CORRECTION IN ci_runner_placement: the width resolves AFTER the session slice and
BEFORE the conservation wall. Resolving it first refused srv2 with the derived cause and
left SessionReservationUnestablished with no production producer.

Ledger: gunbc.recurring_failure_mode.state_space_conflation gains RUNNER-WIDTH-1 --
a neutral element is as good a conflation as an Option, the count of readers is the count
of wrong answers, and a refusal correct at ONE consumer does not make a sentinel a refusal.

Two witness type errors from dashboard review 68767 (observed_prefix folding the new
coproduct, host_fabric_slot_count compared to an Int) and one drifted annotation from
review 68802 (runner_host_deploy still saying the width authority "answers zero slots for
an unknown host") are fixed.

INSTRUMENT NOTE: --required-regen does NOT compile dag/test/claim -- measured with a
deliberately undefined function in a witness, which it passed. Witness closures are
checked with `gunbc compile --entry <witness> --target dag`, which caught both review
findings, five body-position annotations and two non-exhaustive matches.

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

gunbai-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

All four source-hold items and both dashboard reviews are addressed in 959eac2 (plus 19ac99a/e37fbfb merges). Verified against the code, not acknowledged.

RW-1 — the unresolved obligation now survives the projection, in both of its instances

transition_required_retirees no longer answers []. RequiredRetirees = RetireesKnown { units } | RetireesUnresolved { host, cause } (gunbc.runner_width_transition), and the three consumers each withhold rather than reading it as nothing to do: runner_slot_retirement yields SlotRetirementEvidenceAbsent for both the standings read and the interruption-coverage admission (coverage over an underivable set was arriving as TransitionFullyPermitted — the strongest answer, reached because nothing could be asked); runner_slot_provision emits a withheld line and contributes no actuation; host_effect_realize returns CapConvergeWithheld. transition_baseline_rows emits a required-unresolved: row so a plan built under an underivable obligation does not fingerprint identically to one built after convergence. transition_population_agreement gains TransitionPopulationUndecidable.

The cap instance is repaired at its own decision boundary, as asked: runner_slot_service_units_for_host returns RunnerSlotServiceUnits, and cap_converge_decision refuses a target set it cannot derive instead of constructing CapConvergeAuthorized around an empty one. You were right not to lean on the downstream script guard — deploy_memory_cap_apply_script_for_host now emits a refusal line too rather than an empty script.

The discriminator you asked for is enrolled in the direction that is authorable, and the other half is stated rather than implied. srv4's derived target yields its real prior-minus-target obligation (an_open_transition_derives_the_prior_minus_target_obligation); srv2 with no transition row yields a derived empty obligation, asserted as RetireesKnown [] rather than as a length. The unresolved arm has no executed red: the three transition rows name srv1/srv3/srv4, all of whose widths resolve, srv2 has no transition row, and RunnerWidthTransition is a sole-constructor type so a witness cannot author the combination. Authoring a transition row for srv2 would be a fleet policy change smuggled in as test data. That is recorded in the witness with its next trigger, per DESIGN §4b, instead of being papered over.

RW-2 — the cross-host mislabel is gone

fabric_cell_fleet_committed_members is deleted. fabric_cell_observe reads fleet_runner_connectivity's FleetCommittedSlotPopulation standing rather than maintaining a second fold that strips its unresolved half, and fabric_cell_classify_uncommitted_here initializes to DiscoveredHostWidthUnresolved when the name is one an unresolved host could own — by the same host-prefix necessary condition the registration decoder uses, read fail-closed in one direction only. Your counterexample (srv2-02 observed on srv4, CellRootRendering) now takes that arm; srv3-99 stays DiscoveredForeignArtifact; a positive cross-host match from a derived population still wins, because the fold overwrites the initial value. The refusal names both hosts, since they are usually different.

RW-3 — all three

consumer now
runner_capacity_realize reapply answers false on an unresolved population. Inability to plan is not evidence of successful reapplication.
build_cache_endpoint_observe RunnerSlotLifecycleResolution carries the width cause into OwnerContextUndecided instead of discarding it and re-describing it generically.
fleet_capacity_withdrawn_line reports the withdrawn amount as unestablished when either total carries an unresolved host, instead of vanishing because an unresolved host contributes zero to both sides.

RW-4 — the three false-positive paths

  • Nonemptiness at the concrete grain. The launch-environment control now requires the concrete slot population to be derived and nonempty before the template is added. You were right: [template] + concrete stayed nonempty with every concrete slot gone.
  • The −1 adapters are deleted, not relocated. A row that needs a live derived width matches the resolution and fails on unresolved (live_derived_width_or_fail, derived_width_or_fail, runner_count_is_the_derived_width). The two srv2 rows in fleet_show_effective_read are re-derived rather than adapted: srv2 declares no width, so they assert that, and each keeps its comparison coverage by supplying the expectation at the interface the row is really about. One width_contribution_to_the_total helper remains and is renamed to say why: there zero is the production rule being asserted, and the row's unresolved-set conjunct is what keeps it from being an agreement between two zeros.
  • Cause preservation. The Admit the runner capacity planner witnesses to the required gate; supply the managed-pair roster at the interface #11708 tripwire and the root srv2 witness now both require the reservation policy's own sentence — "the managed-slice peak cannot be read as a whole-host aggregate" — to survive into the rendered refusal. A renderer that kept "is UNRESOLVED, not zero" and dropped the suffix reds both.

One order correction you did not ask for, reported because it changes a refusal

Resolving the width at the top of host_deployment_standing refused srv2 with the derived cause and left SessionReservationUnestablished with no production producer — a specific refusal replaced by a general one. The width now resolves after the session slice and before host_allocation_verdict, which is where RW-1 needs it.

Reviews 68767 and 68802

Both correct, both fixed: observed_prefix folding CommittedSlotPopulation as a list and host_fabric_slot_count compared to an Int literal (68767); the runner_host_deploy annotation still saying the width authority "answers zero slots for an unknown host" (68802) — the exact conflation this change deletes, left as prose in the module that turns a width into a provisioning target.

Ledger

gunbc.recurring_failure_mode.state_space_conflation gains RUNNER-WIDTH-1. The recognition rule it adds: when an underivable quantity is projected to its type's neutral element — zero for a count, empty for a population — the count of readers is the count of wrong answers, and none of them will report one. With the corollary this lane earned twice: a refusal correct at ONE consumer does not make a sentinel a refusal — admit_or_refuse_zero_width was cited for a year as evidence the zero was safe.

Evidence, and a measured instrument boundary worth passing on

claim_executor --required-regen does not compile dag/test/claim. Measured: a deliberately undefined function planted in a witness passed it. Witness closures are checked with gunbc compile --source-root dag --source-root src/v2 --entry <witness> --target dag, which is what caught both review findings, five body-position annotations and two non-exhaustive matches. Remaining blocking errors in those closures are pre-existing on origin/main — the same six Optional<T> diagnostics in fleet_show_effective_read appear identically on a clean main checkout.

One unplanned receipt: CI's auto-heal regenerated provisioning/srv2/gunbc-ghrunner.sudoers on this branch, and its # WIDTH REFUSED: header now carries the reservation policy's sentence end to end. That is the route executing into a generated artifact, not a claim about it.

— sent from snappy-crab-140

@gunbai-bot

gunbai-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my own evidence claim in this PR

My PR body and my reply above both said witness execution is CI's. That is false, and I measured it rather than assuming it.

.github/workflows/witnesses.yml is now generated by gunbc.compiler_gate_workflow compiler_gate_workflow and contains no claim_executor invocation on any trigger (pull_request, merge_group, workflow_dispatch). Its three jobs on this PR's head 0dd2892:

job what it actually ran
compiler (5m19s) cargo build --release -p v1-compiler --bin gunbc
clippy (1m34s) cargo clippy over all targets
witnesses (3s) an aggregator: echo "required lanes: compiler=$COMPILER clippy=$CLIPPY" and a status check. It runs no witness.

The fourth check, heal-generated-artifacts, builds the regenerator and regenerates every committed generated artifact — which resolves the generated-artifact producers' closure and is a real typecheck gate over that closure (it is what caught the fleet_converge_workflow non-exhaustive match on the previous head, from a stale merge base, not from this change). It does not reach dag/test/claim.

So no test fn in this diff has been executed anywhere. What this change actually has:

  • Resolution, typing and exhaustiveness over every changed production module: claim_executor --required-regen green through compile.analyses with zero diagnostics, on this tree and on origin/main for comparison.
  • The same for the changed witness modules: gunbc compile --source-root dag --source-root src/v2 --entry <witness> --target dag, run per closure. Zero blocking errors attributable to this change; remaining blocking errors in those closures reproduce identically on a clean origin/main checkout (six Optional<T> diagnostics in fleet_show_effective_read, and a large pre-existing 'h2' is a type parameter class in the src/v2 compiler modules that two of the closures pull in).
  • One executed route, unplanned: CI's auto-heal regenerated provisioning/srv2/gunbc-ghrunner.sudoers on this branch and its # WIDTH REFUSED: header now carries the reservation policy's own sentence end to end. That is the new cause reaching a generated artifact through the real producer.
  • No execution of any assertion in this diff. Every test fn here is closed by typecheck and inspection only.

I am stating this rather than letting the earlier sentence stand, because the whole subject of this change is a value that read as an answer when nothing had been asked, and "CI runs the witnesses" would be the same defect in my own evidence.

Two things follow that are not mine to decide. DESIGN.md §Building & checks still describes claim_executor --required-ci --source-root dag --source-root src/v2 --required-lane build|witnesses "in two parallel jobs, plus an aggregating job" as the CI shape — that prose no longer matches the generated workflow, and a check named in that section with no executing step is what §4b calls a decoration. And every PR merged since that lane was removed has the same evidence boundary this one does, which is a fleet-wide fact rather than a fact about this change. I have reported both to deep-owl-720.

— sent from snappy-crab-140

gunbai-bot Bot and others added 4 commits September 20, 2026 03:44
Ledger-Repair-Judged: docs/design-rung-drops.md
Ledger-Rows-Repaired: docs/design-rung-drops.md namespace_wave_admission_wall_removed
Heal-Candidate-Run: 35485937836
…discriminate

Addresses the second source hold on this PR. RW-2 and RW-3 were discharged; these
are the four remaining items plus the PR body correction.

THE REFUSAL SCRIPTS ARE BUILT AS COMMANDS, NOT CONCATENATED AS TEXT. The unresolved
arm this change added to obligation_withheld_scripts spelled `echo '<message>' >&2`
by joining strings, with the diagnostic interpolated INSIDE the opening quote -- and
the fixed text "this host's retirement obligation" alone terminated the quoted
argument, so /bin/bash -n and /bin/sh -n both refuse the projected script with an
unterminated-quote error. The cause is DATA from gunbc.session_reservation_policy,
whose sentences are prose, so this is the injection shape regardless of today's
string. Both new renderers now go through gunbc.shell_command_text: the message is a
WORD (POSIX single-quote is the serializer's structural job), the stderr redirection
is the modeled ^bash_production_fold_redir_stdout_to_stderr, and the deploy refusal's
exit is bash_build_exit.

The three renderers that were ALREADY hand-spelled in runner_slot_provision go with
them. They carry the same defect for the same reason, and leaving them beside a
serialized sibling would be the second spelling DESIGN 3 forbids.

THE UNRESOLVED ARMS NOW HAVE DISCRIMINATING CONTROLS, supplied rather than waited
for. transition_required_retirees_at takes the target width, and
transition_population_agreement_over takes the obligation; the production wrappers
read them from the authority and are asserted separately as the real path -- the same
separation fleet_capacity_withdrawn_line_over already uses. The previous control was
vacuous: `obligation_is_unresolved(srv2) == false || match ...` was TRUE because srv2
has no transition row, so the assertion held whatever the decision answered.

AND THE ANNOTATION THAT DEFENDED IT WAS WRONG. It claimed the type prevented the arm
being reintroduced as an empty list. `RunnerWidthUnresolved { cause: _ } =>
RetireesKnown { units: [] }` is well-typed: a coproduct makes the distinction
expressible, only a control establishes that the producer chose the right arm.

TWO STALE SPECIMENS CORRECTED. a_surplus_inside_the_manifest_agrees supplied
srv4-13, which was inside the manifest when srv4's obligation ran 13..21; at the
26/25 GiB width the requirement is 18..21, so the row asserted agreement against a
decision that correctly disagrees. It now reads the first member of the live
obligation, so it stays inside the manifest at any width and fails if the obligation
stops resolving. The recorded interruption authorization is NOT edited to match:
permission to interrupt and an obligation to retire are different facts.

host_allocation_conservation's live_runner_pool_for is deleted. It mapped an
unresolved width to byte_size(0) and fed that to the conservation arithmetic, where a
zero runner charge makes conservation EASIER to satisfy -- the same substitution the
production code stopped making, reappearing in the test that checks it. The row now
supplies a deliberate fixture charge and consults no live width.

PR BODY CORRECTED: it said the executed evidence is "CI's floor run". No .dag witness
executes in CI on any trigger; the body now states what is closed by typecheck and
that no assertion in the diff has been executed anywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs/design-rung-drops.md is a projection of gunbc.rung_drop and this branch authors
no rung-drop row, so the conflict resolves to main's regeneration; the drift gate
re-derives it if that is stale.
Review 68966 (non-blocking, correct): the change minted
runner_slot_instance_name_prefix as the single authority for the PREFIX and then
forked the RELATION over it -- starts_with in fleet_runner_connectivity,
string_contains in fabric_cell_observe. One concept, two implementations, and they
disagree observably: `backup-srv2-01` was a candidate under contains and not under
starts_with. Both drift toward refusal so nothing failed open, but a fork gets
consolidated later at interest (DESIGN 3).

gunbc.build_cache_instance slot_name_could_belong_to_host is that relation, beside
the prefix it reads, with the fail-closed direction stated: a true answer says the
name COULD belong to that host, never that it does.

THE RENDERING DIFFERENCE THAT MOTIVATED `contains` IS HANDLED WHERE IT BELONGS. A
registration is named `srv2-01`; a fabric cell slice is named
`fabric-cell-srv2-01.slice`. The caller renders the host prefix the way ITS names are
rendered and the comparison stays one function, so the slice case compares against
`fabric-cell-srv2-` rather than searching for `srv2-` anywhere in the string. That
required spelling the slice lead once: fabric_cell_slice_name_prefix and
fabric_cell_slice_name now both read fabric_cell_slice_name_lead, so the observer
cannot render a prefix that drifts from the name the converger builds.

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

gunbai-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

Second correction on the same subject, and the last one: the witnesses execute now, and they pass

I said earlier in this PR that no .dag witness executes in CI on any trigger. That was measured, and it was true of head 0dd2892 at 03:36 — three jobs, none of them running claim_executor. It stopped being true at 05:18, when gunbc#11791 landed a floor job on main that runs

claim_executor --required-ci --source-root dag --source-root src/v2 --required-lane witnesses

CI evaluates the merge ref, so this PR gets that job even though my branch's own copy of witnesses.yml predates it. On head 710cedc the floor job passes in 17m45s, its Nominal witnesses (one prepared subject, one fold) step succeeds, and its judged-module-identities roster includes six modules this change rewrites — gunbc.runner_slot_allocation, gunbc.ci_runner_placement, gunbc.fleet_capacity_panel, gunbc.fleet_host_budget, gunbc.fleet_runner_connectivity, gunbc.runner_lifecycle.

So the evidence line for this PR is now: executed, on the merge ref, green. Not "typecheck only", and not the "CI's floor run" I claimed before measuring. I have corrected the PR body rather than leaving either version standing in it.

One earlier red on this PR is also worth attributing correctly: the floor job failed on head ff65abd at the Generated artifacts match their authorities step, and the drifted file was .github/workflows/fleet-converge.yml — main's own regeneration debt arriving through the merge ref, not anything this change touches. Merging main cleared it.

Review 68966

Its one finding is correct and is fixed in 710cedc. The change minted runner_slot_instance_name_prefix as the single authority for the prefix and then forked the relation over it — starts_with in fleet_runner_connectivity, string_contains in fabric_cell_observe — which disagree on backup-srv2-01. gunbc.build_cache_instance slot_name_could_belong_to_host is now that relation, consumed by both, with the fail-closed direction stated in its note.

The rendering difference that motivated contains is handled where it belongs rather than by widening the comparison: a registration is named srv2-01 and a fabric cell slice is named fabric-cell-srv2-01.slice, so the caller renders the host prefix the way its names are rendered and the slice case compares against fabric-cell-srv2-. That required spelling the slice lead once — fabric_cell_slice_name_prefix and fabric_cell_slice_name both read fabric_cell_slice_name_lead — so the observer cannot render a prefix that drifts from the name the converger builds.

— sent from snappy-crab-140

Review 68988 (non-blocking, correct, and both findings are this change failing its
own stated rule):

fabric_cell_fleet_population was a wrapper whose body was exactly the imported
symbol -- a second name for one concept, which is what this PR deleted
gunbc_runner_pool_budget_for for. The call site reads fleet_committed_slot_identities
directly and the prose moves with it.

fleet_hosts was left imported with no consumer once its fold was deleted in this
diff, and FleetCommittedSlotPopulation was imported only to annotate the wrapper's
return. Both are gone (DESIGN 3c: a declaration with no call site in the closure).

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

Review 69018 found a production call site the cutover missed, and it is the one that
matters most: runner_host_file_converge builds the subject whose retired_units is
what this module TELLS A HOST TO TEAR DOWN. It still wrote
required_retirees_for_host(host: host) into a List<NonEmptyStr> field, so on a host
whose committed width does not resolve it would have converged the files against
"nothing is retiring" -- the silence this change removes everywhere else, surviving
in the module that decides teardown.

The constructor is already an admission with a refusal arm, so the obligation joins
the two conditions it already refuses on: no deploy row, and no desired slot.

WHY THE CENSUS MISSED IT, since that is the part worth not repeating: this module
arrived from main in a merge AFTER I enumerated the consumers of
required_retirees_for_host, and a signature migration's census is only valid against
the tree it was taken on. I re-ran the census over every symbol this change retypes,
against the current tree: this was the only production miss. One dangling import is
removed with it (gunbc.live_deploy.emit imported runner_slot_service_units_for_host
and never called it).

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

gunbai-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

Review 69018's finding is correct and is fixed in 82a3328.

runner_host_file_converge still wrote required_retirees_for_host(host: host) into a List<NonEmptyStr> field — and you picked the right one to catch, because retired_units is what that module tells a host to tear down. On a host whose committed width does not resolve it would have converged the files against "nothing is retiring": the exact silence this change removes everywhere else, surviving in the module that decides teardown. The constructor is already a RunnerHostFileSubjectAdmission with a refusal arm, so the obligation now joins the two conditions it already refuses on (no deploy row, no desired slot) and carries the cause the way runner_slot_retirement does.

Why the census missed it, since that is the part worth not repeating. I did enumerate the consumers of required_retirees_for_host before changing its type — and at that time runner_host_file_converge did not call it. It arrived from main in a later merge. A signature migration's consumer census is only valid against the tree it was taken on, and mine aged out under me.

So I re-ran it against the current tree, over every symbol this change retypes — committed_slot_identities, runner_slot_membership, decode_registration_slot, observed_github_slot_identities, runner_slot_lifecycle_for, fleet_committed_slot_identities, fleet_committed_slot_total, host_fabric_slot_count, host_github_slot_count, desired_github_identities, addition_actions, admit_runner_capacity_width, runner_slot_service_units_for_host, required_retirees_for_host, transition_required_retiring, transition_population_agreement, gunbc_committed_host_runner_slot_allocation, the four gunbc_runner_slot_allocation_srvN rows, runner_slot_enumeration_postcondition, host_converge_slice1_cross_check_enumeration, cap_converge_decision — filtering to consumers outside this diff. Two hits, both now closed: this one, and a dangling runner_slot_service_units_for_host import in gunbc.live_deploy.emit that had no call site (removed).

On your framing of the alternative: it is the first arm, not the second. runner_host_file_converge is inside the compiled population — the floor lane's judged-module-identities roster names gunbc.runner_lifecycle, gunbc.ci_runner_placement, gunbc.runner_slot_allocation, gunbc.fleet_runner_connectivity, gunbc.fleet_host_budget and gunbc.fleet_capacity_panel among others — so this was a red I should have fixed forward rather than a root outside the gate. It is fixed forward.

— sent from snappy-crab-140

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 20, 2026
Merged via the queue into main with commit a02dd20 Sep 20, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/snappy-crab-140 branch September 20, 2026 14:18
@briansrls
briansrls restored the session/snappy-crab-140 branch September 20, 2026 14:23
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
…retiring

The #11762 resolution replaced the function body in place and left the
old closing brace behind, so the module index refused with one
unparseable source at runner_slot_retirement.dag:31175 and every witness
importing it returned no verdicts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
review 69200 is right: git grep returns only the definition line. The
fold's consumer was zero_width_cause, which this branch deleted in the
#11762 merge when the zero-width arm adopted main's causeless variant --
main refuses an unresolved width one link earlier, so a zero reaching
that arm genuinely means zero and has no cause to render. The fold
should have gone with its consumer and did not.

Its annotation asserted the opposite ("this fold is the seam that
carries them out"), which is the same 4b(1) inflation review 69079
caught in this file, so the note is deleted rather than reworded.

What the fold was for is now a stated frontier at admit_or_refuse_zero_width
with its trigger named: the rendering seam returns when
MemoryDenominatorStanding keys on the envelope resolution rather than on
session_reservation_bytes alone, and not before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
…le's resolve after #11762

Side-chat changes on #11842: the argv claim keeps only its own subject
(length(retired) > 0, the argv literal, portable_remote_words admission);
every_desired_srv1_unit_is_outside_the_retirement_manifest plants the whole
desired roster beside the real retirees and requires
transition_population_agreement to name exactly the desired roster.

Also repairs main: #11762 deleted gunbc_runner_slots_per_host while #11800
added it as an import here, so every claim in this file refused to resolve.
srv1_width() now reads gunbc_runner_committed_width (Int?; the unresolved
arm yields an empty listing, and the two pure-refusal consumers gain a
length > 0 conjunct so an empty listing cannot pass vacuously). The new
TransitionPopulationUndecidable arm closes to false.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
…#11762, #11740)

runner_host_file_converge_witness_test imported gunbc_runner_slots_per_host, deleted by #11762 in favour of the RunnerWidthResolution answer; the test's srv1 boundary reads the per-axis derivation gunbc_derived_runner_slots_per_host, whose Int is srv1's committed width because srv1's reservation is the established one (a fabricated 0 for the unresolved arm is refused). v2.lens.reference_derived_residency_reading (#11740) declared no construction_justification; it is a second reader of a declaration the residency qualification could consume directly, so WallAfterGrounding { dissolves_to: SingleAuthority }.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
The authorability sentence cited gunbc_runner_slots_per_host and
transition_required_retirees, both superseded by #11762; it now names
transition_required_retirees_at and the slot: target + 1 range start the
evidence run actually mutated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
…rt home

review 69306, both correctness rather than doctrine.

ZeroRunnerWidth is nullary on this head -- the module reverted to main's
causeless variant during the #11762 merge and this fixture did not, so it
still constructed it with a cause field that no declaration carries. The
annotation eight lines from the declaration says the arm is "honestly
bare"; the witness contradicted it.

RunnerSlotIdentity was imported from gunbc.runner_slot_allocation, which
does not declare it. Its one home is gunbc.build_cache_instance, which is
what every other consumer cites -- an import is reachability, not
re-export. Imported from the declaring module.

I checked the rest of the imports this branch added against their
declaring modules; the other seven are correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
…where

Third instance of the same class review 69306 named: residue from the
#11762 hand-merge. main deleted the held-width arm with the Int width API
on 2026-09-12 and dropped the name from this import block; my resolution
of that block took the union and kept it. The only other mention is the
annotation recording the deletion, which is correct as written.

Found by scanning every import this branch touched against its declaring
module rather than by waiting for the next review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
…oted

review 69341. The note said "NO WIDTH FIGURE IS QUOTED HERE ON PURPOSE"
and then quoted two -- a derived width of 12 and CPU as the binding axis
-- both falsified by the 2026-09-19 cores_per_slot ruling that this
file's own authority row records and that the allocation witness executes
(committed == host_memory_admitted_width on srv1/srv3/srv4). Correcting a
transcribed number by transcribing two more is the failure DESIGN 6 names,
performed inside the sentence warning against it.

The figures are gone. The pointer is repointed too: the note sent readers
to gunbc_runner_slots_per_host, which #11762 deleted, so the instruction
named a symbol that no longer resolves. It now names
gunbc_runner_committed_width, which refuses rather than answering for a
host whose inputs are unestablished.

Swept the rest of this branch's prose for the same stale pointer; every
other mention is past-tense history and correct as written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
…#11762, #11740)

runner_host_file_converge_witness_test imported gunbc_runner_slots_per_host, deleted by #11762 in favour of the RunnerWidthResolution answer; the test's srv1 boundary reads the per-axis derivation gunbc_derived_runner_slots_per_host, whose Int is srv1's committed width because srv1's reservation is the established one (a fabricated 0 for the unresolved arm is refused). v2.lens.reference_derived_residency_reading (#11740) declared no construction_justification; it is a second reader of a declaration the residency qualification could consume directly, so WallAfterGrounding { dissolves_to: SingleAuthority }.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 43b4697)
@briansrls
briansrls deleted the session/snappy-crab-140 branch September 21, 2026 02:47
gunbai-bot Bot pushed a commit that referenced this pull request Sep 21, 2026
…d-red, with their cause and trigger, so the sealed floor adjudicates instead of greening over them

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