Skip to content

One width was carrying three claims: candidate, actuatable, observed - #7930

Closed
gunbai-bot[bot] wants to merge 1 commit into
mainfrom
capacity-width-honesty
Closed

gunbai-bot[bot] wants to merge 1 commit into
mainfrom
capacity-width-honesty

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

Operator ruling on the #7891 merge (47c602caa74a20719b7d9ade9da3e721796607b8): "22 committed" is still too strong.

One number was answering three different questions, and the strongest was the one being published:

srv1 srv2 srv3 srv4 fleet
memory candidate 5 5 7 7 24
candidate_width_under_current_topology 5 5 7 5 22
actuatable_desired_width 5 5 5 5 20
observed_live_width 5 5 5 5 20

Why the gap is not paperwork

Nothing in this repository creates or activates a slot. host_converge_realize implements PerSlotMemoryCap and returns KnobUnimplemented for RunnerWidth; activation additionally requires a grounded managed-cache receipt and a bound compile-pool receipt, and host_compile_pool reports not-ready for every host under CompilePoolInRunnerSlots. The seventh srv3 slot is unreachable through the modeled path, not merely unprovisioned — and a width claiming it is a claim about a machine no one has changed.

WidthActuationStanding gates advancement. It is ActuationUngrounded on every host today, and it is written per-host rather than as a fleet constant so srv3 can advance alone when its receipt exists, without the arm becoming a place to assert readiness.

And the candidate is not merely waiting on labour

srv3 runner pool   7 × 14 GiB    =  98.0 GiB
derived pool      62 × 1792 MiB  = 108.5 GiB
                                   -------
                                   206.5 GiB   against ~125 GiB, before overhead and headroom

The compile pool is a sibling RAM claim sized from jobserver tokens; today's zero is only a projection of a topology that refuses activation. That sum is very likely double-counting — the per-slot measurement was taken with compiles running inside the slots, so their residency is already inside the 14 GiB — and that is exactly why it matters: the two claims have never been decomposed, so the candidate 7 is unproven in both directions rather than merely unscheduled. This arithmetic now lives in the module instead of only in review.

A live breakage, fixed

Deploy and converge consumers read actuatable_desired_width, because each answers "what should exist on this host right now."

gunbc.host_converge's count cross-check compares declared width to live unit enumeration and refuses the host on mismatch. With srv3 declared at 7 and five units on the box, gunbc converge refused srv3 outright — correct behaviour serving a false premise, shipped by this branch's predecessor. Reading the actuatable width restores agreement without anyone editing a number toward the machine, which gunbc_runner_slot_allocation_authority_disposition forbids.

observed_live_width is deliberately not derived from the declared width. Deriving it would make the observation agree with the model by construction and destroy the only signal that says whether an intent was ever applied.

A false sentence, corrected

The disk-axis note said srv1–srv3 "are not widened by this change." srv3 is the only host being widened. Its unmeasured disk axis is therefore not inherited debt — it is an axis dropped out of the minimum for exactly the host whose width is moving, which is why srv3 owes a disk observation before anything is actuated on it.

The policy this encodes

An operator-declared evidence grade may grant candidate capacity and may not grant live capacity. srv3 and srv4 are session-free by declaration rather than observation, which is good enough to keep 7 as a candidate and not good enough to actuate on.

Not in scope

Deriving the widths from a joint (runner_slots, build_tokens) solve, and producing srv3's activation receipt, are the separately dispatched capacity lane. This change only stops the model overstating what it can reach.

Verification

63 witnesses across runner_slot_allocation, host_allocation_conservation, runner_placement, ci_runner_target, fleet_show_effective_read, srv3_runner_memory_converge and host_converge_slice1 — green by execution, zero failures. New discriminating rows: actuatable_width_never_exceeds_candidate, actuatable_width_is_the_observed_width_while_activation_is_ungrounded, and srv3_is_the_host_whose_candidate_exceeds_what_can_be_actuated — srv3 is the only host where the two differ, so re-collapsing them, or advancing actuatable without a receipt, reds there and nowhere else.

