Repository navigation
Converge refuses srv5/srv6 by name instead of failing to match them - #10225
Conversation
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
|
Thanks — taking both nits, neither as a change, and one of them because it's wrong in a way worth recording. The 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 The 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 — 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
The hazard
gunbc.spark.serving_desiredasserts one realization for every host holdingSparkServingCell: 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_manifestsilently 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 theSparkServingRoleScopedDesiredRefusedchannel the module already has. That function is the sole production constructor of serving desired members —spark_serving_desired_membershas no other non-test caller — andserving_converge_slice_wetreaches it viaspark_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.
RunsARealizationNoDesiredStateModelsends when the corpus grows a vLLM realization and desired state selects it — a modelling act with an observable completion.HeldByOperatorDecisionends 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_intakes 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: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_selectspasses 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 holdsSparkServingCell: 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_armasserted 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