Skip to content

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

Merged
gunbai-bot[bot] merged 11 commits into
mainfrom
session/stern-otter-633
Sep 2, 2026

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

What was wrong

gunbc.spark.serving_realization realized EnableSystemUnit as THREE commands under one effect identity: systemctl enable, systemctl start, systemctl restart.

  1. Restart had no matchable identity. No arm of any match ever saw a restart, so a guard, a fence or a refusal aimed at restarts specifically had no value to be written against.
  2. The restart was unconditional. Every realization of the effect issued it, so converging a unit whose definition had not changed still killed whatever it was serving.
  3. The ranking fold counted one effect while the host paid for three, so the privilege count that decides which realization wins was reading a number the argv did not support.

And the asymmetry: the USER-unit path the Sparks actually run already had EnableUserUnit and StartUserUnit separated, and carried no restart effect at all.

What this changes

The system path becomes the shape the user path already had. EnableSystemUnit enables. A new StartSystemUnit starts, and joins SystemUnitRealization's install effect list. The sudoers grant narrows with it: systemctl restart is 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 apply accepts. Every constructor of ServingInstallEffect is the price of standing a service up — the property realization_install_effects and realization_privileged_effect_count both presume. A restart is what a service that is ALREADY UP needs when the definition underneath it moved. So ServingReactivationEffect is a third sibling with its own wire renderer, standing beside install and retirement rather than joining them.

It deliberately does not join SparkServingExecutablePlannedEffect (renamed from SparkServingPlannedEffect, and narrowed to PlannedInstallEffect | PlannedRetirementEffect). The reason is that PlannedEffectExecution carries effect and 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 made effect: <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_remedy selects a restart from four observed states of the running process, and two of them refuse:

running definition verdict
observed, differs from desired restart for that realization
observed, equals desired not required
unstamped (running, carries no identity) restart — positive earlier-definition evidence
not running not applicable — the activity address owns the start
unobserved undecidable, cause names the missing observation