🤖 Generated with Claude Code

Operator ruling on the #7891 merge: "22 committed" is still too strong.
One number answered what the constraints ADMIT, what the fleet can be MADE
to run, and what is ACTUALLY RUNNING, and the strongest of the three was
the one being published.

  candidate_width_under_current_topology   5/5/7/5
  actuatable_desired_width                 5/5/5/5
  observed_live_width                      5/5/5/5

Nothing in this repository creates or activates a slot: host_converge_realize
returns KnobUnimplemented for RunnerWidth, and activation additionally needs a
managed-cache receipt and a bound compile-pool receipt that host_compile_pool
reports not-ready on every host. So the seventh srv3 slot is unreachable
through the modeled path, not merely unprovisioned. WidthActuationStanding
gates advancement, and only a grounded receipt moves the actuatable number.

Nor is the candidate merely waiting on labour. Seven slots at 14GiB is 98GiB
and the cited 62-token derivation is a further 108.5GiB against ~125GiB
usable. That sum is very likely double-counting, because the per-slot figure
was measured with compiles running inside the slots — which is the point: the
two claims have never been decomposed, so seven is unproven in both
directions. The arithmetic is in the module rather than only in review.

Deploy and converge now read the actuatable width, which fixes a live
breakage this branch's predecessor shipped: with srv3 declared at 7 and five
units on the box, host_converge's count cross-check refused srv3 outright —
correct behaviour serving a false premise. Declared and live agree again
without anyone editing a number toward the machine, which the authority
disposition forbids.

Also corrects a false sentence I wrote in the disk-axis note. It said srv1-3
"are not widened by this change"; srv3 is the ONLY host being widened, so its
unmeasured disk axis is not inherited debt — it is an axis dropped out of the
minimum for exactly the host whose width is moving.

63 witnesses across the five width-consumer files green by execution.
@gunbai-bot
gunbai-bot Bot marked this pull request as draft August 6, 2026 20:42
@gunbai-bot

gunbai-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Converting to draft — held for recut on operator ruling.

The vocabulary stands; the control law is wrong, and it is circular. This PR says, in effect:

until an activation receipt exists:
    actuatable_desired_width = observed_live_width

which closes a loop with no entry:

desired stays 5 until a receipt proves 7
the receipt cannot exist until something actuates 7
nothing plans actuation because desired stays 5

I built WidthActuationStanding to prevent overstating reachable capacity, and it does — by making the requested width unreachable by construction. That is not conservatism, it is a controller that cannot converge.

The correct shape keeps intent and admission as separate facts rather than folding one into the other:

requested_width = 7
admission       = admitted | refused { cause }
observed_width  = 5
plan            = add 06, add 07

Desired state must not reconcile toward the machine before convergence is attempted. Routing deploy and converge consumers to the smaller width — which this PR does, and which I described as fixing a live breakage — is the same error: srv3's converge refusal was real, but the fix is count equality as a postcondition, not lowering the declared width until it matches what is already there. Five observed against seven requested must yield exactly two additions, not a refusal because the counts differ.

What survives the recut: the three-way distinction itself, observed_live_width staying independent so declared and live can disagree, the disk-axis note correction, and the discriminating rows.

Superseded by the top-level gunbc.apply lane, which owns requested width as an operator argument rather than an authored table.

— sent from deep-badger-594

@gunbai-bot

gunbai-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Closing as superseded by #7939, per the wind-down consolidation directive.

This PR split runner width into candidate / actuatable / observed axes. The operator rejected its control law as circular: desired width stays at 5 until a receipt proves 7, and that receipt cannot exist until something actuates 7. The PR was converted to draft at that point and has not been revived.

#7939 carries the corrected width split (memory-admitted width, with disk gated at apply rather than at allocation), so the useful half of this work has a live successor.

State at close: draft, build=cancelled, ci=failure. Its 2 approvals sit on content the operator has already ruled out, which is another case of approvals not establishing that a change should land.

Work continues in #7939.

— sent from deep-badger-594

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