Skip to content

Converge refuses srv5/srv6 by name instead of failing to match them - #10225

Merged
gunbai-bot[bot] merged 2 commits into
mainfrom
session/eager-pike-541-converge-withholding
Sep 3, 2026
Merged

gunbai-bot[bot] merged 2 commits into
mainfrom
session/eager-pike-541-converge-withholding

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

The hazard

gunbc.spark.serving_desired asserts one realization for every host holding SparkServingCell: an Ollama runtime serving a DeepSeek GGUF artifact. The serving-cell roster is srv5 and srv6 — and those two hosts are currently running a vLLM tensor-parallel deployment that no desired state in this corpus models.

So a wet convergence today would not fail to find its target — it would find it and assert the wrong thing: install Ollama over a working tensor-parallel pair and report success. Every part of that behaves as specified, which is what makes it destructive.

Why a refusal rather than leaving them off a roster

A non-match is not a refusal. It holds only while the manifest happens not to match, emits no diagnostic when it stops holding, and one edit to spark_serving_desired_manifest silently turns it back into an actuation. This lands the refusal as a modelled fact: subject, cause, discharge condition.

Where it sits

spark_serving_role_scoped_desired_members_in, before role dispatch, through the SparkServingRoleScopedDesiredRefused channel the module already has. That function is the sole production constructor of serving desired members — spark_serving_desired_members has no other non-test caller — and serving_converge_slice_wet reaches it via spark_serving_axis_for_observation → spark_serving_full_membership_plan. There is no second door.

The reason is a typed coproduct, not a sentence, because the arms discharge differently. RunsARealizationNoDesiredStateModels ends when the corpus grows a vLLM realization and desired state selects it — a modelling act with an observable completion. HeldByOperatorDecision ends only when the operator says so. One string would make a hold a PR can lift look identical to one it cannot.

The withholding roster is a parameter, for the same reason spark_cell_role_in takes one: an arm's evidence must not depend on which machines are withheld today, or the wall's own witnesses go red on the day the wall is deliberately removed.

Executed evidence, not a typecheck

Full witnesses lane with the wall live: verdict=FloorClean, claims_failed=0, unexpected_failures=0, planned=executed=3539. The fail-closed census found nothing else standing on the old premise.

Then the discriminating RED — wall disabled in place, same five claims re-run through claim_batch:

FAIL w_a_withheld_host_refuses_although_its_role_admits_serving
FAIL w_an_operator_hold_withholds_the_same_way
PASS w_withholding_one_host_leaves_the_others_planning
PASS w_the_production_roster_reaches_the_arm_its_withholding_selects
PASS w_the_planner_scopes_desired_state_by_role

The three passes matter as much as the two failures: the mutation broke the wall, not the module.

One honest caveat: w_the_production_roster_reaches_the_arm_its_withholding_selects passes under mutation by construction — it reads the same predicate on both sides, so it is a fixture-vs-production drift check, not the wall's evidence. The wall's evidence is the fixture claim, discriminating because its host holds SparkServingCell: every role check passes, the planner is one step from building seven members, and it is still refused.

One rewritten claim

w_the_production_roster_reaches_the_serving_arm asserted that every production serving cell reaches the seven-member arm. That stopped being true when the wall landed — not from a planner regression, but because a wall landed in front of it. Written that way it would need editing every time the withholding list moves, making it a change detector. It now states the invariant: a withheld production serving cell refuses; one that is not reaches seven. Both arms stay meaningful whether the list is full, partial or empty.

Discharge

Read the running deployment back into a vLLM realization — parallel degree, KV dtype, max model len, max num seqs, worker population, image identity — and have desired state select it. These rows come out then, and not before.

🤖 Generated with Claude Code

https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G

gunbc.spark.serving_desired asserts one realization for every host holding
SparkServingCell: an Ollama runtime serving a DeepSeek GGUF artifact. The
serving-cell roster is srv5 and srv6, and those two hosts are running a vLLM
tensor-parallel deployment that no desired state in this corpus models.

So a wet convergence today would not fail to find its target -- it would find it
and assert the wrong thing. Desired state is not silent about these hosts; it
names them and names an engine, so convergence would install Ollama over a
working tensor-parallel pair and report success. Every part of that behaves as
specified, which is exactly what makes it destructive.

A NON-MATCH IS NOT A REFUSAL. Leaving the hosts off a roster and letting
convergence find nothing to do is an accident: it holds only while the manifest
happens not to match, it emits no diagnostic when it stops holding, and one edit
to spark_serving_desired_manifest silently turns it back into an actuation. This
lands the refusal as a modelled fact instead -- subject, cause, discharge
condition.

The wall sits at spark_serving_role_scoped_desired_members_in, before role
dispatch, returning through the SparkServingRoleScopedDesiredRefused channel the
module already has. That function is the SOLE production constructor of serving
desired members (spark_serving_desired_members has no other non-test caller), and
serving_converge_slice_wet reaches it through spark_serving_axis_for_observation
-> spark_serving_full_membership_plan. There is no second door.

THE REASON IS A TYPED COPROBUCT, NOT A SENTENCE, because the two arms discharge
differently: RunsARealizationNoDesiredStateModels ends when the corpus grows a
vLLM realization AND desired state selects it, a modelling act with an observable
completion; HeldByOperatorDecision ends only when the operator says so. One
string would make a hold a pull request can lift look identical to one it cannot.