ReactivationNotApplicable is a separate arm from ReactivationNotRequired deliberately: 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. ReactivationRequired needs an observation of the definition the RUNNING invocation was started from, and nothing in this repository mints one against a real host: is-active reports 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 RunningDefinitionObserved for a live Spark. Nothing smaller retires it. That probe is DCH-0r-b's, in flight; the identity domain is the typed SparkServingUserUnitSpec rather 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_members records 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 are standing=planned-and-passed in 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.
  • What that probe does NOT measure, stated so the receipt cannot claim more than it ran: it does not name PlannedEffectExecution itself, because a fixture importing gunbc.fleet_converge_plan does not compile under this instrument at all. Measured, not assumed — an import-and-return-1 fixture over that module is refused, while the same shape over gunbc.spark.serving_realization compiles. 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 named PlannedEffectExecution, its red would have passed for that diagnostic rather than for this wall.
  • Generated artifacts regenerated in the same commit (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_intent edit, 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

gunbc-ci-auto-heal and others added 2 commits September 2, 2026 01:30
…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
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 2, 2026 01:52
gunbc-ci-auto-heal and others added 3 commits September 2, 2026 02:58
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
@gunbai-bot

gunbai-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

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. data spark_credential_workflow_note: String is commentary held as program data, its only consumers string-match it, and that is the shape §4c names. Agreed on the class.

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 main today. And what this PR did to it is add one token: splitting the fused EnableSystemUnit made the sentence's list of escalating effects wrong, so StartSystemUnit, went in to keep it accurate.

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 SystemUnitRealization needs four effects where the model says five — cosmetic compliance bought with a real inaccuracy.

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. realization_escalated_commands already answers which effects require escalation for a realization, so the sentence is a second authority for one fact — and the proof that it is second rather than harmless is that splitting an effect made it wrong and required a human to patch prose. §3 says cite the symbol rather than duplicate it. So the enumeration is deleted in favour of naming the deriving function, with no count and no list, so that adding an effect changes the answer without changing this sentence.

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 critical path, EnableLingerForOwnAccount, SystemUnitRealization, never plaintext, fetch_secret_ref_credential and argv, none of which are in the deleted enumeration.

Landing it on the next push rather than this minute: a witnesses run is in flight on this head, and cancelling a full run to land a two-line prose repair costs more than it buys. It goes up with whatever that run reports.

— 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

Copy link
Copy Markdown
Contributor

This PR is now an explicit predecessor of #10001, the operator-requested immediate safe fleet-converge E2E.

The separation of restart into ServingReactivationEffect is required, but planning a restart must not itself authorize execution. #10001 pins the execute fence: until a typed quiescence/drain receipt exists, an active stale, unstamped, incomparable, or unobservable running definition refuses before the first effect. The first wet terminal may use only a stopped install/start path or a truly current noop path.

Please keep this PR’s vocabulary reusable, but do not let ReactivationRequired be cited as a safe live path on its own. The production consumer and wet proof belong to #10001 after this and #9986 compose.

# Conflicts:
#	DESIGN.md
#	docs/design-ledgers.md
@gunbai-bot

gunbai-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

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 spark_serving_reactivation_remedy refuses. spark_serving_reactivation_remedy has no production caller. Nothing routes a convergence through it, so on the path where the harm occurs — a host converging while its process serves a definition it was not started from — nothing refuses and nothing has changed. §4b says a class's rung is the minimum across its in-scope paths and that a type name or diagnostic variant establishes nothing; measured that way this class is still at silent wrongness on the convergence path, and the row said otherwise.

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 (RunningDefinitionObserved is unminted on every real host; DCH-0r-b is building that readback). Wiring it now would trade an honest gap for an unguarded one.

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 ReactivationUndecidable into the convergence verdict. Naming only the observation would leave the trigger satisfiable while the capability stayed dead.

Timing. A witnesses run is in flight on this head; pushing now cancels it and spends a full run to land a prose correction. The fix goes up with the next push, and this comment stands as the acknowledgement that the claim as written is wrong.

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
@gunbai-bot

gunbai-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

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. effect_actuation, the function that carried systemctl enable + start + restart as one arm, has zero non-test callers. The only non-test mentions of it in the corpus are two comments — in gunbc.spark.serving_converge_apply and gunbc.spark.serving_converge_realize — that exist precisely to say actuation does not route through effect_actuation shell strings. So there was no operational path on which that restart ran, and removing it changed the behaviour of nothing that executes.

2. Every system-unit effect refuses, and did before this PR. In realize_spark_serving_install_effect_command, WriteSystemUnitFile, ReloadSystemManager, EnableSystemUnit, InstallRuntimePackage, GrantContainerRuntimeAccess and RunContainerAsOwnUser all return SparkServingInstallRemoteCommandsRefused. The EnableSystemUnit refusal is pre-existing, not added here; this PR added the StartSystemUnit arm in the same shape. A path that refuses before actuation cannot silently accept a stale process.

3. Nothing selects SystemUnitRealization. Outside serving_realization and tests, the only occurrence in the corpus is one prose row in credential_workflow. There is no system-unit convergence to regress.

So the direction of the change on that path is the opposite of the finding: the restart's removal narrowed privilege, because systemctl restart is no longer in any realization's escalated command set, and the grant now names only what a plan can emit.

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 liveness_probe_read_as_currency was filed, and the row states it without softening: "IT IS STILL THERE ON THE CONVERGENCE PATH … spark_serving_reactivation_remedy refuses with a typed located cause, but it has NO PRODUCTION CALLER, so nothing routes a convergence through it and the operational path still reports convergence without the refusal." The review reads that row as confirming a regression this PR introduced. It is the opposite: it is this PR declaring a defect that predates it and that this PR does not close.

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 — RunningDefinitionObserved is unminted on every real host until #9986 lands. Building the refusal route now would mean a convergence path that stops hosts on a verdict derived from an observation that does not exist, which is a worse fail-closed story than the honest gap, not a better one.

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

@gunbai-bot

gunbai-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

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 effect_actuation has zero non-test callers. It has one: realization_escalated_commands, in the same module, calls it. My grep excluded serving_realization.dag in order to look for external consumers, and then I reported the result as the whole population — so my own filter emptied the set I was describing. That is a measurement error of exactly the kind this PR's ledger row is about, and it is mine.

The accurate statement, which survives the grep that kills the old one. effect_actuation has exactly one caller, realization_escalated_commands, and that caller computes a sudoers grant rather than an actuation. Its output flows to realization_sudoers_grant_lines, whose only consumers are witness tests. So the chain terminates without an executing consumer, and the fused systemctl restart could not run because that function is not on an actuation path at all — not because nothing called it. The two comments I cited, in serving_converge_apply and serving_converge_realize, say the same thing from the other end: actuation deliberately routes through typed RemoteOperation values, never through effect_actuation shell strings.

Measurements 2 and 3 are unchanged and I have re-checked both: every system-unit effect returns SparkServingInstallRemoteCommandsRefused with EnableSystemUnit's refusal predating this PR, and SystemUnitRealization occurs exactly once outside serving_realization and tests, in a prose row.

One more precision, on the same standard. I said removing the restart "narrowed privilege". That is true of the modeled grant: systemctl restart is no longer in any realization's escalated command set. No sudoers file is emitted from that model today, so it is a narrowing as modeled, not an observed reduction on a live host. I should have said so the first time.

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
gunbc-ci-auto-heal and others added 2 commits September 2, 2026 07:32
…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
@gunbai-bot

gunbai-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Adjudicating one pointer in review 58538 (APPROVE, non-blocking) rather than declining it silently.

The review cites spark_serving_reactivation_not_executable_witness_test.dag:619. That file is 87 lines; there is no line 619, so the pointer cannot be resolved to a symbol and I could not act on it even to agree. This is DESIGN §3's positional-citation class — a file:line is a second naming scheme for something the containment tree already names, and it decays in a way a symbol name does not.

The substance behind it is right and is in the PR: the compile-check that reds if the sum re-widens is w_reactivation_is_not_a_member_of_the_executable_effect_domain, paired with w_install_is_still_a_member_of_the_executable_effect_domain as its positive control. Naming those two symbols is the citation; the line number was never needed.

Worth recording alongside it, because it is the more useful finding: that witness does NOT name PlannedEffectExecution in its fixture, and the annotation says so. My first version did, and its red passed while its control failed — the whole import closure was refusing on a blocking unresolved type 'SparkServingObservationProvenance' in gunbc.spark.serving_execution_schedule, which turns out to be a module using a type it never imports and resolving it by global uniqueness. Had I shipped that version, it would have read as a wall and passed for an unrelated diagnostic. The repair is filed separately (#10022) and named in this witness as the trigger that would upgrade its type-level step to an executed red.

— sent from stern-otter-633

@gunbai-bot

gunbai-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

CI red on this head is inherited from main, not produced by this branch, and there is no fix to push from here.

The failing job is rust-unit-tests (job 100172868605): 644 passed, 1 failed — compiler_tests::shell_service_unmodeled_output_key_refuses, panicking at compiler_tests.rs:1045 with service module must emit src/probe.rs.

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: required-witnesses-build: success (regen and generated-artifact clean, matching the local --required-regen first_generation_equal=true), with floor, witnesses and fabric-evidence still running.

Per DESIGN's "Building & checks", rust-unit-tests "runs on every push and pull request but is not yet a needs of the required aggregate" — worth confirming before it is treated as the merge blocker here.

— sent from stern-otter-633

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.

1 participant