Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8c9ef78
The observer stops being handed a placeholder: three declared reads b…
Aug 26, 2026
58b2eb5
systemctl show exits zero for a unit that does not exist, so the boun…
Aug 26, 2026
cd21751
Two spellings of one defect: no composition of boolean tests can tell…
Aug 26, 2026
3f784b2
The slice enumeration was discovering unexpected units and the observ…
Aug 26, 2026
87878e5
Each namespace label had two spellings; the location is now projected…
Aug 26, 2026
1c63325
One cell root read twice in one tick, and a completed call that print…
Aug 26, 2026
4c8eb4a
A completed stat that printed nothing named a kind nobody read (#9304)
Aug 26, 2026
facfa71
Fabric-only converge scope: a request coproduct, not a scope flag bes…
Aug 26, 2026
3138805
Scope becomes a routing fact and one annotation stops describing code…
Aug 27, 2026
6bfd39c
Merge remote-tracking branch 'origin/main' into fabric/provisioning-s…
Aug 27, 2026
c257361
The law said ONLY and checked something weaker; compare the remainder…
Aug 27, 2026
1ec1c51
Production apply stops consuming the predecessor seam (#9369)
Aug 27, 2026
e8af169
Repair the call sites the port broke, and retract two claims this bra…
Aug 27, 2026
c9da251
Apply admits through one predicate, and an unreadable scope refuses i…
Aug 27, 2026
63981ae
Ownership becomes a match over the subject, so a host-scoped cell res…
Aug 27, 2026
119e00c
Delete the duplicate apply predicate, which compiled and silently res…
Aug 27, 2026
62ad8d7
Merge branch 'fabric/provisioning-scope' into fabric/host-scoped-cell…
Aug 27, 2026
c93b065
Delete a stale annotation left above the witness it no longer describes
Aug 27, 2026
fb70c53
Model mkdir's exact form, which POSIX has and this module omitted
Aug 27, 2026
a2d4f1e
Cell realization spine: closed effect sum, all-or-nothing refusal, ex…
Aug 27, 2026
362b0c9
Wire the realization into the family that reported it unavailable
Aug 27, 2026
1a53012
Re-express three cell rows against the refusal variant, now that the …
Aug 27, 2026
5ded02d
Merge remote-tracking branch 'origin/main' into fabric/cell-realization
Aug 27, 2026
6f92b1e
Delete the action-line renderer the realization wiring orphaned
Aug 27, 2026
ac0862b
Merge origin/main into fabric/cell-realization
Aug 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dag/extdeps/exec/command.dag
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ fn argv_command(program: NonEmptyStr, arguments: List<String>) -> ArgvCommand
decl_ref(module_path: "extdeps.tools.stat", decl_name: "stat_file_type_command"),
decl_ref(module_path: "extdeps.tools.stat", decl_name: "stat_inode_command"),
decl_ref(module_path: "extdeps.tools.mkdir", decl_name: "mkdir_parents_command_at"),
decl_ref(module_path: "extdeps.tools.mkdir", decl_name: "mkdir_exact_command_at"),
decl_ref(module_path: "gunbc.claim_executor_cli", decl_name: "claim_executor_command"),
decl_ref(module_path: "gunbc.repo_local_git_config", decl_name: "reconcile_install_command"),
]
Expand Down
39 changes: 39 additions & 0 deletions dag/extdeps/tools/mkdir.dag
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,42 @@ fn mkdir_parents_command(path: String) -> ArgvCommand {
fn mkdir_parents_command_at(mkdir_binary: NonEmptyStr, path: String) -> ArgvCommand {
argv_command(program: mkdir_binary, arguments: ["-p", path])
}

// THE EXACT FORM, WHICH IS POSIX'S DEFAULT AND WAS MISSING FROM THIS MODULE BY OMISSION. The cited
// authority above specifies mkdir with and without -p; only the -p half was modeled, so a caller
// needing the exact form had no door and the module mis-described its own upstream. Adding it is
// completing a faithful model of a cited dependency, not authoring structure for an imagined need.
//
// IT DIFFERS FROM THE -p FORM IN TWO WAYS, AND BOTH ARE REFUSALS RATHER THAN CONVENIENCES.
// Without -p, POSIX requires mkdir to fail when the parent directory does not exist, and to fail
// when the named directory already exists. The -p annotation above calls its own behaviour an
// ENSURE rather than a CREATE for exactly this reason.
//
// WHY A CONSUMER WOULD WANT THE REFUSING FORM. -p on a resource whose parent is a DECLARED member
// of another family is an absorbing fallback in the DESIGN section 5 sense: a missing parent is a
// failure state, and -p answers it by FABRICATING the whole chain instead of refusing. Where the
// parent is owned by a different authority -- gunbc.fabric_cell_subject classifies /opt/fabric-cells
// as Ensured, meaning required present and never created by the cell family -- -p would silently
// have that family create the very directory its ownership model says is not its to make, and
// nothing would report it. The exact form turns that into a typed non-zero exit.
//
// THE NON-IDEMPOTENCE IS NOT A DEFECT TO WORK AROUND. A converge that runs this repeatedly against
// an existing directory gets an error, which is correct: this form is reached from an ADDED hunk,
// where the member was observed absent, so a second run producing an error means the observation
// and the host disagree and the line should stop. A caller wanting run-anyway semantics wants -p
// and should say so by calling it.
fn mkdir_exact_args(path: String) -> List<String> {
[path]
}

fn mkdir_exact_argv(path: String) -> List<String> {
concat([mkdir_binary_path as String], mkdir_exact_args(path: path))
}

fn mkdir_exact_command(path: String) -> ArgvCommand {
mkdir_exact_command_at(mkdir_binary: mkdir_binary_path, path: path)
}

fn mkdir_exact_command_at(mkdir_binary: NonEmptyStr, path: String) -> ArgvCommand {
argv_command(program: mkdir_binary, arguments: mkdir_exact_args(path: path))
}
216 changes: 216 additions & 0 deletions dag/gunbc/fabric_cell_effect.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
module gunbc.fabric_cell_effect

import std.types { String, List, Bool, Int, NonEmptyStr, list_length }
import std.disposition { Disposition, SingleAuthority }
import extdeps.exec.command { ArgvCommand }
import extdeps.tools.mkdir { mkdir_exact_command }
import gunbc.build_cache_instance { RunnerSlotIdentity }
import gunbc.membership_reconcile {
MemberAction,
MemberAdded,
MemberChanged,
MemberRemoved,
MemberRemovalRefused,
MemberRosterDuplicateKeyRefused,
TeardownRefusalCause,
}
import gunbc.fabric_cell_converge {
FabricCellMember,
FabricCellResourceAddress,
FabricCellRootAddress,
FabricCellAttemptRootAddress,
FabricCellResourceBoundaryAddress,
fabric_cell_root_path,
fabric_cell_attempt_root_path,
fabric_cell_resource_address_wire,
}

data fabric_cell_effect_authority_disposition: Disposition = SingleAuthority

// WHAT THIS FAMILY CAN ACTUALLY DO TO A HOST, AS A CLOSED SUM. Before this module the fabric cell
// family emitted NO effects at all -- gunbc.fabric_cell_converge has no membership_effects and no
// EffectsOrRefusal, and both gunbc.runner_lifecycle and gunbc.fleet_converge_plan record in their
// own words that its host-effect realization is absent. So this is the family's first realization
// surface, and it is closed rather than open because an effect nobody enumerated is an effect
// nobody reviewed.
type FabricCellEffect = FabricCellDirectoryCreate { path: NonEmptyStr, command: ArgvCommand }

// THE CREATE IS THE EXACT FORM AND NOT -p, AND THAT IS THE WHOLE SAFETY CONTENT OF THIS FUNCTION.
// extdeps.tools.mkdir's own annotation calls -p an ENSURE rather than a CREATE, because a missing
// parent is not an error to it. /opt/fabric-cells is classified Ensured by
// gunbc.fabric_cell_subject -- required present, never created by this family -- so -p here would
// have the cell family silently create the exact directory its ownership model says is not its to
// make, and nothing would report it. The exact form turns a missing base directory into a non-zero
// exit instead of a fabricated parent chain.
fn fabric_cell_directory_create(path: String) -> FabricCellEffect {
FabricCellDirectoryCreate {
path: path as NonEmptyStr,
command: mkdir_exact_command(path: path),
}
}

// THE THREE DIRECTIVES THIS FAMILY NEEDS AND systemd's MODELED VOCABULARY DOES NOT CARRY.
// extdeps.systemd.unit_file SystemdSliceDirective has exactly two arms -- SliceMemoryMax and
// SliceMemoryHigh -- while fabric_cell_resource_boundary_state_digest declares five values. The
// three below have no modeled directive, so the boundary cannot be realized without either
// rendering raw text (which gunbc.build_cache_ensure argues against at length) or growing a fork
// another lane is actively shrinking.
//
// THEY ARE THREE DIFFERENT VALUE GRAINS, WHICH IS WHY THIS IS NOT ONE ROW. MemorySwapMax is a byte
// magnitude and sits inside gunbc.systemd_directive_value_grain's stated scope, whose terminal
// shape replaces the stringly `bytes` field with a modeled magnitude. TasksMax is a count or the
// literal infinity, and CPUWeight is a bounded weight; neither is a byte magnitude and neither is
// in that roster's stated scope, so two of the three have no owner today.
data fabric_cell_boundary_missing_directives: List<String> = [
"MemorySwapMax (byte magnitude; inside gunbc.systemd_directive_value_grain scope)",
"TasksMax (count or infinity; unscoped)",
"CPUWeight (bounded weight; unscoped)",
]

// EVERY REFUSAL THIS REALIZATION CAN PRODUCE, TYPED AND LOCATED. A Bool or a dropped effect would
// make one silence answer for a vocabulary gap, an ownership wall, a duplicate key and an
// unclassifiable change -- four causes with four different owners and four different remedies.
type FabricCellRealizationRefusal
= BoundaryDirectiveVocabularyIncomplete { address_wire: String, missing: List<String> }
| CellTeardownRefusedByOwnership { address_wire: String, cause: TeardownRefusalCause }
| CellRosterDuplicateKey { address_wire: String }
| CellChangeNotYetRealizable { address_wire: String }
| CellTeardownNotYetRealizable { address_wire: String }

type FabricCellAddressRealization
= AddressRealizable { effect: FabricCellEffect }
| AddressRefused { refusal: FabricCellRealizationRefusal }

// ONE ADDRESS AT A TIME, TOTAL OVER BOTH AXES. The address decides WHAT the effect is; the action
// decides WHETHER there is one. Neither axis is collapsed into the other, so a new address and a
// new action each fail to compile here rather than inheriting an arm that happened to fit.
//
// TEARDOWN AND CHANGE REFUSE RATHER THAN BEING REALIZED, and they refuse for a different reason
// than the boundary does. The boundary is blocked on a vocabulary gap that is decidable and
// buildable. Teardown of a cell root is a DESTRUCTIVE effect on a directory that may hold live
// attempt state, and change realization is gunbc.change_realization's question -- it classifies a
// MemberChanged by whether the runtime address survived, and this family has not yet declared its
// staged intermediates. Realizing either by guessing is how a safe transformation acquires an
// unsafe realization, which is that module's own warning. Both carry their own refusal arms so the
// two are countable separately and neither is mistaken for the other.
fn fabric_cell_address_realization(
action: MemberAction<FabricCellMember, String>,
) -> FabricCellAddressRealization {
match action {
MemberAdded { at: _, to: m } =>
match m.address {
FabricCellRootAddress =>
AddressRealizable {
effect: fabric_cell_directory_create(path: fabric_cell_root_path(slot: m.slot)),
}
FabricCellAttemptRootAddress =>
AddressRealizable {
effect: fabric_cell_directory_create(path: fabric_cell_attempt_root_path(slot: m.slot)),
}
FabricCellResourceBoundaryAddress =>
AddressRefused {
refusal: BoundaryDirectiveVocabularyIncomplete {
address_wire: fabric_cell_resource_address_wire(address: m.address),
missing: fabric_cell_boundary_missing_directives,
},
}
}
MemberChanged { at: _, from: _, to: m } =>
AddressRefused {
refusal: CellChangeNotYetRealizable {
address_wire: fabric_cell_resource_address_wire(address: m.address),
},
}
MemberRemoved { at: _, from: m } =>
AddressRefused {
refusal: CellTeardownNotYetRealizable {
address_wire: fabric_cell_resource_address_wire(address: m.address),
},
}
MemberRemovalRefused { from: m, cause: c } =>
AddressRefused {
refusal: CellTeardownRefusedByOwnership {
address_wire: fabric_cell_resource_address_wire(address: m.address),
cause: c,
},
}
MemberRosterDuplicateKeyRefused { roster: _, first: f, duplicate: _ } =>
AddressRefused {
refusal: CellRosterDuplicateKey {
address_wire: fabric_cell_resource_address_wire(address: f.address),
},
}
}
}

// THE ALL-OR-NOTHING FOLD, AND IT IS THE POINT OF THE MODULE RATHER THAN A GUARD ON IT.
//
// AN EARLIER READING WOULD HAVE EMITTED THE TWO DIRECTORY CREATES AND REFUSED ONLY THE BOUNDARY,
// on the reasoning that a typed located refusal is fail-closed. It is not, and the objection is
// decisive rather than a caveat: a refusal that fires AFTER two directories exist has not stopped
// the line, it has stopped half of it and left the other half standing. That is exactly "a
// partially applied infrastructure change without convergence", which DESIGN 4b names as a class
// the ladder exists to catch.
//
// THE OPERATIONAL FORM OF THE SAME ARGUMENT IS STRONGER THAN THE DOCTRINAL ONE. A cgroup slice
// carrying no resource directives is UNBOUNDED. So the partial form would not merely leave a cell
// incomplete -- it would create cells with no memory ceiling, no task ceiling and no weight, on a
// fleet whose dominant failure is jobs evicted under resource pressure. An unbounded cell is worse
// than an absent one, because absent is honest and unbounded LOOKS provisioned.
//
// So a single refused address refuses the whole plan, and the refusals are ACCUMULATED rather than
// short-circuited: an operator fixing one deficit should see the others in the same report instead
// of rediscovering them one run at a time.
type FabricCellRealization
= CellPlanRealizable { effects: List<FabricCellEffect> }
| CellPlanRefused { refusals: List<FabricCellRealizationRefusal> }

fn fabric_cell_plan_refusals(
actions: List<MemberAction<FabricCellMember, String>>,
) -> List<FabricCellRealizationRefusal> {
fold(
actions,
init: [],
f: (acc, a) =>
match fabric_cell_address_realization(action: a) {
AddressRefused { refusal: r } => concat(acc, [r])
AddressRealizable { effect: _ } => acc
}
)
}

fn fabric_cell_plan_effects(
actions: List<MemberAction<FabricCellMember, String>>,
) -> List<FabricCellEffect> {
fold(
actions,
init: [],
f: (acc, a) =>
match fabric_cell_address_realization(action: a) {
AddressRealizable { effect: e } => concat(acc, [e])
AddressRefused { refusal: _ } => acc
}
)
}

// THE EFFECTS ARE STILL CONSTRUCTED ON THE REFUSED PATH AND THEN NOT RETURNED, WHICH IS
// DELIBERATE. Building them costs nothing, and it means the create construction -- including its
// exact-form mkdir -- is exercised by every plan rather than only by the plans that survive. A
// realization surface that is only built once the vocabulary lands would be a surface nobody had
// run.
fn fabric_cell_realization(
actions: List<MemberAction<FabricCellMember, String>>,
) -> FabricCellRealization {
let refusals = fabric_cell_plan_refusals(actions: actions)
if list_length(items: refusals) > 0 {
CellPlanRefused { refusals: refusals }
} else {
CellPlanRealizable { effects: fabric_cell_plan_effects(actions: actions) }
}
}

fn fabric_cell_realization_refusal_count(
actions: List<MemberAction<FabricCellMember, String>>,
) -> Int {
list_length(items: fabric_cell_plan_refusals(actions: actions))
}
81 changes: 65 additions & 16 deletions dag/gunbc/fleet_converge_plan.dag
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ import gunbc.fabric_cell_converge {
fabric_cell_resource_address_wire,
fabric_cell_desired_members,
}
import gunbc.fabric_cell_effect {
FabricCellRealizationRefusal,
BoundaryDirectiveVocabularyIncomplete,
CellTeardownRefusedByOwnership,
CellRosterDuplicateKey,
CellChangeNotYetRealizable,
CellTeardownNotYetRealizable,
CellPlanRealizable,
CellPlanRefused,
FabricCellDirectoryCreate,
fabric_cell_realization,
}
import gunbc.fabric_cell_observation_admission {
FabricCellObservationDecision,
FabricCellObservationOutcome,
Expand Down Expand Up @@ -739,7 +751,7 @@ fn fleet_converge_fabric_cell_lines(
FabricCellFamilyNotApplicable => []
FabricCellFamilyConvergedNoActions => []
FabricCellFamilyRealizationUnavailable { planned: p } =>
map(p.actions, a => concat("REFUSED-REALIZATION ", fabric_cell_action_line(action: a)))
fabric_cell_realization_lines(actions: p.actions)
FabricCellFamilyObservationHostMismatch { planned_host: ph, observed_host: oh } =>
[concat(
"REFUSED-HOST-MISMATCH fabric-cell planned_host=", ph,
Expand All @@ -749,6 +761,58 @@ fn fleet_converge_fabric_cell_lines(
}
}

// THE REALIZATION IS NOW ASKED RATHER THAN ASSUMED UNAVAILABLE, AND THAT IS THE WHOLE CHANGE.
// This arm previously rendered every action as an undifferentiated "REFUSED-REALIZATION", which was
// honest when the fabric family had no realization surface at all. It has one now
// (gunbc.fabric_cell_effect), so the family asks it, and an operator reads WHICH address could not
// be realized and WHY instead of learning that something somewhere is unrealizable.
//
// THE PLAN STILL REFUSES AS A WHOLE, and the lines say so rather than implying partial progress.
// gunbc.fabric_cell_effect refuses a cell in which any address is unrealizable, because a cell root
// created without its resource boundary is an UNBOUNDED cgroup slice -- worse than an absent cell,
// since absent is honest and unbounded looks provisioned.
fn fabric_cell_realization_lines(
actions: List<MemberAction<FabricCellMember, String>>,
) -> List<String> {
match fabric_cell_realization(actions: actions) {
CellPlanRealizable { effects: es } =>
map(es, e => match e {
FabricCellDirectoryCreate { path: pth, command: _ } =>
concat("REALIZE fabric-cell create-directory ", pth as String)
Comment on lines +778 to +781

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Execute directory effects before labeling them realized

When an admitted readback has the resource boundary converged but either directory absent, this branch emits REALIZE fabric-cell create-directory even though the fleet-converge apply path never executes the stored ArgvCommand: fabric_cell_apply_lines turns every returned line into a shell comment and explicitly states that no fabric-cell host effect is emitted. Applying such a plan therefore leaves the directory absent despite the plan presenting it as realizable; either wire these effects into the apply/host-effect path or continue rendering them as unavailable.

Useful? React with 👍 / 👎.

})
CellPlanRefused { refusals: rs } =>
concat(
map(rs, r => concat("REFUSED-REALIZATION ", fabric_cell_realization_refusal_line(refusal: r))),
["REFUSED-REALIZATION fabric-cell plan refused as a whole; no address is applied, because a cell root without its resource boundary is an unbounded slice"],
)
}
}

fn fabric_cell_realization_refusal_line(refusal: FabricCellRealizationRefusal) -> String {
match refusal {
BoundaryDirectiveVocabularyIncomplete { address_wire: w, missing: m } =>
concat(
concat("fabric-cell ", w),
concat(" boundary directives unmodeled in extdeps.systemd.unit_file: ", join(m, "; ")),
)
CellTeardownRefusedByOwnership { address_wire: w, cause: _ } =>
concat("fabric-cell ", concat(w, " teardown refused: member is not Owned"))
CellRosterDuplicateKey { address_wire: w } =>
concat("fabric-cell ", concat(w, " roster duplicate key"))
CellChangeNotYetRealizable { address_wire: w } =>
concat("fabric-cell ", concat(w, " change realization undeclared: no staged intermediates (gunbc.change_realization)"))
CellTeardownNotYetRealizable { address_wire: w } =>
concat("fabric-cell ", concat(w, " teardown unmodeled: destructive effect over possibly-live attempt state"))
}
}

// THE COUNT STAYS TOTAL WHEN THE PLAN REFUSES, AND THAT IS DELIBERATE RATHER THAN LEFTOVER.
// With a realization surface in place it is tempting to count only the TYPED refusals, which today
// would report one deficit for a cell whose three addresses all went unapplied. That understates
// the owed work by exactly the amount the all-or-nothing rule causes: when the plan refuses, no
// address is applied, so every action is undone work whether or not its own address was realizable.
// The annotation below was written for the blanket arm and survives the change unaltered because
// its argument was never about the arm -- it is about the deficit being per unit of work owed.
// EVERY UNREALIZABLE ACTION COUNTS AS A REFUSAL, not one refusal for the family. The count is what
// makes the deficit's frequency visible, and the deficit is per unit of work owed: three resources
// that cannot be provisioned is three pieces of undone work, not one inconvenience.
Expand All @@ -765,21 +829,6 @@ fn fleet_converge_fabric_cell_refusal_count(
}
}

fn fabric_cell_action_line(action: MemberAction<FabricCellMember, String>) -> String {
match action {
MemberAdded { at: _, to: t } =>
concat("ADD fabric-cell ", fabric_cell_member_key(member: t))
MemberChanged { at: _, from: _, to: t } =>
concat("CHANGE fabric-cell ", fabric_cell_member_key(member: t))
MemberRemoved { at: _, from: f } =>
concat("REMOVE fabric-cell ", fabric_cell_member_key(member: f))
MemberRemovalRefused { from: f, cause: _ } =>
concat("REFUSED-REMOVAL fabric-cell ", fabric_cell_member_key(member: f))
MemberRosterDuplicateKeyRefused { roster: _, first: f, duplicate: _ } =>
concat("REFUSED-DUPLICATE-KEY fabric-cell ", fabric_cell_member_key(member: f))
}
}

fn fleet_converge_slot_lines(
host: NonEmptyStr,
observed_slots: List<RunnerSlotMember>,
Expand Down
Loading
Loading