THE WITHHOLDING ROSTER IS A PARAMETER for the same reason the assignment roster
already is (gunbc.spark.cell_role spark_cell_role_in): an arm's evidence must not
depend on which machines are withheld today, or the wall's own witnesses go red
on the day the wall is deliberately removed.

EXECUTED EVIDENCE, not a typecheck. Full witnesses lane with the wall live:
verdict=FloorClean, claims_failed=0, unexpected_failures=0, planned=executed=3539
-- so the fail-closed census found nothing else standing on the old premise. Then
the discriminating RED, by disabling the wall in place and re-running the five
claims through claim_batch:

  FAIL w_a_withheld_host_refuses_although_its_role_admits_serving
  FAIL w_an_operator_hold_withholds_the_same_way
  PASS w_withholding_one_host_leaves_the_others_planning
  PASS w_the_production_roster_reaches_the_arm_its_withholding_selects
  PASS w_the_planner_scopes_desired_state_by_role

The three passes matter as much as the two failures: the mutation broke the wall
specifically, not the module. Note that the production-roster claim passes under
mutation BY CONSTRUCTION -- it reads the same predicate on both sides, so it is a
drift check between fixture and production, not the wall's evidence. The fixture
claim is the wall's evidence, and it is discriminating because its host holds
SparkServingCell: every role check passes and the planner is one step from
building seven members for it, and it is still refused.

w_the_production_roster_reaches_the_serving_arm is rewritten rather than edited.
It asserted that every production serving cell reaches the seven-member arm,
which stopped being true when the wall landed -- not from a planner regression
but because a wall landed in front of it. Written that way it would need editing
every time the withholding list moves, making it a change detector. It now says
the invariant: a withheld production serving cell refuses, one that is not
reaches seven. Both arms stay meaningful whether the list is full or empty.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G
@gunbai-bot

gunbai-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — taking both nits, neither as a change, and one of them because it's wrong in a way worth recording.

The "; also " joiner. It never appears in the single-reason case: join over a one-element list emits no separator, so one row renders as just its reason sentence. The joiner is reachable only when two rows name the same host, which is the case the annotation above it is about. So there's no awkward single-reason rendering to fix.

But the fact that it read as though there were is worth keeping, because it's the honest cost of the design choice directly above it: I deliberately did not mint an ambiguity arm for duplicate rows. The neighbouring spark_cell_role_in has one, and needs it — two rows there can name two different roles, so the roster genuinely fails to decide what the host is. Here every row says the same thing about the decision (withheld) and differs only in why, so an ambiguity arm would be a variant for a state that changes no outcome. The joiner is what that choice costs: a rendering path that only exists for a case the model doesn't otherwise distinguish.

The 0 - 1 sentinel. Agreed it reads awkwardly, and it's pre-existing — w_role_scoped_count already used it before this diff, as the "refused" encoding for a function whose green values are member counts. I kept it rather than introducing a second spelling in the same file for the same idea; changing it is a separate cleanup over every use, not a rider on a safety wall.

Worth flagging one thing the review's framing gets slightly generous, since I'd rather it be on the record than accepted: the summary credits the tests with exercising the "roster changes don't silently break the check" property via the parameterized _in form. That's true of the fixture claims. The production claim (w_the_production_roster_reaches_the_arm_its_withholding_selects) does not discriminate — it reads the same withholding predicate on both sides of the match, so it passes whether the wall fires or is inert. I verified that by disabling the wall in place: it stays green while the two fixture claims go red. It's a fixture-vs-production drift check, not evidence the wall works, and the PR body says so.

— sent from eager-pike-541

Two corrections to the withholding note, both from a first-hand HTTP readback of
the live front door that I should have done before writing the note rather than
after.

RETRACTED: "the pair serves vLLM at tensor-parallel degree 2". That was a report
repeated as an observation. What the readback actually establishes is /v1/models
answering owned_by "vllm", a vLLM /version build string, the official
DeepSeek-V4-Flash checkpoint as the model root, and a vLLM cache-config gauge
naming an fp8_ds_mla KV dtype with prefix caching on. No HTTP surface on the front
door exposes a parallel-config gauge, so parallel degree, worker population and
image identity are NOT readable from the vantage that established everything else.
The second host does not answer on the front-door port, which is consistent with a
single front door and establishes nothing further by itself.

The withholding never needed those fields -- one unmodelled engine is enough to
refuse -- so the note now says what was observed and says plainly what was not.
Asserting the rest to make the row look complete is the fabricated-plausible-output
this repository refuses everywhere else, and I wrote it into a module whose whole
subject is not acting on things nobody looked at.

STRENGTHENED: the discharge. It read "these rows come out when that lands", which
is too weak in a way that reopens the hazard. Deleting the rows the moment a vLLM
realization merely EXISTS would leave the wall gone while the planner still
constructs desired members from whatever the manifest names -- which is exactly how
converge came to be pointed at an Ollama runtime over a working deployment. The
rows now come out in the SAME motion that makes the serving axis consume an
admitted target before constructing any member, never in a separate earlier
smaller change. A window in which neither gate stands is worse than the state this
module was written to fix, because it looks resolved.

Annotation-only: §4c annotations are erased before semantic passes, so no claim
verdict can move on this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G
@gunbai-bot
gunbai-bot Bot merged commit b6f406d into main Sep 3, 2026
7 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/eager-pike-541-converge-withholding branch September 3, 2026 18:59
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