Repository navigation
DCH-0r-a: give restart its own typed effect identity — spark.serving_realization fuses systemctl enable+start+restart into EnableSystemUnit (unconditional restart, no matchable identity), while the user-unit path the Sparks run has enable and start separated and NO restart effect at all - #9984
Conversation
…er paths EnableSystemUnit actuated three commands under one identity -- enable, start and restart. Nothing could match on a restart, the restart was unconditional, and the ranking fold counted one effect while the host paid for three. The system path is now the shape the user path already had: EnableSystemUnit enables, StartSystemUnit starts. Restart is neither -- it is what a service that is already up needs when the definition underneath it moved -- so it gets its own sibling vocabulary, ServingReactivationEffect, joined to install and retirement at SparkServingPlannedEffect. The remedy REFUSES rather than defaults. Selecting a restart needs the definition the RUNNING invocation was started from, which no probe answers today: is-active reports active for a stale process and a converged one alike, and the registration member's digest is the file on disk. So the selector returns a typed, located ReactivationUndecidable naming the missing observation, and the carrier states that no arm has a reachable green path until that readback lands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n
… verdict The observation carries four states because a probe can distinguish four and folding any pair loses a remedy. An unstamped running process is a POSITIVE reading -- it was started before the identity existed -- so it selects the restart rather than the refusal the unobserved arm gets; spending it as ignorance would leave the stale process this vocabulary exists to catch permanently undecidable, and on the hosts this lane targets that is the state they are most likely in. The premise that arm rests on is named on the carrier: it holds only while the desired definition stamps unconditionally. A unit that is not running gets ReactivationNotApplicable rather than ReactivationNotRequired. The second means this host is converged on the definition it is serving; reporting a stopped unit that way puts two questions' answers under one symbol and a consumer concludes a stopped host is fine. The activity address owns the start. The identity is the typed spec, not the rendered content: an identity embedded in a unit whose bytes it is a hash of has no fixed point. The domain obligation is on the carrier, because a desired identity in one domain against an observed identity in another is a permanent restart loop on a converged host -- the same failure the fused unconditional restart produced. Files one recurring failure mode, liveness_probe_read_as_currency: a probe that answers whether a thing is RUNNING consumed as whether it is running the CURRENT definition, with its rung, ceiling and next-rung trigger. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n
The arm claimed an unstamped process PREDATES the desired definition. That is stronger than the reading supports: a foreign process, or one started by hand, carries no stamp either and may have started LATER. Nothing here measures when anything started. What the reading establishes is that the process carries no definition identity, so it cannot be established as having been started from the current desired stamped definition. The remedy is unchanged -- unestablished is precisely what a restart is the remedy for -- and the wiring is untouched, so no verdict moves. The correction covers the executed diagnostic cause string, not only the annotations: a cause has no oracle, and one asserting an unmeasured chronology is program data a consumer could reasonably act on. Names the four preconditions the narrowed claim rests on, each owned elsewhere: the environment read completed; absence is distinguished from permission, parse, truncation and observation failure; the desired specification stamps unconditionally; and the stamp identity covers every process-start input whose change requires reactivation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n
# Conflicts: # DESIGN.md # dag/gunbc/recurring_failure_mode.dag # docs/design-ledgers.md
|
On review 58427 — the finding is real, I am not disputing it, and I am doing something slightly different from what it asks. Reasoning here so it can be checked rather than taken. The row is a prose carrier and §4c does say so. Two facts about the instance. That row landed in #9238, well before this lane. The simple form of the same shape matches thousands of rows on Why not the remedy as written. Deleting the row and retyping its two witnesses is correct about the class and lands a one-of-thousands cleanup inside a restart-decomposition PR, in a module that is not this change's subject. Reverting the token instead would leave the sentence claiming What I am doing instead, which is smaller than both and fixes a defect neither names. That sentence hand-copies a list the model derives. That reduces the prose rather than expanding it and removes the maintenance trap that produced the diff you flagged. It does not make the row stop being a prose carrier; that is the corpus-wide class and it is not this PR's to close. Both existing witnesses still pass on the surviving tokens — they match Landing it on the next push rather than this minute: a — sent from stern-otter-633 |
review 58427 found this row expanding commentary held as a String, and the class is real: the row is a prose carrier and there are thousands like it on main. The fix it asked for -- delete the row, move to //, retype its two witnesses -- is right about the class and lands a one-of-thousands cleanup in a restart PR, in a module that is not its subject. The defect underneath is neither. The sentence hand-enumerated the effects SystemUnitRealization needs escalation for, which realization_escalated_commands already derives, so it was a second authority for one fact -- and the proof that it drifts is that splitting the fused effect made it wrong and needed a human to patch prose. So the enumeration is deleted in favour of naming the deriving function, with no count and no list, and adding an effect now changes the answer without changing this sentence. This reduces the prose rather than expanding it and removes the maintenance trap that produced the flagged diff. It does not make the row stop being a prose carrier; that class is corpus-wide and not this PR's to close. Both witnesses over the note still pass by execution on the surviving tokens. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n
|
This PR is now an explicit predecessor of #10001, the operator-requested immediate safe fleet-converge E2E. The separation of restart into Please keep this PR’s vocabulary reusable, but do not let |
# Conflicts: # DESIGN.md # docs/design-ledgers.md
|
On review 58458 — the finding is correct, it is about a claim I wrote, and I am taking the second of the two remedies it offers. The claim is inflated and I am fixing it. The ledger row says the class "is now MITIGATABLE" because Why the first remedy is not available, and this is a decision rather than an omission. "Wire the remedy into serving convergence" would create a path that can emit a restart. There is no admission fence, no drain, and no lease population yet — those are DCH-0r-c — so a restart emitted today would be exactly the destructive convergence this gate exists to prevent, and it would be selected off an observation that no probe produces ( So the unwired selector is the deliberate landing state, and the correct repair is the one the review names second: keep the ledger rung honest. What the row will say instead. That the class remains at silent wrongness on the convergence path; that what this change establishes is the vocabulary plus a selector whose refusal is executed only against fixture inputs, which is a boundary and not a rung; and that the next rung requires TWO things, not one — the running-definition observation, AND a guarded producer that routes Timing. A For the avoidance of doubt about what is and is not established here: the restart decomposition, the four observed states and the refusing selector are all green by execution. The thing that is NOT established, and that the row wrongly implied, is that any of it protects a live convergence today. — sent from stern-otter-633 |
review 58458 found rung inflation in this PR's own ledger row, and it is right. The row said the class "is now MITIGATABLE" because spark_serving_reactivation_remedy refuses. That function has no production caller: nothing routes a convergence through it, so on the path where the harm occurs nothing refuses and nothing changed. A rung is the minimum across a class's in-scope paths, and a selector nobody calls moves none of them. The row now says the class remains at silent wrongness on the convergence path, and that what this change establishes is the vocabulary plus a selector whose refusal executes against FIXTURE inputs -- a declared boundary, not a rung, named so it cannot be cited as coverage. The review's other remedy -- wire the remedy into convergence -- is DECLINED for a safety reason rather than deferred for convenience, and the row says so: a production caller is a path that can emit a restart, and until an admission fence, a drain protocol and a lease population exist, a restart on a live host is the destructive convergence this gate exists to prevent. Wiring it early trades an honest gap for an unguarded one. The trigger is now a CONJUNCTION that cannot be half-satisfied: the running-invocation observation AND a guarded producer routing ReactivationUndecidable into the convergence verdict. Naming only the first would let a probe landing retire the trigger while the capability stayed dead, which is the grain mismatch 4b(3) warns about and which the original row had. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n
|
On review 58481. I am declining this one on evidence rather than on judgment, because its central factual claim — that removing the restart expanded silent wrongness to the system-unit path — does not hold against the tree. Three measurements, all re-derivable. 1. The fused restart never executed anywhere. 2. Every system-unit effect refuses, and did before this PR. In 3. Nothing selects So the direction of the change on that path is the opposite of the finding: the restart's removal narrowed privilege, because What is true, and what the row already says. A user-unit host whose process is stale does still report converged. That is the pre-existing class, it is why On the proposed remedy. "Route the unavailable running-definition observation into a typed convergence refusal" is conjunct (ii) of that row's own trigger, and it is DCH-0r-c's work, deliberately sequenced after an admission fence, a drain protocol and a lease population. It also requires an observation that no probe mints yet — I have not changed the code in response to this review. If the measurements above are wrong I would rather be shown which one — each is a single grep over the corpus. — sent from stern-otter-633 |
|
Correcting one of my own three measurements from the comment above, before anyone else has to. The decline stands; one sentence supporting it was false, and the true version is stronger. What I got wrong. I wrote that The accurate statement, which survives the grep that kills the old one. Measurements 2 and 3 are unchanged and I have re-checked both: every system-unit effect returns One more precision, on the same standard. I said removing the restart "narrowed privilege". That is true of the modeled grant: None of this changes the conclusion — there is no system-unit convergence path, so removing the restart could not have expanded silent wrongness onto one — but a correct decline resting on an overstated measurement is worth less than the measurement it overstated. — sent from stern-otter-633 |
… as a confession Twice now a reviewer has read this row's honest statement -- that the operational path still reports convergence without the refusal -- as evidence that this change introduced the gap. It is the opposite: the row is the change declaring a defect it did not cause and does not close. That is not a reason to soften the claim. It is a missing piece of provenance, so the row now carries it: the convergence path acquired this class when activity was modeled on `systemctl --user is-active` in gunbc.spark.serving_observed_members, whose four states name whether a unit runs and never which definition it runs. An honest declaration of a pre-existing gap is indistinguishable, to a reader scanning a diff for regressions, from a confession of a new one. Naming what introduced the state is what makes the two distinguishable without weakening either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n
…on is not a member
PlannedEffectExecution carries `effect` and the steps as INDEPENDENT fields and apply
consumes the steps without checking they realize the effect beside them. So an arm of the
sum that carries no producer is not inert: it is admitted as the LABEL beside commands
whose authority is independent of that label. Zero producers established only that no
current site constructs that value, never that the value cannot reach apply.
SparkServingPlannedEffect is therefore renamed SparkServingExecutablePlannedEffect and
narrowed to installs and retirements. ServingReactivationEffect stays as standalone
vocabulary with its own wire renderer; what leaves is only its membership in the sum apply
accepts. DCH-0r-c admits it properly, and not by re-widening this sum:
PlannedBoundReactivationExecution { plan, steps } derives the steps from the bound plan.
The exclusion is enforced by execution, not by annotation. The new witness compiles a
fixture that puts a reactivation in a field declared as the real corpus sum and requires a
refusal, with a positive control differing on exactly one axis. The witness records what it
did NOT measure and why: a fixture importing gunbc.fleet_converge_plan does not compile
under this instrument at all -- measured, an import-and-return-1 fixture over that module is
refused on a blocking `unresolved type 'SparkServingObservationProvenance'` in
gunbc.spark.serving_execution_schedule, which this change does not touch -- so naming
PlannedEffectExecution directly would have made the red vacuous.
Green by execution: 2 not-executable witnesses, 4 wire witnesses, 7 reactivation-remedy
witnesses, 5 retirement witnesses; --required-regen first_generation_equal=true.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n
…ng type The witness recorded its residue against a diagnostic it had read wrong. `SparkServingObservationProvenance` is not missing -- it is declared in gunbc.spark.serving_observation_transaction. gunbc.spark.serving_execution_schedule uses it at two field positions and names it in no import, so it resolves by global uniqueness in the full corpus and by nothing at all under a narrower source closure. That framing matters twice over: the old wording would send the next reader hunting a corpus bug that does not exist, and the real one names a one-line repair as the trigger. So the witness's residue is now closed by a declared import in that module, sufficient for a fixture importing gunbc.fleet_converge_plan to compile -- at which point the type-level step from "not a member of the sum" to "cannot label an execution" becomes an executed red. Annotation only; no type, selector, or witness behaviour changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n
|
Adjudicating one pointer in review 58538 (APPROVE, non-blocking) rather than declining it silently. The review cites The substance behind it is right and is in the PR: the compile-check that reds if the sum re-widens is Worth recording alongside it, because it is the more useful finding: that witness does NOT name — sent from stern-otter-633 |
|
CI red on this head is inherited from The failing job is Established by content rather than by run outcome:
A PR run tests the head merged with main, which is the only reason a test absent from this branch can fail on it. The subject is service emission and stdout binding; this PR splits a systemd effect and narrows a plan sum, and touches no Rust at all. Everything this branch is answerable for is green on this head: Per DESIGN's "Building & checks", — sent from stern-otter-633 |
What was wrong
gunbc.spark.serving_realizationrealizedEnableSystemUnitas THREE commands under one effect identity:systemctl enable,systemctl start,systemctl restart.And the asymmetry: the USER-unit path the Sparks actually run already had
EnableUserUnitandStartUserUnitseparated, and carried no restart effect at all.What this changes
The system path becomes the shape the user path already had.
EnableSystemUnitenables. A newStartSystemUnitstarts, and joinsSystemUnitRealization's install effect list. The sudoers grant narrows with it:systemctl restartis no longer in any realization's escalated commands, because no plan can emit it.Restart gets its own vocabulary, and that vocabulary is NOT a member of the sum
applyaccepts. Every constructor ofServingInstallEffectis the price of standing a service up — the propertyrealization_install_effectsandrealization_privileged_effect_countboth presume. A restart is what a service that is ALREADY UP needs when the definition underneath it moved. SoServingReactivationEffectis a third sibling with its own wire renderer, standing beside install and retirement rather than joining them.It deliberately does not join
SparkServingExecutablePlannedEffect(renamed fromSparkServingPlannedEffect, and narrowed toPlannedInstallEffect | PlannedRetirementEffect). The reason is thatPlannedEffectExecutioncarrieseffectand the steps as INDEPENDENT fields, and apply consumes the steps without checking they realize the effect beside them — so membership in that sum is not a rendering convenience. A member is admitted as the LABEL beside commands whose authority is independent of that label, and a reactivation arm would have madeeffect: <a restart>, first_step: <any step at all>a writable, apply-accepted value. Zero producers would have established only that no current site constructs it, never that it cannot reach apply.DCH-0r-c owns the first executable join, and not by re-widening this sum:
PlannedBoundReactivationExecution { plan, steps }, whose steps are DERIVED from the bound plan rather than authored independently beside it — the same defect closed at its root for reactivation instead of inherited from installs.The remedy refuses rather than defaults.
spark_serving_reactivation_remedyselects a restart from four observed states of the running process, and two of them refuse:ReactivationNotApplicableis a separate arm fromReactivationNotRequireddeliberately: the second means this host is converged on the definition it is serving, and reporting a stopped unit that way lets a consumer conclude a stopped host is fine.The honest part, and it is on the carrier
No arm of the reactivation vocabulary has a reachable green path today.
ReactivationRequiredneeds an observation of the definition the RUNNING invocation was started from, and nothing in this repository mints one against a real host:is-activereports active for a stale process and a converged one alike, and the registration member's digest is the file on disk. A fixture can construct the observation — which is why the selector is checked by execution — but no Spark convergence has ever emitted a restart through this vocabulary.That is written in the type's own annotation, not only here, so "we model restart" cannot be cited as coverage for a capability that has never once executed. The next-rung trigger names the CAPABILITY: an observation joining a unit's running invocation to the definition it was started from, sufficient to mint
RunningDefinitionObservedfor a live Spark. Nothing smaller retires it. That probe is DCH-0r-b's, in flight; the identity domain is the typedSparkServingUserUnitSpecrather than the rendered content, because an identity embedded in bytes it is a hash of has no fixed point, and the carrier states the obligation that both sides take it in one domain.The derivation that looks available and is not, recorded so nobody re-proposes it: selecting a restart when the REGISTRATION member changes. That reads "the file on disk is now what we want" as "the process is stale" — the observed importing the desired, the error
serving_observed_membersrecords against its own earlier revision — and it would restart a converged host whose file was merely rewritten.One recurring failure mode filed
liveness_probe_read_as_currency— a probe answering whether a thing is RUNNING consumed as whether it is running the CURRENT definition. Found at silent wrongness (a stale server and a converged one produced the same verdict with no diagnostic), now mitigatable (typed located refusal), ceiling structurally guaranteed since the question is decidable, trigger as above. It notes that this is a CANDIDATE cause for the observed-versus-desired drift on both Sparks and that candidate is the correct word until someone reads a host back.Evidence, by execution
spark_serving_reactivation_remedy_witness_test— 7/7 PASS, covering all four observed states, both realizations with a restart, the container realization without one, and the discriminating pair where unstamped restarts while unobserved refuses.spark_serving_reactivation_not_executable_witness_test— 2/2 PASS. The exclusion is enforced by execution, not by annotation: a fixture placing a reactivation in a field declared as the real corpus sum must be REFUSED, paired with a positive control differing on exactly one axis (the effect placed there) that must COMPILE. Both arestanding=planned-and-passedin the floor on this head, so the wall executes in CI and is not local-only.spark_serving_effect_wire_witness_test— 4/4 PASS, including that a restart never renders as a start.serving_privilege_derivation_witness_test— 12/12 PASS, unchanged. The split moves the grant without breaking the derivation.spark_cell_role_retirement_witness_test— 5/5 PASS on the arms the reshape touches.PlannedEffectExecutionitself, because a fixture importinggunbc.fleet_converge_plandoes not compile under this instrument at all. Measured, not assumed — an import-and-return-1fixture over that module is refused, while the same shape overgunbc.spark.serving_realizationcompiles. The cause is a module using a type it never imports and resolving it by global uniqueness, repaired separately in Declare the two bare type references in gunbc.spark.serving_execution_schedule, and enroll the closure pair that can see them #10022 and named in the witness as the trigger that would upgrade the type-level step to an executed red. Had the probe namedPlannedEffectExecution, its red would have passed for that diagnostic rather than for this wall.DESIGN.md,docs/design-ledgers.md).Not in scope
No admission fence, no lease population, no drain protocol, no incarnation readback. No srv5/srv6 enrolment, no
fleet_intentedit, nothing run against a live Spark. There is no production caller wiring a plan into the selector; that join is unclaimed.🤖 Generated with Claude Code
https://claude.ai/code/session_01UeXMgoLPiVCvgAQbXZab5n