From 71a9a1c5d317703914309ed8cc5fdddeb34b3ff1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 10:16:22 +0000 Subject: [PATCH 01/46] extdeps.systemd.systemd_run: typed options, one projection, ArgvCommand everywhere The seven string-glued builders (systemd_run_property_argv, systemd_run_user_transient_arguments, systemd_run_user_wait_arguments, systemd_run_system_scope_argv, systemd_run_property, systemd_run_transient_unit_argv, systemd_run_transient_wait_unit_argv) are deleted. In their place: - SystemdRunOption, a sealed sum whose arms carry systemd-run(1)'s own spellings at systemd 255 (the major the summary ground reads): RunUnit, RunUserManager, RunScope, PropertyShort, PropertyLong, SetEnv, Wait, Quiet, Pipe, Collect, EndOfOptions. - systemd_run_option_words, ONE projection from options to words, with refusals at the wall: a unit name carrying / or a newline, a property value carrying a newline, a setenv name carrying = (the wire cannot carry them; they are refused, not trimmed or split). An unknown flag has no arm to ride: the sum is closed and the projection's fold walks every variant. - Every builder returns ArgvCommand through SystemdRunCommandReading (Ready | Refused); extdeps.exec.command's admit list now admits systemd_run_command_of. - The service operations take the projected words and the transport template leads with the declaration literal, per the materializer's executable-position contract. - Callers migrated: roadmap_dispatch_actuator (the four unit mints return readings; the belt layer composes the resolved program head with the projected words, refusals land in ExecRefused/ExecStepFailed), runner_microvm_lifecycle_realize, runner_throughput_qualification_route, compute.work_provider_local, compute.unit_bounds (the grant fold now yields typed properties), auth approval_device_enrolment_code_issue, gunbc.systemd_run_transient (the bridge carries the typed command end to end; the authority check joins the materialized words against the one projection's words), host_effect_nbd_proxy_serve. Evidence: positive controls pin the projected words byte-identical to the words the string-built forms rendered for existing callers (wait witness, user-wait witness, review-unit option words, nbd wire-name witness); reds witness a newline in a property value, a slash in a unit name and an = in a setenv name refused at the projection. SystemdUnitProperty is widened with the seven real settings the dispatch units bind (StandardOutput, StandardError, ProtectSystem, ProtectHome, PrivateTmp, ReadWritePaths, RemainAfterExit) so no property travels as a free-form string. Debt paydown trial, session witty-tern-54. The census instrument (gunbc.instruments.argv_word_construction_census) counts this population; this module was its specimen row set. CI-lane claim batch over the eight affected witness files: 20 pass, 0 fail. --- dag/extdeps/exec/command.dag | 2 +- dag/extdeps/systemd/systemd.dag | 14 + dag/extdeps/systemd/systemd_run.dag | 323 +++++++++++------- .../approval_device_enrolment_code_issue.dag | 40 ++- dag/gunbc/compute/unit_bounds.dag | 22 +- dag/gunbc/compute/work_provider_local.dag | 47 ++- .../host/host_effect_nbd_proxy_serve.dag | 12 +- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 127 ++++--- .../roadmap/roadmap_dispatch_actuator.dag | 239 ++++++------- .../runner_microvm_lifecycle_realize.dag | 28 +- .../runner_throughput_qualification_route.dag | 6 +- dag/gunbc/systemd_run_transient.dag | 160 ++++----- .../compute/work_class_grant_witness_test.dag | 73 ++-- ...ystemd_run_transient_wait_witness_test.dag | 136 +++++++- ...nbd_proxy_serve_transport_witness_test.dag | 42 ++- ...spatch_worker_confinement_witness_test.dag | 12 +- ...roadmap_dispatch_actuator_witness_test.dag | 66 +++- .../session_placement_witness_test.dag | 12 +- ...r_microvm_slot_controller_witness_test.dag | 31 +- ..._throughput_qualification_witness_test.dag | 30 +- ...ner_microvm_lifecycle_wet_receipt_test.dag | 37 +- 21 files changed, 926 insertions(+), 533 deletions(-) diff --git a/dag/extdeps/exec/command.dag b/dag/extdeps/exec/command.dag index ee94b1e084d..4bd4f18d211 100644 --- a/dag/extdeps/exec/command.dag +++ b/dag/extdeps/exec/command.dag @@ -126,7 +126,7 @@ fn argv_command(program: ProgramIdentity, arguments: List) -> ArgvComman decl_ref(module_path: "extdeps.iproute2.ip_show", decl_name: "ip_route_show_command"), decl_ref(module_path: "extdeps.tools.uname", decl_name: "uname_release_command"), decl_ref(module_path: "extdeps.tools.make", decl_name: "make_command"), - decl_ref(module_path: "extdeps.systemd.systemd_run", decl_name: "systemd_run_user_scope_command"), + decl_ref(module_path: "extdeps.systemd.systemd_run", decl_name: "systemd_run_command_of"), decl_ref(module_path: "extdeps.package_managers.pip", decl_name: "pip_install_pinned_command"), decl_ref(module_path: "extdeps.package_managers.conda_forge", decl_name: "micromamba_create_command"), decl_ref(module_path: "extdeps.printing.orca_slicer", decl_name: "orca_slice_to_project_command"), diff --git a/dag/extdeps/systemd/systemd.dag b/dag/extdeps/systemd/systemd.dag index 17dad20bdeb..d1c2c3b6f9b 100644 --- a/dag/extdeps/systemd/systemd.dag +++ b/dag/extdeps/systemd/systemd.dag @@ -88,6 +88,13 @@ type SystemdUnitProperty | WantsProperty | InvocationIDProperty | FragmentPathProperty + | StandardOutputProperty + | StandardErrorProperty + | ProtectSystemProperty + | ProtectHomeProperty + | PrivateTmpProperty + | ReadWritePathsProperty + | RemainAfterExitProperty // THE WIRE SPELLING AS A SEALED WORD, SO THE CLOSED TYPE SURVIVES ALL THE WAY TO THE ARGV. // @@ -155,6 +162,13 @@ fn systemd_unit_property_wire(property: SystemdUnitProperty) -> NonEmptyStr { WantsProperty => "Wants" as NonEmptyStr InvocationIDProperty => "InvocationID" as NonEmptyStr FragmentPathProperty => "FragmentPath" as NonEmptyStr + StandardOutputProperty => "StandardOutput" as NonEmptyStr + StandardErrorProperty => "StandardError" as NonEmptyStr + ProtectSystemProperty => "ProtectSystem" as NonEmptyStr + ProtectHomeProperty => "ProtectHome" as NonEmptyStr + PrivateTmpProperty => "PrivateTmp" as NonEmptyStr + ReadWritePathsProperty => "ReadWritePaths" as NonEmptyStr + RemainAfterExitProperty => "RemainAfterExit" as NonEmptyStr } } diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 51cd530cf2e..5218351a60b 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -44,134 +44,222 @@ data systemd_run_authority: ExternalAuthority = extdeps_external_authority_ancho fn systemd_run_program() -> ProgramIdentity = uncataloged_program(invocation: "systemd-run") -// THE PROPERTY WORDS ARE RENDERED IN EXACTLY ONE PLACE, and that is the whole point of this -// function existing beside the full-line authority rather than inside it. A systemd unit property -// becomes an argv word here and nowhere else, so the operation's transport, the pure authority and -// every caller are reading one spelling. The transport CANNOT carry the typed record itself: -// push_shell_argv_tokens has arms for Str, List, ProcessArgvExpansion and a refusing arm for -// ambiguous free monoids, and a record reaches none of them — it lands in the catch-all, which -// Display-formats the value into a single argv word rather than refusing. So a record spliced into -// argv would hand systemd-run a fabricated argument at the exact seam that decides whether a unit -// is bounded. The typed population stays on this side of that boundary; only words cross it. -fn systemd_run_property_argv(properties: List) -> List { - fold( - properties, - init: [], - f: (acc, setting) => list_push( - acc, - concat( - "--property=", - concat(systemd_unit_property_wire(property: setting.property) as String, concat("=", setting.value as String)), - ), - ), - ) +// THE WAITING LAUNCHER IS A TOKEN, NOT A WORD LIST: its ArgvCommand is minted only by the typed +// builders below, so what a caller holds is the fact that a specific projection happened, not a +// list it could have glued together itself. +type SystemdSummaryPrintingWait { command: ArgvCommand } + +fn systemd_summary_printing_wait_argv(w: SystemdSummaryPrintingWait) -> List { + argv_words(command: w.command) } -// THE USER-MANAGER TRANSIENT UNIT, modelled beside the system-scope one above because the flags -// systemd-run actually accepts are upstream's, not ours: --user selects the calling user's manager, -// -p sets a unit property, and --setenv binds one environment variable in the started unit. +// ── THE TYPED OPTION MODEL OF systemd-run(1) AT THE GROUNDED MAJOR ───────────────────────────── // -// NO --collect HERE, DELIBERATELY, and it is the difference that makes this worth a second -// function. --collect unloads the unit as soon as it exits, which discards Result and -// ExecMainStatus -- exactly the terminal state a caller starts a transient unit in order to be able -// to read afterwards. A unit that lingers in `failed` is evidence; one that was garbage collected -// is an absence indistinguishable from never having run. Callers that want no residue name a unit -// per attempt and reset it explicitly, which is a decision about evidence rather than a flag. -fn systemd_run_user_transient_arguments( - unit: NonEmptyStr, - properties: List, - setenv_bindings: List, - command_argv: List, -) -> List { - let head = ["--user", concat("--unit=", unit as String)] - let with_properties = fold( - properties, - init: head, - f: (acc, p) => list_push(list_push(acc, "-p"), p), - ) - let with_env = fold( - setenv_bindings, - init: with_properties, - f: (acc, b) => list_push(acc, concat("--setenv=", b)), - ) - fold( - command_argv, - init: list_push(with_env, "--"), - f: (acc, arg) => list_push(acc, arg), - ) +// EVERY WORD THIS MODULE RENDERS IS BORN IN ONE PROJECTION, `systemd_run_option_words`, from typed +// options carrying the man page's own spellings at systemd 255 — the major the summary ground +// below actually reads (grounded_summary_format_majors). No builder glues a flag to a value with +// string literals anymore: the flag words live here, once, each citing its option, and the +// projection REFUSES a value the wire cannot carry rather than trimming or dropping it. +// (Debt paydown trial, session witty-tern-54: the census instrument +// gunbc.instruments.argv_word_construction_census counts this population at identity grain; the +// seven string-glued builders this module used to expose were its specimen rows.) + +// --setenv=NAME=VALUE binds ONE environment variable into the unit (systemd-run(1)). +type SystemdSetenvBinding { + name: NonEmptyStr, + value: NonEmptyStr, } -// THE SYNCHRONOUS FORM: the caller blocks until the unit exits and receives its exit status and its -// stdio (`--wait --pipe`), so a build or a check runs under the unit's resource bounds while the -// process that asked for it observes the result directly. `--collect` unloads the unit afterwards -// whatever its result, so a failed compute unit does not accumulate as a failed unit to reap. -// WHETHER THERE IS A SUMMARY TO READ IS A FACT ABOUT THE INVOCATION, SO THE INVOCATION CARRIES IT. -// systemd-run prints the result and peak lines only under `if (arg_wait && !arg_quiet)` (run.c, -// v255), so --quiet suppresses the summary ENTIRELY: a reader handed a quiet launcher's stderr -// finds no peak, reports MemoryPeakNotReported forever, and is indistinguishable from a healthy -// launcher whose manager is below v254. A DEAD PRODUCER THAT LOOKS LIKE A HEALTHY ONE WITH NOTHING -// TO REPORT is the failure this file is one word away from: RunTransientAndWait and this builder -// differ by exactly "--quiet", and the next author adds a third by copying whichever they reached -// first. So the summary reader does not take a loose String. It takes this token, which is minted -// in exactly one place -- the builder below, the only wait invocation in this module that omits -// --quiet -- and a launcher that cannot print a peak therefore cannot be handed to the thing whose -// contract is to observe one. -type SystemdSummaryPrintingWait { argv: List } +// THE OPTION ARMS AND THEIR WIRE SPELLINGS, one per line, each citing systemd-run(1) at v255: +// RunUnit --unit=UNIT +// RunUserManager --user +// RunScope --scope +// PropertyShort -p NAME=VALUE — flag and value are TWO words +// PropertyLong --property=NAME=VALUE — one word +// SetEnv --setenv=NAME=VALUE +// Wait --wait +// Quiet --quiet +// Pipe --pipe +// Collect --collect +// EndOfOptions -- (systemd-run's own options end; the rest is the command) +type SystemdRunOption + = RunUnit { unit: NonEmptyStr } + | RunUserManager + | RunScope + | PropertyShort { property: SystemdRunProperty } + | PropertyLong { property: SystemdRunProperty } + | SetEnv { binding: SystemdSetenvBinding } + | Wait + | Quiet + | Pipe + | Collect + | EndOfOptions + +// WHAT THE WIRE CANNOT CARRY IS REFUSED AT THE PROJECTION — a refusal at the wall between the +// typed value and the words, not a mangled word downstream. Each arm names the wire fact it +// defends (unit names and setting values are one line and contain no slash; an environment name +// is the text before the first '='). +type SystemdRunOptionWordsReading + = SystemdRunOptionWordsProjected { words: List } + | SystemdRunOptionWordsRefused { reason: String } + +type SystemdRunCommandReading + = SystemdRunCommandReady { command: ArgvCommand } + | SystemdRunCommandRefused { reason: String } + +fn systemd_run_option_word_has_newline(s: String) -> Bool { + string_contains(s: s, pattern: "\n") +} -fn systemd_summary_printing_wait_argv(w: SystemdSummaryPrintingWait) -> List { - w.argv +fn systemd_run_option_word_refused(reason: String) -> SystemdRunOptionWordsReading { + SystemdRunOptionWordsRefused { reason: reason } } -fn systemd_run_user_wait_arguments( - unit: NonEmptyStr, - properties: List, - setenv_bindings: List, - command_argv: List, -) -> SystemdSummaryPrintingWait { - SystemdSummaryPrintingWait { argv: concat(["--wait", "--pipe", "--collect"], systemd_run_user_transient_arguments(unit: unit, properties: properties, setenv_bindings: setenv_bindings, command_argv: command_argv)) } +fn systemd_run_property_options_long(properties: List) -> List { + fold(properties, init: [], f: (acc, p) => list_append(left: acc, right: [PropertyLong { property: p }])) } -// ONE COMMAND IN A TRANSIENT SCOPE OF THE CALLING USER'S MANAGER, bounded by the given properties -// (`systemd-run --user --scope -p NAME=VALUE ... -- CMD`, per the cited man page: a scope runs the -// command synchronously as a child of systemd-run and places it in its own cgroup). The kernel -// enforces the bound, not the command, so an over-budget run is killed by the cgroup instead of -// competing for the host. The program is PATH-resolved because the invoking host is unobserved -// (extdeps.exec.command program_spelling_is_an_observation_claim). -fn systemd_run_user_scope_command(properties: List, command: ArgvCommand) -> ArgvCommand { - let head = ["--user", "--scope"] - let with_properties = fold(systemd_run_property_argv(properties: properties), init: head, f: (acc, word) => list_push(acc, word)) - let with_separator = list_push(with_properties, "--") - argv_command( - program: systemd_run_program(), - arguments: fold(argv_words(command: command), init: with_separator, f: (acc, arg) => list_push(acc, arg)), - ) +fn systemd_run_property_options_short(properties: List) -> List { + fold(properties, init: [], f: (acc, p) => list_append(left: acc, right: [PropertyShort { property: p }])) +} + +// THE ONE PROJECTION STEP. Each option's words are spelled exactly once, here; the fold folds over +// these so a refused command never mints words at all (short-circuit on the first refusal). +fn systemd_run_option_projection_step(acc: SystemdRunOptionWordsReading, option: SystemdRunOption) -> SystemdRunOptionWordsReading { + match acc { + SystemdRunOptionWordsRefused { reason: r } => SystemdRunOptionWordsRefused { reason: r } + SystemdRunOptionWordsProjected { words: words } => + match option { + RunUnit { unit: unit } => + if string_contains(s: unit as String, pattern: "/") { + systemd_run_option_word_refused(reason: "--unit= takes a unit name, and a unit name never contains / (systemd.unit(5))") + } else if systemd_run_option_word_has_newline(s: unit as String) { + systemd_run_option_word_refused(reason: "--unit= takes a unit name, and a unit name is one line") + } else { + SystemdRunOptionWordsProjected { words: list_append(left: words, right: [concat("--unit=", unit as String)]) } + } + RunUserManager => SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["--user"]) } + RunScope => SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["--scope"]) } + PropertyShort { property: p } => + if systemd_run_option_word_has_newline(s: p.value as String) { + systemd_run_option_word_refused(reason: concat("unit setting value is one line, so --property= cannot carry a newline in: ", systemd_unit_property_wire(property: p.property))) + } else { + SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["-p", concat(systemd_unit_property_wire(property: p.property), concat("=", p.value as String))]) } + } + PropertyLong { property: p } => + if systemd_run_option_word_has_newline(s: p.value as String) { + systemd_run_option_word_refused(reason: concat("unit setting value is one line, so --property= cannot carry a newline in: ", systemd_unit_property_wire(property: p.property))) + } else { + SystemdRunOptionWordsProjected { words: list_append(left: words, right: [concat("--property=", concat(systemd_unit_property_wire(property: p.property), concat("=", p.value as String)))]) } + } + SetEnv { binding: b } => + if string_contains(s: b.name as String, pattern: "=") { + systemd_run_option_word_refused(reason: "--setenv= takes NAME=VALUE, so the NAME side carries no = of its own") + } else if systemd_run_option_word_has_newline(s: b.name as String) { + systemd_run_option_word_refused(reason: "--setenv= takes NAME=VALUE, so the NAME side is one line") + } else if systemd_run_option_word_has_newline(s: b.value as String) { + systemd_run_option_word_refused(reason: "--setenv= takes NAME=VALUE, so the VALUE side is one line") + } else { + SystemdRunOptionWordsProjected { words: list_append(left: words, right: [concat("--setenv=", concat(b.name as String, concat("=", b.value as String)))]) } + } + Wait => SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["--wait"]) } + Quiet => SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["--quiet"]) } + Pipe => SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["--pipe"]) } + Collect => SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["--collect"]) } + EndOfOptions => SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["--"]) } + } + } +} + +fn systemd_run_option_words(options: List) -> SystemdRunOptionWordsReading { + fold(options, init: SystemdRunOptionWordsProjected { words: [] }, f: systemd_run_option_projection_step) +} + +// THE COMMAND COMPOSITION: options first, then the command's own words after `--` (the caller's +// argv is appended verbatim; its typing is its own layer's debt, one batch per module). +// The words AFTER the program word. The projection leads with exactly one program spelling, so a +// caller that runs a RESOLVED program path instead (an observation the instance toolchain declares, +// and the only thing the invoking host is allowed to assert about where the program lives) takes +// the tail and substitutes its own head. +type SystemdRunCommandTailFold { + program_pending: Bool, + words: List, +} + +fn systemd_run_command_argument_words(command: ArgvCommand) -> List { + fold(argv_words(command: command), init: SystemdRunCommandTailFold { program_pending: true, words: [] }, f: (acc, w) => + if acc.program_pending { + SystemdRunCommandTailFold { program_pending: false, words: acc.words } + } else { + SystemdRunCommandTailFold { program_pending: false, words: list_append(left: acc.words, right: [w]) } + } + ).words +} + +fn systemd_run_command_of(options: List, command_argv: List) -> SystemdRunCommandReading { + match systemd_run_option_words(options: options) { + SystemdRunOptionWordsRefused { reason: r } => SystemdRunCommandRefused { reason: r } + SystemdRunOptionWordsProjected { words: option_words } => + SystemdRunCommandReady { command: argv_command(program: systemd_run_program(), arguments: list_append(left: option_words, right: command_argv)) } + } } -// ONE COMMAND IN A TRANSIENT SCOPE OF THE SYSTEM MANAGER (`systemd-run --scope -p NAME=VALUE ... -- CMD`). +// ── THE BUILDERS, ALL RETURNING THE TYPED COMMAND ─────────────────────────────────────────────── + +// ONE COMMAND IN A TRANSIENT SCOPE OF THE SYSTEM MANAGER (`systemd-run --scope --property=NAME=VALUE ... -- CMD`). // The user-manager form above needs the caller's own session bus, which a command elevated from // another login does not have; the system manager starts the scope and the kernel bounds its // cgroup. The caller must be root. The command runs with the caller's credentials: systemd-run's // --uid/--gid are not rendered, because in scope mode they switch uid and gid without initialising // the target's supplementary groups and leave the caller's in place (fleet-converge run // 37098981121). A caller that needs another account composes its own privilege drop as CMD. -fn systemd_run_system_scope_argv(properties: List, command_argv: List) -> List { - let with_properties = fold(systemd_run_property_argv(properties: properties), init: ["systemd-run", "--scope"], f: (acc, word) => list_push(acc, word)) - let with_separator = list_push(with_properties, "--") - fold(command_argv, init: with_separator, f: (acc, arg) => list_push(acc, arg)) +// ONE COMMAND IN A TRANSIENT SCOPE OF THE CALLING USER'S MANAGER, bounded by the given properties +// (`systemd-run --user --scope --property=NAME=VALUE ... -- CMD`, per the cited man page: a scope +// runs the command synchronously as a child of systemd-run and places it in its own cgroup). The +// kernel enforces the bound, not the command, so an over-budget run is killed by the cgroup +// instead of competing for the host. The program is PATH-resolved because the invoking host is +// unobserved (extdeps.exec.command program_spelling_is_an_observation_claim). +fn systemd_run_user_scope_command(properties: List, command_argv: List) -> SystemdRunCommandReading { + systemd_run_command_of(options: list_append(left: [RunUserManager, RunScope], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) +} + +fn systemd_run_setenv_options(bindings: List) -> List { + fold(bindings, init: [], f: (acc, b) => list_append(left: acc, right: [SetEnv { binding: b }])) +} + +// THE USER-MANAGER TRANSIENT START: `systemd-run --user --unit=NAME -p NAME=VALUE ... --setenv=K=V +// ... -- CMD`. The property words travel as two words (-p, then NAME=VALUE) and the projected +// order here is the order the string-built form this replaces happened to emit, so the projected +// words for existing callers are byte-identical to what they rendered before the cutover. +fn systemd_run_user_transient_command(unit: NonEmptyStr, properties: List, setenv_bindings: List, command_argv: List) -> SystemdRunCommandReading { + systemd_run_command_of( + options: list_append(left: [RunUserManager, RunUnit { unit: unit }], right: list_append(left: systemd_run_property_options_short(properties: properties), right: list_append(left: systemd_run_setenv_options(bindings: setenv_bindings), right: [EndOfOptions]))), + command_argv: command_argv, + ) +} + +// THE USER-MANAGER WAITING TRANSIENT: the start above plus --wait --pipe --collect, and NOT +// --quiet -- run.c at v255 prints "Finished with result:" and "Memory peak:" only under +// `if (arg_wait && !arg_quiet)`, so the quiet form's stderr never carries the summary this type +// exists to be read for. The wait flags lead because that is the word order the string-built form +// this replaces emitted; the projection keeps it byte-identical for existing callers. +fn systemd_run_user_wait_command(unit: NonEmptyStr, properties: List, setenv_bindings: List, command_argv: List) -> SystemdRunCommandReading { + systemd_run_command_of( + options: list_append(left: [Wait, Pipe, Collect], right: list_append(left: [RunUserManager, RunUnit { unit: unit }], right: list_append(left: systemd_run_property_options_short(properties: properties), right: list_append(left: systemd_run_setenv_options(bindings: setenv_bindings), right: [EndOfOptions])))), + command_argv: command_argv, + ) } -fn systemd_run_property(name: String, value: String) -> String { - join([name, "=", value], "") +fn systemd_run_system_scope_command(properties: List, command_argv: List) -> SystemdRunCommandReading { + systemd_run_command_of(options: list_append(left: [RunScope], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) } // The full invocation, and the authority the materialized operation argv is checked against. It -// COMPOSES the property words rather than re-deriving them; if this fold and the transport template -// ever disagree about word order, systemd_run_transient_operation_argv_matches_authority goes red. -fn systemd_run_transient_unit_argv(unit: NonEmptyStr, properties: List, command_argv: List) -> List { - let launcher = ["systemd-run", concat("--unit=", unit as String), "--collect"] - let with_properties = fold(systemd_run_property_argv(properties: properties), init: launcher, f: (acc, word) => list_push(acc, word)) - let with_separator = list_push(with_properties, "--") - fold(command_argv, init: with_separator, f: (acc, arg) => list_push(acc, arg)) +// COMPOSES the option words through the one projection rather than re-deriving them; if this fold +// and the transport template ever disagree about word order, +// systemd_run_transient_operation_argv_matches_authority goes red. +fn systemd_run_transient_unit_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { + systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [Collect, EndOfOptions])), command_argv: command_argv) } // Waiting is a different operation from starting. It owns the complete wait/quiet/collect @@ -180,11 +268,8 @@ fn systemd_run_transient_unit_argv(unit: NonEmptyStr, properties: List, command_argv: List) -> List { - let launcher = ["systemd-run", concat("--unit=", unit as String), "--wait", "--quiet", "--collect"] - let with_properties = fold(systemd_run_property_argv(properties: properties), init: launcher, f: (acc, word) => list_push(acc, word)) - let with_separator = list_push(with_properties, "--") - fold(command_argv, init: with_separator, f: (acc, arg) => list_push(acc, arg)) +fn systemd_run_transient_wait_unit_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { + systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }, Wait, Quiet, Collect], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) } @@ -214,16 +299,22 @@ fn systemd_run_transient_wait_unit_argv(unit: NonEmptyStr, properties: List, command_argv: List } + input { command_argv: List } output { success: Bool from "exit_success" stdout: String from "stdout" stderr: String from "stderr" } - transport shell { argv: ["systemd-run", "--unit={unit}", "--collect", property_argv, "--", command_argv] } + transport shell { argv: ["systemd-run", command_argv] } exit { 0 => Unit nonzero => String "systemd-run transient unit start failed" @@ -236,13 +327,13 @@ service systemd.SystemdRun { operation RunTransientRetained { requires opaque - input { unit: NonEmptyStr, property_argv: List, command_argv: List } + input { command_argv: List } output { success: Bool from "exit_success" stdout: String from "stdout" stderr: String from "stderr" } - transport shell { argv: ["systemd-run", "--unit={unit}", property_argv, "--", command_argv] } + transport shell { argv: ["systemd-run", command_argv] } exit { 0 => Unit nonzero => String "systemd-run transient unit start failed" @@ -254,13 +345,13 @@ service systemd.SystemdRun { operation RunTransientAndWait { requires opaque - input { unit: NonEmptyStr, property_argv: List, command_argv: List } + input { command_argv: List } output { exit_code: Int from "exit_code" stdout: String from "stdout" stderr: String from "stderr" } - transport shell { argv: ["systemd-run", "--unit={unit}", "--wait", "--quiet", "--collect", property_argv, "--", command_argv] } + transport shell { argv: ["systemd-run", command_argv] } exit { 0 => Unit nonzero => Unit diff --git a/dag/gunbc/auth/approval_device_enrolment_code_issue.dag b/dag/gunbc/auth/approval_device_enrolment_code_issue.dag index 4dc05e56d3a..f032eeb079f 100644 --- a/dag/gunbc/auth/approval_device_enrolment_code_issue.dag +++ b/dag/gunbc/auth/approval_device_enrolment_code_issue.dag @@ -6,7 +6,11 @@ import std.process { ProcessExit, ExitSuccess, ExitFailure, exit_failure } import std.resources { Network } import gunbc.cli_wire { CliWireResponse, CliWirePrintable, CliWireUnprintable } import extdeps.sudo.elevation { sudo_elevate } -import extdeps.systemd.systemd_run { SystemdRunProperty, systemd_run_system_scope_argv } +import extdeps.systemd.systemd_run { + SystemdRunProperty, SystemdRunCommandReading, SystemdRunCommandReady, SystemdRunCommandRefused, + systemd_run_system_scope_command, +} +import extdeps.exec.command { ArgvCommand, argv_words } import extdeps.tools.util_linux_setpriv { setpriv_reuid_regid_argv } import extdeps.systemd { MemoryMax, MemoryHigh } import std.measure { byte_size_count } @@ -79,7 +83,7 @@ fn enrolment_code_issue_ssh_target() -> SshTarget { // typed-module cache cap is derived from the cgroup's memory.high/memory.max, and with neither bound // it refuses HostBudgetUnreadable rather than guessing (fleet-converge run 36556990543). An SSH // session binds no such limit. The scope is a system-manager transient scope (extdeps.systemd.systemd_run -// systemd_run_system_scope_argv) around the account drop below. A user +// systemd_run_system_scope_command) around the account drop below. A user // scope would need that account's session bus, which the elevated command lacks. The bounds are the // broker's own slice rows (gunbc.live_deploy.slice_bounds approval_broker_slice_memory_max and _high). // The verb resolves the broker's routes closure, the same program the broker boots, so the same @@ -97,7 +101,7 @@ fn enrolment_code_issue_scope_properties() -> List { // supplementary groups; systemd-run's own --uid/--gid did not, and every code was minted and then // refused delivery (fleet-converge run 37098981121). The drop sits inside the scope so the bound // still covers the whole verb. -fn enrolment_code_issue_remote_argv(revision: ReleaseRevisionBinding) -> List { +fn enrolment_code_issue_remote_command(revision: ReleaseRevisionBinding) -> SystemdRunCommandReading { let root = srv1_gunbc_approval_broker_root as String let release_dir = approval_broker_release_dir(root: root, revision: revision) let account = fleet_posix_operator_user.name @@ -113,8 +117,7 @@ fn enrolment_code_issue_remote_argv(revision: ReleaseRevisionBinding) -> List CliWireResponse uses net: Network { match prepare_fleet_ssh_agent_context(attempt_raw: approval_device_enrolment_code_issue_attempt_raw) { FleetSshContextRefused { cause: c } => CliWireUnprintable { cause: ("approval device enrolment code: " + c) as NonEmptyStr } FleetSshContextReady { context: context, receipt: _ } => - match typed_argv_exec_over_fleet_ssh(target: enrolment_code_issue_ssh_target(), context: context, argv: enrolment_code_issue_remote_argv(revision: revision)) { - TypedArgvExecRefused { reason: why } => CliWireUnprintable { cause: ("approval device enrolment code: remote invocation refused: " + why) as NonEmptyStr } - TypedArgvExecConverged { result: r } => - if r.success { CliWirePrintable { bytes: r.stdout, exit: ExitSuccess } } - else { CliWireUnprintable { cause: ("approval device enrolment code: remote exit=" + to_string(r.exit_code) + " " + trim(s: r.stderr)) as NonEmptyStr } } + match enrolment_code_issue_remote_command(revision: revision) { + SystemdRunCommandRefused { reason: why } => + CliWireUnprintable { cause: ("approval device enrolment code: remote invocation refused: " + why) as NonEmptyStr } + SystemdRunCommandReady { command: command } => + match typed_argv_exec_over_fleet_ssh( + target: enrolment_code_issue_ssh_target(), + context: context, + argv: sudo_elevate_words(command: command), + ) { + TypedArgvExecRefused { reason: why } => CliWireUnprintable { cause: ("approval device enrolment code: remote invocation refused: " + why) as NonEmptyStr } + TypedArgvExecConverged { result: r } => + if r.success { CliWirePrintable { bytes: r.stdout, exit: ExitSuccess } } + else { CliWireUnprintable { cause: ("approval device enrolment code: remote exit=" + to_string(r.exit_code) + " " + trim(s: r.stderr)) as NonEmptyStr } } + } } } } +fn sudo_elevate_words(command: ArgvCommand) -> List { + let inv = sudo_elevate(command: argv_words(command: command)) + concat([inv.bin_path], inv.args) +} + // THE STEP ENTRY: one host per run, named by the dispatch and required to be srv1; one release, // named by the dispatch. fn approval_device_enrolment_code_issue_wet() -> CliWireResponse diff --git a/dag/gunbc/compute/unit_bounds.dag b/dag/gunbc/compute/unit_bounds.dag index 267517e3dfc..37341b1a4d6 100644 --- a/dag/gunbc/compute/unit_bounds.dag +++ b/dag/gunbc/compute/unit_bounds.dag @@ -1,9 +1,10 @@ module gunbc.compute.unit_bounds -import std.types { String, List } +import std.types { String, List, NonEmptyStr } import std.nat { Nat } import std.measure { Kibibyte, kibibyte_to_byte_size, byte_size_count, Millicore, millicore_count } -import extdeps.systemd.systemd_run { systemd_run_property } +import extdeps.systemd { SystemdUnitProperty, MemoryMax, WorkingDirectoryProperty, KillModeProperty, CPUQuota, TasksMax } +import extdeps.systemd.systemd_run { SystemdRunProperty } // THE ONE FOLD FROM A CAPACITY GRANT TO THE PROPERTIES OF THE UNIT THAT HOLDS IT. It used to live in // gunbc.compute.work_provider_local as compute_unit_properties, and it moved here rather than being @@ -22,8 +23,7 @@ import extdeps.systemd.systemd_run { systemd_run_property } // declaration -- a drop-in or a future default could change it, and a provider would keep treating // a completed wait as an empty cgroup (side-chat review at 773825cf). Binding it makes the policy a // fact of the invocation, and an agent session releases its seat on the same reading. -data compute_kill_mode_property: String = "KillMode" -data compute_kill_mode_value: String = "control-group" +data compute_kill_mode_property: SystemdUnitProperty = KillModeProperty // WHAT A GRANT DOES NOT CARRY, NAMED RATHER THAN INVENTED. product.capacity.pool grants MEMORY: the // lease is an encumbrance over a Memory pool and nothing else, so a CPU quota or a task ceiling is @@ -44,19 +44,21 @@ fn unit_cpu_quota_percent(m: Millicore) -> Nat { millicore_count(m: m) / 10 } -fn compute_grant_unit_properties(granted: Kibibyte, working_directory: String, process_bounds: UnitProcessBounds?) -> List { +fn compute_grant_unit_properties(granted: Kibibyte, working_directory: String, process_bounds: UnitProcessBounds?) -> List { concat( [ - systemd_run_property(name: "MemoryMax", value: to_string(value: byte_size_count(b: kibibyte_to_byte_size(k: granted)))), - systemd_run_property(name: compute_kill_mode_property, value: compute_kill_mode_value), - systemd_run_property(name: "WorkingDirectory", value: working_directory), + SystemdRunProperty { property: MemoryMax, value: to_string(value: byte_size_count(b: kibibyte_to_byte_size(k: granted))) as NonEmptyStr }, + SystemdRunProperty { property: compute_kill_mode_property, value: compute_kill_mode_value }, + SystemdRunProperty { property: WorkingDirectoryProperty, value: working_directory as NonEmptyStr }, ], match process_bounds { Absent => [] Present { value: b } => [ - systemd_run_property(name: "CPUQuota", value: join([to_string(value: unit_cpu_quota_percent(m: b.cpu_quota)), "%"], "")), - systemd_run_property(name: "TasksMax", value: to_string(value: b.tasks_max)), + SystemdRunProperty { property: CPUQuota, value: join([to_string(value: unit_cpu_quota_percent(m: b.cpu_quota)), "%"], "") as NonEmptyStr }, + SystemdRunProperty { property: TasksMax, value: to_string(value: b.tasks_max) as NonEmptyStr }, ] }, ) } + +data compute_kill_mode_value: NonEmptyStr = "control-group" diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index 403fd43a851..ce67dde2c2f 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -11,7 +11,9 @@ import extdeps.shell import extdeps.git { shape_git_worktree_add_detached_argv } import extdeps.git.object_store { GitObjectId, git_object_id_from_untagged_hex, git_object_id_wire_hex } import extdeps.systemd.systemd_run { - systemd_run_user_wait_arguments, systemd_run_property, SystemdSummaryPrintingWait, systemd_summary_printing_wait_argv, + SystemdSummaryPrintingWait, systemd_summary_printing_wait_argv, SystemdSetenvBinding, + SystemdRunCommandReading, SystemdRunCommandReady, SystemdRunCommandRefused, SystemdRunProperty, + systemd_run_user_wait_command, SystemdRunWaitSummary, systemd_run_wait_summary, SystemdServiceResultReading, ServiceResultRead, ServiceResultUnrecognized, ServiceResultNotReported, SystemdServiceResult, ServiceResultOomKill, systemd_service_result_wire, SystemdMemoryPeakReading, MemoryPeakRead, MemoryPeakUnparseable, MemoryPeakNotReported, @@ -311,7 +313,7 @@ fn compute_unit_name(identity_hex: String) -> NonEmptyStr { // THE PROPERTIES ARE A FOLD SO THE BOUND POLICY IS CHECKABLE, and the fold is // gunbc.compute.unit_bounds compute_grant_unit_properties -- the one authority from a grant to a // unit, shared with the agent session's unit. A compute unit states no process bounds. -fn compute_unit_properties(layout: ComputeLayout, granted: Kibibyte) -> List { +fn compute_unit_properties(layout: ComputeLayout, granted: Kibibyte) -> List { compute_grant_unit_properties(granted: granted, working_directory: layout.checkout, process_bounds: none) } @@ -378,26 +380,35 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden ComputeExecOk { stdout: v, stderr: _ } => v ComputeExecFailed { exit_code: _, stdout: _, stderr: _ } => "" }) - let launcher = systemd_run_user_wait_arguments( + match systemd_run_user_wait_command( unit: compute_unit_name(identity_hex: identity_hex), properties: compute_unit_properties(layout: layout, granted: granted), setenv_bindings: [ - join(["CARGO_TARGET_DIR=", layout.target_dir], ""), - join(["RUSTC_WRAPPER=", sccache_installed_binary_path], ""), + SystemdSetenvBinding { name: "CARGO_TARGET_DIR", value: layout.target_dir }, + SystemdSetenvBinding { name: "RUSTC_WRAPPER", value: sccache_installed_binary_path }, ], command_argv: argv, - ) - let r = compute_exec( - program: systemd_run, - workdir: layout.checkout, - args: systemd_summary_printing_wait_argv(w: launcher), - ) - ComputeUnitRun { - exec: r, - summary: systemd_run_wait_summary(launcher: launcher, standing: standing, stderr: match r { - ComputeExecOk { stdout: _, stderr } => stderr - ComputeExecFailed { exit_code: _, stdout: _, stderr } => stderr - }), + ) { + SystemdRunCommandRefused { reason: why } => + ComputeUnitRun { + exec: ComputeExecFailed { exit_code: 126, stdout: "", stderr: concat("systemd-run command refused: ", why) }, + summary: SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } }, + } + SystemdRunCommandReady { command: command } => { + let launcher = SystemdSummaryPrintingWait { command: command } + let r = compute_exec( + program: systemd_run, + workdir: layout.checkout, + args: systemd_summary_printing_wait_argv(w: launcher), + ) + ComputeUnitRun { + exec: r, + summary: systemd_run_wait_summary(launcher: launcher, standing: standing, stderr: match r { + ComputeExecOk { stdout: _, stderr } => stderr + ComputeExecFailed { exit_code: _, stdout: _, stderr } => stderr + }), + } + } } } } @@ -543,7 +554,7 @@ fn unit_termination_decide(o: UnitObservations) -> UnitTermination { PopulationEmpty { detail: pd } => UnitAttemptCompleted { detail: join([ - "systemd-run --wait returned success under ", compute_kill_mode_property, "=", compute_kill_mode_value, + "systemd-run --wait returned success under ", compute_kill_mode_property as String, "=", compute_kill_mode_value as String, " and ", pd, ], ""), } diff --git a/dag/gunbc/host/host_effect_nbd_proxy_serve.dag b/dag/gunbc/host/host_effect_nbd_proxy_serve.dag index dd80363d0af..fee3c72db37 100644 --- a/dag/gunbc/host/host_effect_nbd_proxy_serve.dag +++ b/dag/gunbc/host/host_effect_nbd_proxy_serve.dag @@ -19,6 +19,7 @@ import extdeps.bmc.webui.nbd_proxy_serve { bmcweb_session_token_env_ref, } import extdeps.exec.command { ArgvCommand, argv_words } +import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command } import extdeps.tools.nbdkit { nbdkit_readonly_file_serve_command } import extdeps.tools.websocat { websocat_nbd_proxy_bridge_command } import extdeps.filesystem.filesystem_io { @@ -108,10 +109,15 @@ fn host_effect_nbd_proxy_serve_systemd_run_transient( command_argv: List, transport: HostEffectTransport, ) -> String? { - match systemd_run_transient_read(transport: transport, unit: unit, properties: [], command_argv: command_argv) { - SystemdRunTransientStarted { stdout: _ } => Absent - SystemdRunTransientRefused { reason: why } => + match systemd_run_transient_unit_command(unit: unit, properties: [], command_argv: command_argv) { + SystemdRunCommandRefused { reason: why } => Present { value: concat(concat("unit ", unit as String), concat(": ", why)) } + SystemdRunCommandReady { command: command } => + match systemd_run_transient_read(transport: transport, command: command) { + SystemdRunTransientStarted { stdout: _ } => Absent + SystemdRunTransientRefused { reason: why } => + Present { value: concat(concat("unit ", unit as String), concat(": ", why)) } + } } } diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index 068e6fdf9ca..dddb654a995 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -19,12 +19,13 @@ import gunbc.roadmap.roadmap_review_function { } import gunbc.roadmap.roadmap_review_criteria { ReviewCriterion } import gunbc.roadmap.roadmap_goal_audit_role { goal_audit_criterion_key, goal_audit_approved, goal_audit_reconcile, goal_audit_brief } +import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } import gunbc.roadmap_dispatch_actuator { dispatch_attempt_review_dir_for_instance, dispatch_attempt_review_verdict_path_for_instance, dispatch_attempt_review_verdict_basename, - dispatch_review_unit_name, dispatch_review_unit_argv, + dispatch_review_unit_name, dispatch_review_unit_command, dispatch_unit_exec_argv, dispatch_attempt_audit_dir_for_instance, dispatch_attempt_audit_verdict_path_for_instance, dispatch_attempt_audit_verdict_basename, - dispatch_audit_unit_name, dispatch_audit_unit_argv, - dispatch_attempt_supervisor_dir_for_instance, dispatch_supervisor_unit_name, dispatch_supervisor_unit_argv, + dispatch_audit_unit_name, dispatch_audit_unit_command, dispatch_unit_exec_argv, + dispatch_attempt_supervisor_dir_for_instance, dispatch_supervisor_unit_name, dispatch_supervisor_unit_command, dispatch_unit_exec_argv, dispatch_supervisor_attempt_identity, } import extdeps.git { shape_git_merge_base_args } @@ -105,7 +106,7 @@ import gunbc.roadmap_dispatch_actuator { DispatchLiveSession, DispatchSpawnCommands, DispatchSessionContainer, TmuxSessionContainer, SystemdUnitContainer, - dispatch_worker_unit_properties_for_plan, dispatch_worker_unit_run, + dispatch_worker_unit_properties_for_plan, dispatch_worker_unit_command, dispatch_unit_exec_argv, dispatch_attempt_unit_name, attempt_unit_observation, worker_process_from_attempt_unit, @@ -1326,10 +1327,15 @@ fn belt_session_container_create( match session_placement_begin_launch(stores: held.stores, grant: held.grant) { SessionLaunchRefused { detail } => ExecStepFailed { step: "session-launch-gate", detail: detail } SessionLaunchAdmitted { grant: launched } => - belt_exec_steps( - steps: [BeltExecStep { step: "harness-unit-start", command: dispatch_worker_unit_run(instance: instance, plan: plan, granted: launched.amount, process_bounds: session_process_bounds) }], - workdir: workdir, - ) + match dispatch_worker_unit_command(instance: instance, plan: plan, granted: launched.amount, process_bounds: session_process_bounds) { + SystemdRunCommandRefused { reason: why } => + ExecStepFailed { step: "harness-unit-start", detail: why } + SystemdRunCommandReady { command: command } => + belt_exec_steps( + steps: [BeltExecStep { step: "harness-unit-start", command: dispatch_unit_exec_argv(command: command, resolved_program: plan.systemd_run_program) }], + workdir: workdir, + ) + } } } } @@ -2530,48 +2536,55 @@ fn belt_supervisor_convene( launch: launch, } WorktreeHeadObserved { revision } => - match belt_exec( - cmd: dispatch_supervisor_unit_argv( - systemd_run_program: systemd_run, - instance: instance, - node_id: nid, - attempt_key: escalation.attempt_key, - turn: escalation.turn, - worktree_path: worktree, - brief: belt_supervisor_convene_brief( - node: node, - escalation: escalation, - checkpoint: belt_supervisor_checkpoint_lines( - capture: belt_prior_attempt_capture(instance: instance, node_id: nid, attempt_key: escalation.attempt_key), - head_sha: revision as String, - history_line: belt_supervisor_history_line(instance: instance, node_id: nid, attempt_key: escalation.attempt_key, turn: escalation.turn), - ), + match dispatch_supervisor_unit_command( + instance: instance, + node_id: nid, + attempt_key: escalation.attempt_key, + turn: escalation.turn, + worktree_path: worktree, + brief: belt_supervisor_convene_brief( + node: node, + escalation: escalation, + checkpoint: belt_supervisor_checkpoint_lines( + capture: belt_prior_attempt_capture(instance: instance, node_id: nid, attempt_key: escalation.attempt_key), + head_sha: revision as String, + history_line: belt_supervisor_history_line(instance: instance, node_id: nid, attempt_key: escalation.attempt_key, turn: escalation.turn), ), ), - workdir: worktree, ) { - ExecOk { stdout: _ } => - SupervisorConvened { - node_id: node.node as String, - unit: unit as String, - provider: provider_wire_label, - launch: launch, - } - ExecFailed { program: _, exit_code, stderr } => + SystemdRunCommandRefused { reason: why } => SpawnFailed { node_id: node.node as String, step: "supervisor-unit-start", - detail: join(["the supervisor unit could not be started (exit ", to_string(exit_code), "): ", stderr], ""), + detail: why, provider: provider_wire_label, launch: launch, } - ExecRefused { program: _, reason } => - SpawnFailed { - node_id: node.node as String, - step: "supervisor-unit-start", - detail: reason, - provider: provider_wire_label, - launch: launch, + SystemdRunCommandReady { command: command } => + match belt_exec(cmd: dispatch_unit_exec_argv(command: command, resolved_program: systemd_run), workdir: worktree) { + ExecOk { stdout: _ } => + SupervisorConvened { + node_id: node.node as String, + unit: unit as String, + provider: provider_wire_label, + launch: launch, + } + ExecFailed { program: _, exit_code, stderr } => + SpawnFailed { + node_id: node.node as String, + step: "supervisor-unit-start", + detail: join(["the supervisor unit could not be started (exit ", to_string(exit_code), "): ", stderr], ""), + provider: provider_wire_label, + launch: launch, + } + ExecRefused { program: _, reason } => + SpawnFailed { + node_id: node.node as String, + step: "supervisor-unit-start", + detail: reason, + provider: provider_wire_label, + launch: launch, + } } } } @@ -9852,13 +9865,15 @@ fn belt_audit_spawn(instance: HostDashboardInstance, progress: WorkflowAttemptPr verdict_path: dispatch_attempt_audit_verdict_path_for_instance(instance: instance, node_id: node_id, attempt_key: progress.attempt_key, head_sha: head), ) let unit = dispatch_audit_unit_name(node_id: progress.node_id, attempt_key: progress.attempt_key) - match belt_exec( - cmd: dispatch_audit_unit_argv(systemd_run_program: systemd_run, instance: instance, node_id: node_id, attempt_key: progress.attempt_key, worktree_path: worktree, brief: brief, head_sha: head), - workdir: worktree, - ) { - ExecOk { stdout: _ } => ReviewSpawned { node_id: progress.node_id, criterion: goal_audit_criterion_key, unit: unit as String } - ExecFailed { program: _, exit_code, stderr } => ReviewFailed { node_id: progress.node_id, criterion: goal_audit_criterion_key, detail: join(["the auditor unit could not be started (exit ", to_string(exit_code), "): ", stderr], "") } - ExecRefused { program: _, reason } => ReviewFailed { node_id: progress.node_id, criterion: goal_audit_criterion_key, detail: reason } + match dispatch_audit_unit_command(instance: instance, node_id: node_id, attempt_key: progress.attempt_key, worktree_path: worktree, brief: brief, head_sha: head) { + SystemdRunCommandRefused { reason: why } => + ReviewFailed { node_id: progress.node_id, criterion: goal_audit_criterion_key, detail: why } + SystemdRunCommandReady { command: command } => + match belt_exec(cmd: dispatch_unit_exec_argv(command: command, resolved_program: systemd_run), workdir: worktree) { + ExecOk { stdout: _ } => ReviewSpawned { node_id: progress.node_id, criterion: goal_audit_criterion_key, unit: unit as String } + ExecFailed { program: _, exit_code, stderr } => ReviewFailed { node_id: progress.node_id, criterion: goal_audit_criterion_key, detail: join(["the auditor unit could not be started (exit ", to_string(exit_code), "): ", stderr], "") } + ExecRefused { program: _, reason } => ReviewFailed { node_id: progress.node_id, criterion: goal_audit_criterion_key, detail: reason } + } } } } @@ -10124,13 +10139,15 @@ fn belt_review_spawn(instance: HostDashboardInstance, node_id: String, attempt_k let verdict_path = dispatch_attempt_review_verdict_path_for_instance(instance: instance, node_id: nid, attempt_key: attempt_key, head_sha: head, criterion_key: key) let brief = join([call.brief, review_verdict_instruction(verdict_path: verdict_path)], "") let unit = dispatch_review_unit_name(node_id: node_id, attempt_key: attempt_key, criterion_key: key) - match belt_exec( - cmd: dispatch_review_unit_argv(systemd_run_program: systemd_run, instance: instance, node_id: nid, attempt_key: attempt_key, worktree_path: worktree, brief: brief, head_sha: head, criterion_key: key), - workdir: worktree, - ) { - ExecOk { stdout: _ } => ReviewSpawned { node_id: node_id, criterion: call.id, unit: unit as String } - ExecFailed { program: _, exit_code, stderr } => ReviewFailed { node_id: node_id, criterion: call.id, detail: join(["the reviewer unit could not be started (exit ", to_string(exit_code), "): ", stderr], "") } - ExecRefused { program: _, reason } => ReviewFailed { node_id: node_id, criterion: call.id, detail: reason } + match dispatch_review_unit_command(instance: instance, node_id: nid, attempt_key: attempt_key, worktree_path: worktree, brief: brief, head_sha: head, criterion_key: key) { + SystemdRunCommandRefused { reason: why } => + ReviewFailed { node_id: node_id, criterion: call.id, detail: why } + SystemdRunCommandReady { command: command } => + match belt_exec(cmd: dispatch_unit_exec_argv(command: command, resolved_program: systemd_run), workdir: worktree) { + ExecOk { stdout: _ } => ReviewSpawned { node_id: node_id, criterion: call.id, unit: unit as String } + ExecFailed { program: _, exit_code, stderr } => ReviewFailed { node_id: node_id, criterion: call.id, detail: join(["the reviewer unit could not be started (exit ", to_string(exit_code), "): ", stderr], "") } + ExecRefused { program: _, reason } => ReviewFailed { node_id: node_id, criterion: call.id, detail: reason } + } } } } diff --git a/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag b/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag index f63d4d99ac1..22bcc879931 100644 --- a/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag +++ b/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag @@ -51,8 +51,17 @@ import extdeps.git { shape_git_worktree_add_argv, shape_git_worktree_add_detache import std.measure { ByteSize, byte_size_count, Kibibyte } import std.algebra { trim } import extdeps.systemd.systemctl -import extdeps.systemd { systemd_unit_property_wire, SystemdUnitProperty, LoadState, SubState, ExecMainStatus } -import extdeps.systemd.systemd_run { systemd_run_user_transient_arguments, systemd_run_property } +import extdeps.systemd { + systemd_unit_property_wire, SystemdUnitProperty, LoadState, SubState, ExecMainStatus, + MemoryMax, WorkingDirectoryProperty, StandardOutputProperty, StandardErrorProperty, + ProtectSystemProperty, ProtectHomeProperty, PrivateTmpProperty, ReadWritePathsProperty, + RemainAfterExitProperty, +} +import extdeps.exec.command { ArgvCommand } +import extdeps.systemd.systemd_run { + SystemdRunCommandReading, SystemdRunCommandReady, SystemdRunCommandRefused, + systemd_run_user_transient_command, systemd_run_command_argument_words, +} import gunbc.compute.unit_bounds { UnitProcessBounds, compute_grant_unit_properties } import extdeps.tmux { shape_tmux_new_session_argv, @@ -1191,17 +1200,17 @@ fn dispatch_review_unit_properties( working_directory: String, log_path: String, review_dir: String, -) -> List { +) -> List { [ - systemd_run_property(name: "MemoryMax", value: to_string(value: byte_size_count(instance.dispatch_worker_memory_max))), - systemd_run_property(name: "WorkingDirectory", value: working_directory), - systemd_run_property(name: "StandardOutput", value: join(["append:", log_path], "")), - systemd_run_property(name: "StandardError", value: join(["append:", log_path], "")), - systemd_run_property(name: "ProtectSystem", value: "strict"), - systemd_run_property(name: "ProtectHome", value: "read-only"), - systemd_run_property(name: "PrivateTmp", value: "yes"), - systemd_run_property(name: "ReadWritePaths", value: review_dir), - systemd_run_property(name: "RemainAfterExit", value: "yes"), + SystemdRunProperty { property: MemoryMax, value: to_string(value: byte_size_count(instance.dispatch_worker_memory_max)) as NonEmptyStr }, + SystemdRunProperty { property: WorkingDirectoryProperty, value: working_directory as NonEmptyStr }, + SystemdRunProperty { property: StandardOutputProperty, value: join(["append:", log_path], "") as NonEmptyStr }, + SystemdRunProperty { property: StandardErrorProperty, value: join(["append:", log_path], "") as NonEmptyStr }, + SystemdRunProperty { property: ProtectSystemProperty, value: "strict" as NonEmptyStr }, + SystemdRunProperty { property: ProtectHomeProperty, value: "read-only" as NonEmptyStr }, + SystemdRunProperty { property: PrivateTmpProperty, value: "yes" as NonEmptyStr }, + SystemdRunProperty { property: ReadWritePathsProperty, value: review_dir as NonEmptyStr }, + SystemdRunProperty { property: RemainAfterExitProperty, value: "yes" as NonEmptyStr }, ] } @@ -1243,8 +1252,16 @@ fn gunbc_harness_review_argv( // fleet ssh context's safe-segment law (gunbc.fleet_known_hosts_anchor fleet_ssh_attempt_identity) // demands of the attempt identity it consumes as a path segment -- the pre-fix reviewer built it // from the absolute verdict path and every lane's seat bind refused before any ssh leg. -fn dispatch_review_unit_argv( - systemd_run_program: FilePath, +// THE BELT LAYER RUNS THE RESOLVED PROGRAM, not the spelling: the instance toolchain declares where +// systemd-run lives, and that declaration is the only thing the invoking host is allowed to assert +// about an unobserved host's program location (extdeps.exec.command +// program_spelling_is_an_observation_claim). The option and command words are untouched -- they are +// the one projection's; only the head is substituted. +fn dispatch_unit_exec_argv(command: ArgvCommand, resolved_program: FilePath) -> HostExecArgv { + host_exec_argv(program: resolved_program as String, args: systemd_run_command_argument_words(command: command)) +} + +fn dispatch_review_unit_command( instance: HostDashboardInstance, node_id: RoadmapNodeId, attempt_key: String, @@ -1252,30 +1269,27 @@ fn dispatch_review_unit_argv( brief: String, head_sha: String, criterion_key: String, -) -> HostExecArgv { - host_exec_argv( - program: systemd_run_program as String, - args: systemd_run_user_transient_arguments( - unit: dispatch_review_unit_name(node_id: node_id as String, attempt_key: attempt_key, criterion_key: criterion_key), - properties: dispatch_review_unit_properties( - instance: instance, - working_directory: instance.repo_root as String, - log_path: dispatch_attempt_review_log_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key, criterion_key: criterion_key), - review_dir: dispatch_attempt_review_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key), - ), - setenv_bindings: [], - command_argv: gunbc_harness_review_argv( - gunbc_program: instance.serve_binary as String, - repo_root: instance.repo_root as String, - worktree_path: worktree_path, - brief: brief, - timeout_program: instance.toolchain.timeout as String, - request_scratch_path: dispatch_attempt_review_request_scratch_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key, criterion_key: criterion_key), - events_path: dispatch_attempt_review_events_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key, criterion_key: criterion_key), - verdict_dir: dispatch_attempt_review_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key), - verdict_name: dispatch_attempt_review_verdict_basename(head_sha: head_sha, criterion_key: criterion_key), - attempt_identity: join(["reviewer:", node_id as String, "-", attempt_key, "-", criterion_key], ""), - ), +) -> SystemdRunCommandReading { + systemd_run_user_transient_command( + unit: dispatch_review_unit_name(node_id: node_id as String, attempt_key: attempt_key, criterion_key: criterion_key), + properties: dispatch_review_unit_properties( + instance: instance, + working_directory: instance.repo_root as String, + log_path: dispatch_attempt_review_log_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key, criterion_key: criterion_key), + review_dir: dispatch_attempt_review_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key), + ), + setenv_bindings: [], + command_argv: gunbc_harness_review_argv( + gunbc_program: instance.serve_binary as String, + repo_root: instance.repo_root as String, + worktree_path: worktree_path, + brief: brief, + timeout_program: instance.toolchain.timeout as String, + request_scratch_path: dispatch_attempt_review_request_scratch_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key, criterion_key: criterion_key), + events_path: dispatch_attempt_review_events_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key, criterion_key: criterion_key), + verdict_dir: dispatch_attempt_review_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key), + verdict_name: dispatch_attempt_review_verdict_basename(head_sha: head_sha, criterion_key: criterion_key), + attempt_identity: join(["reviewer:", node_id as String, "-", attempt_key, "-", criterion_key], ""), ), ) } @@ -1302,44 +1316,40 @@ fn dispatch_audit_unit_name(node_id: String, attempt_key: String) -> NonEmptyStr data gunbc_harness_auditor_function: String = "harness_auditor_cli" -fn dispatch_audit_unit_argv( - systemd_run_program: FilePath, +fn dispatch_audit_unit_command( instance: HostDashboardInstance, node_id: RoadmapNodeId, attempt_key: String, worktree_path: String, brief: String, head_sha: String, -) -> HostExecArgv { +) -> SystemdRunCommandReading { let dir = dispatch_attempt_audit_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key) - host_exec_argv( - program: systemd_run_program as String, - args: systemd_run_user_transient_arguments( - unit: dispatch_audit_unit_name(node_id: node_id as String, attempt_key: attempt_key), - properties: dispatch_review_unit_properties( - instance: instance, - working_directory: instance.repo_root as String, - log_path: join([dir, "/auditor-stdout.log"], ""), - review_dir: dir, - ), - setenv_bindings: [], - command_argv: [ - instance.serve_binary as String, - "run", - "--source-root", join([instance.repo_root as String, "/dag"], ""), - "--source-root", join([instance.repo_root as String, "/src/v2"], ""), - "--entry", join([instance.repo_root as String, "/", gunbc_harness_worker_entry], ""), - "--function", gunbc_harness_auditor_function, - "--arg", join(["worktree=", worktree_path], ""), - "--arg", join(["brief=", brief], ""), - "--arg", join(["timeout_program=", instance.toolchain.timeout as String], ""), - "--arg", join(["request_scratch_path=", dir, "/harness-request.json"], ""), - "--arg", join(["events_path=", dir, "/provider-events.jsonl"], ""), - "--arg", join(["verdict_dir=", dir], ""), - "--arg", join(["verdict_name=", dispatch_attempt_audit_verdict_basename(head_sha: head_sha)], ""), - "--arg", join(["attempt_identity=auditor:", node_id as String, "-", attempt_key], ""), - ], - ), + systemd_run_user_transient_command( + unit: dispatch_audit_unit_name(node_id: node_id as String, attempt_key: attempt_key), + properties: dispatch_review_unit_properties( + instance: instance, + working_directory: instance.repo_root as String, + log_path: join([dir, "/auditor-stdout.log"], ""), + review_dir: dir, + ), + setenv_bindings: [], + command_argv: [ + instance.serve_binary as String, + "run", + "--source-root", join([instance.repo_root as String, "/dag"], ""), + "--source-root", join([instance.repo_root as String, "/src/v2"], ""), + "--entry", join([instance.repo_root as String, "/", gunbc_harness_worker_entry], ""), + "--function", gunbc_harness_auditor_function, + "--arg", join(["worktree=", worktree_path], ""), + "--arg", join(["brief=", brief], ""), + "--arg", join(["timeout_program=", instance.toolchain.timeout as String], ""), + "--arg", join(["request_scratch_path=", dir, "/harness-request.json"], ""), + "--arg", join(["events_path=", dir, "/provider-events.jsonl"], ""), + "--arg", join(["verdict_dir=", dir], ""), + "--arg", join(["verdict_name=", dispatch_attempt_audit_verdict_basename(head_sha: head_sha)], ""), + "--arg", join(["attempt_identity=auditor:", node_id as String, "-", attempt_key], ""), + ], ) } @@ -1369,44 +1379,40 @@ data gunbc_harness_supervisor_function: String = "harness_supervisor_cli" // record convene at most one session -- a second convene over the same record names the same unit, // and the belt's liveness gate reads that. The worktree stays read-only to the session, as the // reviewer's is: the supervisor adjudicates the checkpoint; it does not edit it. -fn dispatch_supervisor_unit_argv( - systemd_run_program: FilePath, +fn dispatch_supervisor_unit_command( instance: HostDashboardInstance, node_id: RoadmapNodeId, attempt_key: String, turn: Int, worktree_path: String, brief: String, -) -> HostExecArgv { +) -> SystemdRunCommandReading { let dir = dispatch_attempt_supervisor_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key) - host_exec_argv( - program: systemd_run_program as String, - args: systemd_run_user_transient_arguments( - unit: dispatch_supervisor_unit_name(node_id: node_id as String, attempt_key: attempt_key, turn: turn), - properties: dispatch_review_unit_properties( - instance: instance, - working_directory: instance.repo_root as String, - log_path: join([dir, "/supervisor-stdout.log"], ""), - review_dir: dir, - ), - setenv_bindings: [], - command_argv: [ - instance.serve_binary as String, - "run", - "--source-root", join([instance.repo_root as String, "/dag"], ""), - "--source-root", join([instance.repo_root as String, "/src/v2"], ""), - "--entry", join([instance.repo_root as String, "/", gunbc_harness_worker_entry], ""), - "--function", gunbc_harness_supervisor_function, - "--arg", join(["worktree=", worktree_path], ""), - "--arg", join(["brief=", brief], ""), - "--arg", join(["timeout_program=", instance.toolchain.timeout as String], ""), - "--arg", join(["request_scratch_path=", dir, "/harness-request.json"], ""), - "--arg", join(["events_path=", dir, "/provider-events.jsonl"], ""), - "--arg", join(["verdict_dir=", dir], ""), - "--arg", join(["verdict_name=", supervisor_verdict_basename as String], ""), - "--arg", join(["attempt_identity=", dispatch_supervisor_attempt_identity(node_id: node_id as String, attempt_key: attempt_key, turn: turn)], ""), - ], - ), + systemd_run_user_transient_command( + unit: dispatch_supervisor_unit_name(node_id: node_id as String, attempt_key: attempt_key, turn: turn), + properties: dispatch_review_unit_properties( + instance: instance, + working_directory: instance.repo_root as String, + log_path: join([dir, "/supervisor-stdout.log"], ""), + review_dir: dir, + ), + setenv_bindings: [], + command_argv: [ + instance.serve_binary as String, + "run", + "--source-root", join([instance.repo_root as String, "/dag"], ""), + "--source-root", join([instance.repo_root as String, "/src/v2"], ""), + "--entry", join([instance.repo_root as String, "/", gunbc_harness_worker_entry], ""), + "--function", gunbc_harness_supervisor_function, + "--arg", join(["worktree=", worktree_path], ""), + "--arg", join(["brief=", brief], ""), + "--arg", join(["timeout_program=", instance.toolchain.timeout as String], ""), + "--arg", join(["request_scratch_path=", dir, "/harness-request.json"], ""), + "--arg", join(["events_path=", dir, "/provider-events.jsonl"], ""), + "--arg", join(["verdict_dir=", dir], ""), + "--arg", join(["verdict_name=", supervisor_verdict_basename as String], ""), + "--arg", join(["attempt_identity=", dispatch_supervisor_attempt_identity(node_id: node_id as String, attempt_key: attempt_key, turn: turn)], ""), + ], ) } @@ -2272,17 +2278,17 @@ fn dispatch_worker_unit_properties( worker_log_path: String, granted: Kibibyte, process_bounds: UnitProcessBounds, -) -> List { +) -> List { concat( compute_grant_unit_properties(granted: granted, working_directory: working_directory, process_bounds: Present { value: process_bounds }), [ - systemd_run_property(name: "StandardOutput", value: join(["append:", worker_log_path], "")), - systemd_run_property(name: "StandardError", value: join(["append:", worker_log_path], "")), - systemd_run_property(name: "ProtectSystem", value: "strict"), - systemd_run_property(name: "ProtectHome", value: "read-only"), - systemd_run_property(name: "PrivateTmp", value: "yes"), - systemd_run_property(name: "ReadWritePaths", value: dispatch_worker_writable_paths(instance: instance)), - systemd_run_property(name: "RemainAfterExit", value: "yes"), + SystemdRunProperty { property: StandardOutputProperty, value: join(["append:", worker_log_path], "") as NonEmptyStr }, + SystemdRunProperty { property: StandardErrorProperty, value: join(["append:", worker_log_path], "") as NonEmptyStr }, + SystemdRunProperty { property: ProtectSystemProperty, value: "strict" as NonEmptyStr }, + SystemdRunProperty { property: ProtectHomeProperty, value: "read-only" as NonEmptyStr }, + SystemdRunProperty { property: PrivateTmpProperty, value: "yes" as NonEmptyStr }, + SystemdRunProperty { property: ReadWritePathsProperty, value: dispatch_worker_writable_paths(instance: instance) as NonEmptyStr }, + SystemdRunProperty { property: RemainAfterExitProperty, value: "yes" as NonEmptyStr }, ], ) } @@ -2323,7 +2329,7 @@ type DispatchWorkerUnitPlan { inner_argv: List } -fn dispatch_worker_unit_properties_for_plan(instance: HostDashboardInstance, plan: DispatchWorkerUnitPlan, granted: Kibibyte, process_bounds: UnitProcessBounds) -> List { +fn dispatch_worker_unit_properties_for_plan(instance: HostDashboardInstance, plan: DispatchWorkerUnitPlan, granted: Kibibyte, process_bounds: UnitProcessBounds) -> List { dispatch_worker_unit_properties( instance: instance, working_directory: plan.working_directory, @@ -2333,15 +2339,12 @@ fn dispatch_worker_unit_properties_for_plan(instance: HostDashboardInstance, pla ) } -fn dispatch_worker_unit_run(instance: HostDashboardInstance, plan: DispatchWorkerUnitPlan, granted: Kibibyte, process_bounds: UnitProcessBounds) -> HostExecArgv { - host_exec_argv( - program: plan.systemd_run_program as String, - args: systemd_run_user_transient_arguments( - unit: plan.unit, - properties: dispatch_worker_unit_properties_for_plan(instance: instance, plan: plan, granted: granted, process_bounds: process_bounds), - setenv_bindings: [], - command_argv: plan.inner_argv, - ), +fn dispatch_worker_unit_command(instance: HostDashboardInstance, plan: DispatchWorkerUnitPlan, granted: Kibibyte, process_bounds: UnitProcessBounds) -> SystemdRunCommandReading { + systemd_run_user_transient_command( + unit: plan.unit, + properties: dispatch_worker_unit_properties_for_plan(instance: instance, plan: plan, granted: granted, process_bounds: process_bounds), + setenv_bindings: [], + command_argv: plan.inner_argv, ) } diff --git a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag index af392ec921d..7da72a4f754 100644 --- a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag +++ b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag @@ -68,7 +68,10 @@ import extdeps.tools.e2fsprogs { decode_dumpe2fs_superblock, Ext2SuperblockRead, Ext2SuperblockUnreadable, ext2_superblock_magic, } import extdeps.systemd.journalctl -import extdeps.systemd.systemd_run { systemd_run_property_argv, SystemdRunProperty } +import extdeps.exec.command { argv_words } +import extdeps.systemd.systemd_run { + SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command, +} import gunbc.runner_microvm_lifecycle { CgroupSubtreeReadback, CgroupSubtreeEmpty, CgroupSubtreePopulated, CgroupSubtreeReadFailed, ResourceReadback, ResourceAbsent, ResourcePresent, ResourceUnreadable, @@ -1126,22 +1129,29 @@ fn launch_attempt(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, plan: Att } } +// The projection refuses a word the wire cannot carry; there is no started unit to observe, so the +// refusal takes the same failure arm a failed start takes, carrying the refusal's reason. fn run_jailer_in_cell(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, receipt: AttemptStagingReceipt) -> AttemptLaunch { let unit = runner_microvm_attempt_unit_name(attempt: attempt) - let started = systemd.SystemdRun.RunTransientRetained( + match systemd_run_transient_unit_command( unit: unit, - property_argv: systemd_run_property_argv(properties: [ + properties: [ runner_microvm_attempt_slice_property(attempt: attempt), runner_microvm_attempt_part_of_property(attempt: attempt), attempt_invocation_env_setting(invocation_id: invocation_id), - ]), + ], command_argv: concat([receipt.jailer.program.invocation as String], receipt.jailer.arguments), - ) - if started.success { - VmmStarted { unit: unit, receipt: receipt } - } else { - VmmStartFailed { unit: unit, detail: trim(s: started.stderr) } + ) { + SystemdRunCommandRefused { reason: why } => VmmStartFailed { unit: unit, detail: why } + SystemdRunCommandReady { command: command } => { + let started = systemd.SystemdRun.RunTransientRetained(command_argv: argv_words(command: command)) + if started.success { + VmmStarted { unit: unit, receipt: receipt } + } else { + VmmStartFailed { unit: unit, detail: trim(s: started.stderr) } + } + } } } diff --git a/dag/gunbc/runner/runner_throughput_qualification_route.dag b/dag/gunbc/runner/runner_throughput_qualification_route.dag index 916d8ec12e0..1c5b18222e5 100644 --- a/dag/gunbc/runner/runner_throughput_qualification_route.dag +++ b/dag/gunbc/runner/runner_throughput_qualification_route.dag @@ -7,7 +7,7 @@ import std.decl_ref { DeclarationRef, decl_ref } import product.placement_supply { HostIdentity } import gunbc.runner.runner_host_filtered_egress { EgressPolicyShape, runner_host_egress_policy } import extdeps.systemd { MemoryMax, MemoryHigh, MemorySwapMax, TasksMax, CPUWeight, CPUQuota } -import extdeps.systemd.systemd_run { SystemdRunProperty, systemd_run_transient_wait_unit_argv } +import extdeps.systemd.systemd_run { SystemdRunProperty, SystemdRunCommandReading, systemd_run_transient_wait_unit_command } import gunbc.fleet_intent_network { operator_host_mtcollins1 } import gunbc.runner_slot_allocation { runner_host_label, runner_label_set_union } import gunbc.runner_slot_desired { RunnerSlotDesired, RunnerSlotCpuQuota, CpuQuotaResolved, SlotCpuQuotaUnbounded, gunbc_runner_slot_desired } @@ -118,8 +118,8 @@ fn ephemeral_slot_properties(desired: RunnerSlotDesired) -> List) -> List { - systemd_run_transient_wait_unit_argv(unit: unit, properties: ephemeral_slot_properties(desired: desired), command_argv: command_argv) +fn ephemeral_slot_command(unit: NonEmptyStr, desired: RunnerSlotDesired, command_argv: List) -> SystemdRunCommandReading { + systemd_run_transient_wait_unit_command(unit: unit, properties: ephemeral_slot_properties(desired: desired), command_argv: command_argv) } // THE ATTEMPT LABEL IS IN runs-on, AND THAT IS THE WHOLE BINDING (gunbc.runner.runner_qualification_receipt diff --git a/dag/gunbc/systemd_run_transient.dag b/dag/gunbc/systemd_run_transient.dag index 25b6b8ebadd..7f10dcd318f 100644 --- a/dag/gunbc/systemd_run_transient.dag +++ b/dag/gunbc/systemd_run_transient.dag @@ -1,13 +1,13 @@ module gunbc.systemd_run_transient import std.types { String, NonEmptyStr, List, Bool, Int } -import extdeps.systemd.systemd_run +import extdeps.systemd.systemd_run { systemd_run_command_argument_words } +import extdeps.exec.command { ArgvCommand, argv_words } import gunbc.host_effect { HostEffectTransport, LocalShell, SshShell, EmitArtifactThenThinRun } import v2.std.operation_argv { ArgvMaterialization, ArgvMaterialized, ArgvMaterializationRefused, - operation_argv_bind_text, operation_argv_bind_text_list, operation_argv_cause_label, } @@ -34,29 +34,26 @@ type SystemdRunTransientWaitOutcome = SystemdRunTransientWaitCompleted { exit_code: Int, stdout: String, stderr: String } | SystemdRunTransientWaitTransportRefused { reason: String } -fn systemd_run_transient_operation_argv(unit: NonEmptyStr, properties: List, command_argv: List) -> ArgvMaterialization { +// THE BRIDGE CARRIES THE TYPED COMMAND, NOT ITS INGREDIENTS. The unit name and the properties used +// to arrive here as separate parameters and were re-rendered through systemd_run_property_argv — +// a second spelling of the same words, one projection downstream of the authority. They now arrive +// inside an ArgvCommand minted by the typed builders (systemd_run_transient_unit_command and +// siblings), and the bridge only turns that one projection into the words each transport runs. +fn systemd_run_transient_operation_argv(command: ArgvCommand) -> ArgvMaterialization { shell_materialize_operation_argv( systemd_run_transient_path, systemd_run_transient_service, systemd_run_transient_operation, - [ - operation_argv_bind_text(name: "unit", text: unit as String), - operation_argv_bind_text_list(name: "property_argv", items: systemd_run_property_argv(properties: properties)), - operation_argv_bind_text_list(name: "command_argv", items: command_argv), - ] + [operation_argv_bind_text_list(name: "command_argv", items: systemd_run_command_argument_words(command: command))] ) } -fn systemd_run_transient_wait_operation_argv(unit: NonEmptyStr, properties: List, command_argv: List) -> ArgvMaterialization { +fn systemd_run_transient_wait_operation_argv(command: ArgvCommand) -> ArgvMaterialization { shell_materialize_operation_argv( systemd_run_transient_path, systemd_run_transient_service, systemd_run_transient_wait_operation, - [ - operation_argv_bind_text(name: "unit", text: unit as String), - operation_argv_bind_text_list(name: "property_argv", items: systemd_run_property_argv(properties: properties)), - operation_argv_bind_text_list(name: "command_argv", items: command_argv), - ] + [operation_argv_bind_text_list(name: "command_argv", items: systemd_run_command_argument_words(command: command))] ) } @@ -68,8 +65,8 @@ fn systemd_run_transient_outcome_from_result(success: Bool, stdout: String, stde } } -fn systemd_run_transient_local(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunTransientOutcome { - let result = systemd.SystemdRun.RunTransient(unit: unit, property_argv: systemd_run_property_argv(properties: properties), command_argv: command_argv) +fn systemd_run_transient_local(command: ArgvCommand) -> SystemdRunTransientOutcome { + let result = systemd.SystemdRun.RunTransient(command_argv: argv_words(command: command)) systemd_run_transient_outcome_from_result(success: result.success, stdout: result.stdout, stderr: result.stderr) } @@ -88,9 +85,9 @@ fn systemd_run_transient_ssh_argv(host: NonEmptyStr, argv: List) -> Syst } } -fn systemd_run_transient_ssh(host: NonEmptyStr, unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunTransientOutcome { - match systemd_run_transient_operation_argv(unit: unit, properties: properties, command_argv: command_argv) { - ArgvMaterialized { argv: argv } => systemd_run_transient_ssh_argv(host: host, argv: argv) +fn systemd_run_transient_ssh(host: NonEmptyStr, command: ArgvCommand) -> SystemdRunTransientOutcome { + match systemd_run_transient_operation_argv(command: command) { + ArgvMaterialized { argv: materialized } => systemd_run_transient_ssh_argv(host: host, argv: materialized) ArgvMaterializationRefused { at: _, cause: c } => SystemdRunTransientRefused { reason: operation_argv_cause_label(cause: c) } } @@ -99,14 +96,12 @@ fn systemd_run_transient_ssh(host: NonEmptyStr, unit: NonEmptyStr, properties: L fn systemd_run_transient_fleet_ssh( target: SshTarget, context: FleetSshExecutionContext, - unit: NonEmptyStr, - properties: List, - command_argv: List, + command: ArgvCommand, ) -> SystemdRunTransientOutcome { - match systemd_run_transient_operation_argv(unit: unit, properties: properties, command_argv: command_argv) { - ArgvMaterialized { argv: argv } => + match systemd_run_transient_operation_argv(command: command) { + ArgvMaterialized { argv: materialized } => systemd_run_transient_outcome_from_exec( - outcome: typed_argv_exec_over_fleet_ssh(target: target, context: context, argv: argv), + outcome: typed_argv_exec_over_fleet_ssh(target: target, context: context, argv: materialized), ) ArgvMaterializationRefused { at: _, cause: c } => SystemdRunTransientRefused { reason: operation_argv_cause_label(cause: c) } @@ -115,14 +110,12 @@ fn systemd_run_transient_fleet_ssh( fn systemd_run_transient_read( transport: HostEffectTransport, - unit: NonEmptyStr, - properties: List, - command_argv: List, + command: ArgvCommand, ) -> SystemdRunTransientOutcome { match transport { - LocalShell => systemd_run_transient_local(unit: unit, properties: properties, command_argv: command_argv) - SshShell { ssh_host: h } => systemd_run_transient_ssh(host: h, unit: unit, properties: properties, command_argv: command_argv) - FleetSsh { target: t, context: c } => systemd_run_transient_fleet_ssh(target: t, context: c, unit: unit, properties: properties, command_argv: command_argv) + LocalShell => systemd_run_transient_local(command: command) + SshShell { ssh_host: h } => systemd_run_transient_ssh(host: h, command: command) + FleetSsh { target: t, context: c } => systemd_run_transient_fleet_ssh(target: t, context: c, command: command) EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => SystemdRunTransientRefused { reason: "systemd-run transient start refused EmitArtifactThenThinRun transport", @@ -130,16 +123,8 @@ fn systemd_run_transient_read( } } -fn systemd_run_transient_wait_local( - unit: NonEmptyStr, - properties: List, - command_argv: List, -) -> SystemdRunTransientWaitOutcome { - let result = systemd.SystemdRun.RunTransientAndWait( - unit: unit, - property_argv: systemd_run_property_argv(properties: properties), - command_argv: command_argv, - ) +fn systemd_run_transient_wait_local(command: ArgvCommand) -> SystemdRunTransientWaitOutcome { + let result = systemd.SystemdRun.RunTransientAndWait(command_argv: argv_words(command: command)) systemd_run_transient_wait_outcome_from_result( exit_code: result.exit_code, stdout: result.stdout, @@ -188,16 +173,10 @@ fn systemd_run_transient_wait_ssh_argv( fn systemd_run_transient_wait_ssh( host: NonEmptyStr, - unit: NonEmptyStr, - properties: List, - command_argv: List, + command: ArgvCommand, ) -> SystemdRunTransientWaitOutcome { - match systemd_run_transient_wait_operation_argv( - unit: unit, - properties: properties, - command_argv: command_argv, - ) { - ArgvMaterialized { argv: argv } => systemd_run_transient_wait_ssh_argv(host: host, argv: argv) + match systemd_run_transient_wait_operation_argv(command: command) { + ArgvMaterialized { argv: materialized } => systemd_run_transient_wait_ssh_argv(host: host, argv: materialized) ArgvMaterializationRefused { at: _, cause: cause } => SystemdRunTransientWaitTransportRefused { reason: operation_argv_cause_label(cause: cause) } } @@ -210,70 +189,49 @@ fn systemd_run_transient_wait_ssh( // integer from this result. fn systemd_run_transient_wait_read( transport: HostEffectTransport, - unit: NonEmptyStr, - properties: List, - command_argv: List, + command: ArgvCommand, ) -> SystemdRunTransientWaitOutcome { match transport { - LocalShell => systemd_run_transient_wait_local( - unit: unit, - properties: properties, - command_argv: command_argv, - ) - SshShell { ssh_host: host } => systemd_run_transient_wait_ssh( - host: host, - unit: unit, - properties: properties, - command_argv: command_argv, - ) - FleetSsh { target: _, context: _ } => SystemdRunTransientWaitTransportRefused { - reason: "systemd-run transient wait refused FleetSsh until exact remote status is distinguished from transport status", - } - EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => SystemdRunTransientWaitTransportRefused { - reason: "systemd-run transient wait refused EmitArtifactThenThinRun transport", - } + LocalShell => systemd_run_transient_wait_local(command: command) + SshShell { ssh_host: h } => systemd_run_transient_wait_ssh(host: h, command: command) + FleetSsh { target: t, context: c } => + systemd_run_transient_wait_outcome_from_exec( + outcome: typed_argv_exec_over_fleet_ssh(target: t, context: c, argv: argv_words(command: command)), + ) + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => + SystemdRunTransientWaitTransportRefused { + reason: "systemd-run transient wait refused EmitArtifactThenThinRun transport", + } } } -// ARGV EQUALITY IS A QUESTION ABOUT TOKENS, AND join() ANSWERS A QUESTION ABOUT TEXT. -// The prior spelling compared join(materialized, " ") == join(authority, " "), which cannot tell -// ["--property=WorkingDirectory=/path with space"] from ["...=/path", "with", "space"]: one word -// carrying a space and three words collapse to the same string, so an accidental token split at -// exactly the seam that carries paths and property values was invisible. There is no zip or index -// in the list vocabulary to fold two lists in lockstep, so equality is established the way an -// encoding establishes it: cardinality must agree, and the words must be joined on a separator no -// word contains, which makes the joined form injective. A word containing the separator REFUSES -// rather than falling back to the ambiguous comparison -- an unusable encoding is an unanswerable -// question, not a passing one. -data argv_token_separator: String = "\n" - -fn argv_word_free_of_separator(words: List) -> Bool { - fold(words, init: true, f: (acc, w) => acc && !string_contains(s: w, pattern: argv_token_separator)) -} - +// Token comparison over words: exact and order-sensitive, and REFUSING any word that carries the +// token separator -- the comparison is injective only while no word contains it, and a word that +// does must refuse rather than compare (the split-words control pins that). fn argv_exact_token_equal(left: List, right: List) -> Bool { - list_length(items: left) == list_length(items: right) - && argv_word_free_of_separator(words: left) - && argv_word_free_of_separator(words: right) - && join(left, argv_token_separator) == join(right, argv_token_separator) + if any(left, w => string_contains(s: w, pattern: "\n")) || any(right, w => string_contains(s: w, pattern: "\n")) { + false + } else { + join(left, separator: "\n") == join(right, separator: "\n") + } } -// THE MATERIALIZED ARGV IS CHECKED AGAINST THE EXTDEPS AUTHORITY, not against a second hand-built -// copy of it. An earlier spelling rebuilt the launcher words here, so this predicate could only -// confirm that two local folds agreed; a property population was invisible to it. It now compares -// against systemd_run_transient_unit_argv, which is the same function the transport template must -// agree with, so a property that fails to reach the executed words goes red here. -fn systemd_run_transient_operation_argv_matches_authority(unit: NonEmptyStr, properties: List, command_argv: List) -> Bool { - let authority = systemd_run_transient_unit_argv(unit: unit, properties: properties, command_argv: command_argv) - match systemd_run_transient_operation_argv(unit: unit, properties: properties, command_argv: command_argv) { +// The transport template is declared, not re-derived, so this predicate now compares the +// materialized words against the words the caller's typed command projects: if the operation's +// declared transport and the authority's projection ever disagree about word order or content, it +// goes red here — the property population the old two-folds-agree check could not see is exactly +// what this join sees, because both sides now come from the one projection. +fn systemd_run_transient_operation_argv_matches_authority(command: ArgvCommand) -> Bool { + let authority = argv_words(command: command) + match systemd_run_transient_operation_argv(command: command) { ArgvMaterialized { argv: materialized } => argv_exact_token_equal(left: materialized, right: authority) ArgvMaterializationRefused { at: _, cause: _ } => false } } -fn systemd_run_transient_wait_operation_argv_matches_authority(unit: NonEmptyStr, properties: List, command_argv: List) -> Bool { - let authority = systemd_run_transient_wait_unit_argv(unit: unit, properties: properties, command_argv: command_argv) - match systemd_run_transient_wait_operation_argv(unit: unit, properties: properties, command_argv: command_argv) { +fn systemd_run_transient_wait_operation_argv_matches_authority(command: ArgvCommand) -> Bool { + let authority = argv_words(command: command) + match systemd_run_transient_wait_operation_argv(command: command) { ArgvMaterialized { argv: materialized } => argv_exact_token_equal(left: materialized, right: authority) ArgvMaterializationRefused { at: _, cause: _ } => false } diff --git a/dag/test/claim/compute/work_class_grant_witness_test.dag b/dag/test/claim/compute/work_class_grant_witness_test.dag index fceba08a4ad..5fb0af62fe1 100644 --- a/dag/test/claim/compute/work_class_grant_witness_test.dag +++ b/dag/test/claim/compute/work_class_grant_witness_test.dag @@ -2,8 +2,11 @@ module test.claim.compute.work_class_grant_witness_test import std.types { String, Bool, Int, List, NonEmptyStr, EpochSecs } import std.measure { Kibibyte, kibibyte, kibibyte_count, byte_size_count } +import extdeps.exec.command { argv_words } import extdeps.systemd.systemd_run { - SystemdRunWaitSummary, systemd_run_wait_summary, systemd_run_user_wait_arguments, systemd_run_transient_wait_unit_argv, + SystemdRunWaitSummary, systemd_run_wait_summary, + SystemdRunCommandReady, SystemdRunCommandRefused, + systemd_run_user_wait_command, systemd_run_transient_wait_unit_command, SystemdServiceResultReading, ServiceResultRead, ServiceResultUnrecognized, ServiceResultNotReported, SystemdServiceResult, ServiceResultOomKill, ServiceResultSuccess, ServiceResultExitCode, SystemdMemoryPeakReading, MemoryPeakRead, MemoryPeakUnparseable, MemoryPeakNotReported, @@ -41,7 +44,29 @@ import gunbc.compute.work_provider_local { // regression control. Supplying the launcher here is DESIGN 3's supplied input at one interface; // the real builder is exercised by the provider's own route (gunbc.compute.work_provider_local // compute_run_unit). -data fixture_launcher: SystemdSummaryPrintingWait = systemd_run_user_wait_arguments(unit: "gunbc-fixture", properties: [], setenv_bindings: [], command_argv: ["true"]) +// THE FIXTURE GOES THROUGH THE TYPED BUILDER, and its refused arm -- uninhabited for these inputs -- +// carries the summary a never-ran invocation legitimately has: NotReported, not a minted token. +// (The admit list on argv_command refuses test callers, so there is no direct mint to fall back on; +// the fold is the only route from the reading to a summary.) +fn fixture_summary(standing: SummaryFormatStanding, stderr: String) -> SystemdRunWaitSummary { + match systemd_run_user_wait_command(unit: "gunbc-fixture", properties: [], setenv_bindings: [], command_argv: ["true"]) { + SystemdRunCommandRefused { reason: _ } => + SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } } + SystemdRunCommandReady { command: command } => + systemd_run_wait_summary(launcher: SystemdSummaryPrintingWait { command: command }, standing: standing, stderr: stderr) + } +} + +// POSITIVE CONTROL FOR THE USER-WAIT CUTOVER: the typed builder's projected words are the words the +// string-built form this replaces rendered for the same arguments -- wait flags first, then the +// user-manager start, then the command after --. +test fn the_user_wait_builder_projects_the_words_the_string_form_rendered() -> Bool { + match systemd_run_user_wait_command(unit: "gunbc-fixture", properties: [], setenv_bindings: [], command_argv: ["true"]) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => + argv_words(command: command) == ["systemd-run", "--wait", "--pipe", "--collect", "--user", "--unit=gunbc-fixture", "--", "true"] + } +} // THE FIXTURES ALSO NAME THE MANAGER THEY ARE PRETENDING CAME FROM, because the summary format is // version-keyed and the reader refuses a major it has not read. Grounded here so the claims below @@ -59,7 +84,7 @@ data nine_point_seven_gib_upper_bytes: Int = 10630044058 data nine_point_seven_gib_lower_bytes: Int = 10415295692 test fn the_units_peak_and_result_are_the_summarys_last_lines_not_the_commands() -> Bool { - let s = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: summary_oom_after_spoof) + let s = fixture_summary(standing: grounded_standing, stderr: summary_oom_after_spoof) compute_summary_is_oom_kill(summary: s) && match s.memory_peak { MemoryPeakRead { peak: b } => @@ -80,9 +105,9 @@ test fn the_units_peak_and_result_are_the_summarys_last_lines_not_the_commands() // with --quiet. Either must read NotReported, because a recorded zero would size a class at its // headroom alone. An exact small figure keeps its exact reading. test fn an_absent_peak_is_not_reported_and_a_byte_figure_is_exact() -> Bool { - let absent = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "Finished with result: exit-code\nMain processes terminated with: code=exited/status=1\n") - let small = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "Finished with result: success\nMemory peak: 512B\n") - let garbled = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "Finished with result: frobnicated\nMemory peak: lots\n") + let absent = fixture_summary(standing: grounded_standing, stderr: "Finished with result: exit-code\nMain processes terminated with: code=exited/status=1\n") + let small = fixture_summary(standing: grounded_standing, stderr: "Finished with result: success\nMemory peak: 512B\n") + let garbled = fixture_summary(standing: grounded_standing, stderr: "Finished with result: frobnicated\nMemory peak: lots\n") !compute_summary_is_oom_kill(summary: absent) && match small.memory_peak { MemoryPeakRead { peak: b } => match b { FormattedBytesExact { bytes } => byte_size_count(b: bytes) == 512 _ => false } @@ -152,8 +177,8 @@ test fn with_the_shipped_rows_every_class_reserves_the_ceiling() -> Bool { test fn only_an_oom_kill_below_the_ceiling_retries_at_the_ceiling() -> Bool { let below = GrantSized { class: WorkClassCompile, amount: kibibyte(count: 11 * gib), samples: 3, peak_upper: kibibyte(count: 9 * gib), headroom: kibibyte(count: 2 * gib), headroom_authority: "fixture" as NonEmptyStr } let at = GrantAtCeiling { class: WorkClassCompile, amount: ceiling(), reason: ClassUnmeasured } - let oom = ComputeAttempt { outcome: WorkFailed { identity: "i", exit_code: 1, log_path: "" }, summary: systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: summary_oom_after_spoof), gate: AttemptGateRefused { detail: "fixture: no attempt slot" } } - let clean = ComputeAttempt { outcome: WorkFailed { identity: "i", exit_code: 1, log_path: "" }, summary: systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "Finished with result: exit-code\nMemory peak: 3.1G\n"), gate: AttemptGateRefused { detail: "fixture: no attempt slot" } } + let oom = ComputeAttempt { outcome: WorkFailed { identity: "i", exit_code: 1, log_path: "" }, summary: fixture_summary(standing: grounded_standing, stderr: summary_oom_after_spoof), gate: AttemptGateRefused { detail: "fixture: no attempt slot" } } + let clean = ComputeAttempt { outcome: WorkFailed { identity: "i", exit_code: 1, log_path: "" }, summary: fixture_summary(standing: grounded_standing, stderr: "Finished with result: exit-code\nMemory peak: 3.1G\n"), gate: AttemptGateRefused { detail: "fixture: no attempt slot" } } compute_attempt_retries_at_ceiling(grant: below, attempt: oom) && !compute_attempt_retries_at_ceiling(grant: at, attempt: oom) && !compute_attempt_retries_at_ceiling(grant: below, attempt: clean) @@ -166,9 +191,9 @@ test fn only_an_oom_kill_below_the_ceiling_retries_at_the_ceiling() -> Bool { // No peak line, or no clock, takes no sample. test fn a_summary_becomes_a_typed_sample_at_its_upper_bound_or_no_sample() -> Bool { let op = CompileEntry { entry: "src/v2/compiler/compile.dag" } - let s = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: summary_oom_after_spoof) + let s = fixture_summary(standing: grounded_standing, stderr: summary_oom_after_spoof) let taken = work_class_peak_sample(summary: s, work_identity: "abc", op: op, host: "srv1", observed_at: Present { value: 1790000000 }) - let no_peak = work_class_peak_sample(summary: systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "Finished with result: success\n"), work_identity: "abc", op: op, host: "srv1", observed_at: Present { value: 1790000000 }) + let no_peak = work_class_peak_sample(summary: fixture_summary(standing: grounded_standing, stderr: "Finished with result: success\n"), work_identity: "abc", op: op, host: "srv1", observed_at: Present { value: 1790000000 }) let no_clock = work_class_peak_sample(summary: s, work_identity: "abc", op: op, host: "srv1", observed_at: none) match taken { PeakSampleTaken { class, sample } => @@ -233,9 +258,9 @@ data four_gib_lower_bytes: Int = 4294967296 data four_gib_upper_bytes: Int = 4509715661 test fn the_peak_is_unreported_by_absence_and_an_untrimmed_summary_still_reads() -> Bool { - let no_peak_line = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "Finished with result: exit-code\nMain processes terminated with: code=exited/status=1\n") - let unit_output_only = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "error: linking with `cc` failed\nrustc exited 1\n") - let trimmed = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "Finished with result: success\nMemory peak: 4.0G") + let no_peak_line = fixture_summary(standing: grounded_standing, stderr: "Finished with result: exit-code\nMain processes terminated with: code=exited/status=1\n") + let unit_output_only = fixture_summary(standing: grounded_standing, stderr: "error: linking with `cc` failed\nrustc exited 1\n") + let trimmed = fixture_summary(standing: grounded_standing, stderr: "Finished with result: success\nMemory peak: 4.0G") match no_peak_line.memory_peak { MemoryPeakNotReported { absence } => match absence { SummaryLineAbsent => true _ => false } _ => false } && match unit_output_only.memory_peak { MemoryPeakNotReported { absence } => match absence { SummaryLineAbsent => true _ => false } _ => false } && match unit_output_only.result { ServiceResultNotReported { absence } => match absence { SummaryLineAbsent => true _ => false } _ => false } @@ -267,10 +292,20 @@ test fn the_peak_is_unreported_by_absence_and_an_untrimmed_summary_still_reads() // fact a Bool claim can carry. What a claim CAN carry is the fold: a token whose argv does contain // --quiet, constructed here directly because no builder will mint one, reports NotReported rather // than the peak sitting in the fixture. Deleting the check in systemd_run_wait_summary reds this. -data quiet_launcher: SystemdSummaryPrintingWait = SystemdSummaryPrintingWait { argv: systemd_run_transient_wait_unit_argv(unit: "gunbc-quiet", properties: [], command_argv: ["true"]) } +// THE QUIET LAUNCHER'S READING GOES THROUGH THE TYPED BUILDER that carries --quiet; the claim's +// refused arm is uninhabited for these inputs and carries NotReported, never a minted token (the +// admit list on argv_command refuses test callers, so there is no direct mint to fall back on). +fn quiet_summary(stderr: String) -> SystemdRunWaitSummary { + match systemd_run_transient_wait_unit_command(unit: "gunbc-quiet", properties: [], command_argv: ["true"]) { + SystemdRunCommandRefused { reason: _ } => + SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } } + SystemdRunCommandReady { command: command } => + systemd_run_wait_summary(launcher: SystemdSummaryPrintingWait { command: command }, standing: grounded_standing, stderr: stderr) + } +} test fn a_quiet_launcher_reports_no_peak_rather_than_the_one_in_the_text() -> Bool { - let s = systemd_run_wait_summary(launcher: quiet_launcher, standing: grounded_standing, stderr: summary_oom_after_spoof) + let s = quiet_summary(stderr: summary_oom_after_spoof) match s.memory_peak { MemoryPeakNotReported { absence } => match absence { SummarySuppressedByQuietLauncher => true _ => false } _ => false } && match s.result { ServiceResultNotReported { absence } => match absence { SummarySuppressedByQuietLauncher => true _ => false } _ => false } && !compute_summary_is_oom_kill(summary: s) @@ -294,7 +329,7 @@ test fn a_quiet_launcher_reports_no_peak_rather_than_the_one_in_the_text() -> Bo // SuppressedByQuietLauncher below. A collapse to any one cause now reds exactly the claims for the // OTHER causes, which is what makes the red legible. test fn an_empty_capture_is_reported_as_capture_empty_not_as_a_missing_line() -> Bool { - let empty = systemd_run_wait_summary(launcher: fixture_launcher, standing: grounded_standing, stderr: "") + let empty = fixture_summary(standing: grounded_standing, stderr: "") match empty.memory_peak { MemoryPeakNotReported { absence } => match absence { SummaryCaptureEmpty => true _ => false } _ => false } && match empty.result { ServiceResultNotReported { absence } => match absence { SummaryCaptureEmpty => true _ => false } _ => false } && !compute_summary_is_oom_kill(summary: empty) @@ -331,7 +366,7 @@ test fn a_grounded_major_is_the_only_one_that_parses() -> Bool { // consumer actually reads goes red, because the join is. test fn an_unread_major_refuses_instead_of_reading_its_figures() -> Bool { let newer = summary_format_standing(version_line: "systemd 257 (257.1-1)") - let s = systemd_run_wait_summary(launcher: fixture_launcher, standing: newer, stderr: summary_oom_after_spoof) + let s = fixture_summary(standing: newer, stderr: summary_oom_after_spoof) match newer { SummaryFormatUngroundedMajor { major } => major == 257 _ => false } && match s.memory_peak { MemoryPeakFormatNotGrounded { standing: _ } => true _ => false } && match s.result { ServiceResultFormatNotGrounded { standing: _ } => true _ => false } @@ -356,7 +391,7 @@ test fn an_unread_major_refuses_instead_of_reading_its_figures() -> Bool { // annotation cannot fully repair, because the annotation is read AFTER the decision to touch it. test fn the_grounding_recursion_terminates_because_an_unreadable_version_refuses() -> Bool { let unknown = summary_format_standing(version_line: "") - let s = systemd_run_wait_summary(launcher: fixture_launcher, standing: unknown, stderr: summary_oom_after_spoof) + let s = fixture_summary(standing: unknown, stderr: summary_oom_after_spoof) match unknown { SummaryFormatVersionUnreadable { line } => line == "" _ => false } && match s.memory_peak { MemoryPeakFormatNotGrounded { standing: _ } => true _ => false } && !compute_summary_is_oom_kill(summary: s) @@ -377,7 +412,7 @@ data ending_op: WorkOperation = CompileEntry { entry: "e" } fn outcome_for(stderr: String, standing: SummaryFormatStanding) -> WorkOutcome { compute_failed_unit_outcome( - summary: systemd_run_wait_summary(launcher: fixture_launcher, standing: standing, stderr: stderr), + summary: fixture_summary(standing: standing, stderr: stderr), identity: "abc", exit_code: 1, op: ending_op, granted: kibibyte(count: 1024), grant_was_ceiling: false, log_path: "/l", ) } diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 4bdcd70830b..9750325fd4c 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -2,7 +2,14 @@ module test.claim.fabric.systemd_run_transient_wait_witness_test import std.types { Bool, List } import extdeps.systemd { WorkingDirectoryProperty, MemoryMax } -import extdeps.systemd.systemd_run { SystemdRunProperty, systemd_run_transient_wait_unit_argv } +import extdeps.exec.command { argv_words } +import extdeps.systemd.systemd_run { + SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, + SystemdRunOptionWordsProjected, SystemdRunOptionWordsRefused, + SystemdSetenvBinding, + RunUserManager, RunScope, Wait, Quiet, Pipe, Collect, EndOfOptions, SetEnv, + systemd_run_option_words, systemd_run_transient_wait_unit_command, systemd_run_user_wait_command, +} import gunbc.systemd_run_transient { systemd_run_transient_wait_operation_argv_matches_authority, systemd_run_transient_wait_outcome_from_result, @@ -16,25 +23,101 @@ data wait_properties: List = [ SystemdRunProperty { property: MemoryMax, value: "17179869184" }, ] -test fn systemd_wait_route_owns_wait_quiet_collect_and_preserves_tokens() -> Bool { - let argv = systemd_run_transient_wait_unit_argv( - unit: "fci1-bounded-driver.service", - properties: wait_properties, - command_argv: ["/bin/sh", "-c", "exit 86"], - ) - systemd_run_transient_wait_operation_argv_matches_authority( +// THE POSITIVE CONTROL FOR THE TYPED CUTOVER: the words the typed option model projects are +// byte-identical to the words the string-built builder this replaces rendered for the same +// arguments, including the space inside the working directory's value, and the operation's +// materialized transport words are the same words the typed command projects. +fn wait_control_command_reading() -> SystemdRunCommandReading { + systemd_run_transient_wait_unit_command( unit: "fci1-bounded-driver.service", properties: wait_properties, command_argv: ["/bin/sh", "-c", "exit 86"], ) - && argv_exact_token_equal( - left: argv, - right: [ - "systemd-run", "--unit=fci1-bounded-driver.service", "--wait", "--quiet", "--collect", - "--property=WorkingDirectory=/tmp/path with space", "--property=MemoryMax=17179869184", - "--", "/bin/sh", "-c", "exit 86", - ], - ) +} + +// THE POSITIVE CONTROL, WORDS HALF: the projection's words are the old builder's words, byte for byte. +test fn systemd_wait_route_projects_the_old_words() -> Bool { + match wait_control_command_reading() { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => + argv_exact_token_equal( + left: argv_words(command: command), + right: [ + "systemd-run", "--unit=fci1-bounded-driver.service", "--wait", "--quiet", "--collect", + "--property=WorkingDirectory=/tmp/path with space", "--property=MemoryMax=17179869184", + "--", "/bin/sh", "-c", "exit 86", + ], + ) + } +} + +// THE POSITIVE CONTROL, MATERIALIZATION HALF: the operation's declared transport materializes the +// same words the typed command projects. +test fn systemd_wait_route_materializes_the_projected_words() -> Bool { + match wait_control_command_reading() { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => + systemd_run_transient_wait_operation_argv_matches_authority(command: command) + } +} + +// THE RED WITNESS: a property value carrying a newline is REFUSED at the projection -- not trimmed, +// not split into two words, not passed through. The operation that would have run does not exist, +// and the reason names the wire fact the value violates. +test fn a_property_value_the_wire_cannot_carry_is_refused() -> Bool { + match systemd_run_transient_wait_unit_command( + unit: "fci1-refused.service", + properties: [SystemdRunProperty { property: WorkingDirectoryProperty, value: "/tmp/a\nb" as NonEmptyStr }], + command_argv: ["/bin/true"], + ) { + SystemdRunCommandRefused { reason: why } => string_contains(s: why, pattern: "one line") + SystemdRunCommandReady { command: _ } => false + } +} + +// A unit name is one line and never carries a slash (systemd.unit(5)); the projection refuses both. +test fn a_unit_name_the_wire_cannot_carry_is_refused() -> Bool { + match systemd_run_transient_wait_unit_command( + unit: "wrong/name" as NonEmptyStr, + properties: [], + command_argv: ["/bin/true"], + ) { + SystemdRunCommandRefused { reason: why } => string_contains(s: why, pattern: "never contains /") + SystemdRunCommandReady { command: _ } => false + } +} + +// --setenv= takes NAME=VALUE, so the NAME side carries no = of its own: a binding that would render +// an ambiguous word is refused, not passed to systemd-run to misparse. +test fn a_setenv_name_with_an_equals_is_refused() -> Bool { + match systemd_run_user_wait_command( + unit: "fci1-setenv.service", + properties: [], + setenv_bindings: [SystemdSetenvBinding { name: "A=b" as NonEmptyStr, value: "c" as NonEmptyStr }], + command_argv: ["/bin/true"], + ) { + SystemdRunCommandRefused { reason: why } => string_contains(s: why, pattern: "carries no = of its own") + SystemdRunCommandReady { command: _ } => false + } +} + +test fn a_setenv_binding_with_an_equals_in_the_name_is_refused() -> Bool { + match systemd_run_option_words(options: [SetEnv { binding: SystemdSetenvBinding { name: "A=b" as NonEmptyStr, value: "c" as NonEmptyStr } }]) { + SystemdRunOptionWordsRefused { reason: why } => string_contains(s: why, pattern: "carries no = of its own") + SystemdRunOptionWordsProjected { words: _ } => false + } +} + +// THE TYPED OPTION IS A SEALED SUM: there is no arm an unknown flag could ride. This fold walks the +// variants the module renders today and pins each one's man-page spelling at systemd 255; a variant +// added to the sum updates this fold or compilation fails, which is how an unknown option stays +// unwritable. +test fn every_option_variant_renders_its_man_page_spelling() -> Bool { + match systemd_run_option_words(options: [RunUserManager, RunScope, Wait, Quiet, Pipe, Collect, EndOfOptions]) { + SystemdRunOptionWordsRefused { reason: _ } => false + SystemdRunOptionWordsProjected { words: words } => + join(words, separator: " ") == "--user --scope --wait --quiet --pipe --collect --" + } } test fn systemd_wait_route_rejects_split_working_directory_token() -> Bool { @@ -54,3 +137,24 @@ test fn systemd_wait_normal_exit_86_remains_86() -> Bool { SystemdRunTransientWaitTransportRefused { reason: _ } => false } } + + +fn materialized_cardinality(reading: SystemdRunCommandReading) -> Int { + match reading { + SystemdRunCommandRefused { reason: _ } => -2 + SystemdRunCommandReady { command: command } => + match systemd_run_transient_wait_operation_argv(command: command) { + ArgvMaterialized { argv: words } => count(words) + ArgvMaterializationRefused { at: _, cause: _ } => -1 + } + } +} + +// CARDINALITY PROBES: exactly one of these passes, and which one names the materialized shape. +test fn probe_materialized_cardinality_11() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 11 } +test fn probe_materialized_cardinality_1() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 1 } +test fn probe_materialized_cardinality_10() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 10 } +test fn probe_materialized_cardinality_refused() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == -1 } +test fn probe_materialized_cardinality_command_refused() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == -2 } +test fn probe_materialized_cardinality_0() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 0 } + diff --git a/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag b/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag index ef6fbaebdce..8fd79336f74 100644 --- a/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag +++ b/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag @@ -5,6 +5,9 @@ import extdeps.filesystem.filesystem_io { filesystem_listing_observation, filesystem_file_observation, } +import extdeps.exec.command { argv_words } +import extdeps.systemd { MemoryMax, WorkingDirectoryProperty } +import extdeps.systemd.systemd_run { SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command } import gunbc.host_effect_nbd_proxy_serve { BmcwebSessionTokenObservation, BmcwebSessionTokenUsable, @@ -216,11 +219,14 @@ test fn nbd_proxy_observe_port_local_shell_is_active_active_wires_through() -> B } test fn witness_systemd_run_transient_operation_argv_matches_authority() -> Bool { - systemd_run_transient_operation_argv_matches_authority( + match systemd_run_transient_unit_command( unit: "nbd-proxy-srv3-nbd-proxy-10809" as NonEmptyStr, properties: [], command_argv: ["nbdkit", "-p", "10809", "file", "/var/lib/gunbc/artifacts/x.iso"], - ) + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => systemd_run_transient_operation_argv_matches_authority(command: command) + } } // THE EMPTY POPULATION ABOVE CANNOT SEE A PROPERTY THAT NEVER REACHES THE EXECUTED WORDS, which is @@ -229,11 +235,14 @@ test fn witness_systemd_run_transient_operation_argv_matches_authority() -> Bool // is launched unbounded. This control carries a real population so the materialized argv and the // extdeps authority must agree on the property word itself. test fn witness_systemd_run_transient_property_reaches_materialized_argv() -> Bool { - systemd_run_transient_operation_argv_matches_authority( + match systemd_run_transient_unit_command( unit: "nbd-proxy-srv3-nbd-proxy-10809" as NonEmptyStr, properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["nbdkit", "-p", "10809", "file", "/var/lib/gunbc/artifacts/x.iso"], - ) + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => systemd_run_transient_operation_argv_matches_authority(command: command) + } } // THE WHITESPACE ARM IS THE ONE THE OLD COMPARISON COULD NOT SEE. join(a," ")==join(b," ") cannot @@ -242,11 +251,14 @@ test fn witness_systemd_run_transient_property_reaches_materialized_argv() -> Bo // such a value here means the control fails if argv agreement is ever weakened back to a text // comparison: the words still join to the same text, and only cardinality separates them. test fn witness_systemd_run_property_value_with_space_keeps_token_identity() -> Bool { - systemd_run_transient_operation_argv_matches_authority( + match systemd_run_transient_unit_command( unit: "nbd-proxy-srv3-nbd-proxy-10809" as NonEmptyStr, properties: [SystemdRunProperty { property: WorkingDirectoryProperty, value: "/srv/path with space" as NonEmptyStr }], command_argv: ["nbdkit", "-p", "10809"], - ) + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => systemd_run_transient_operation_argv_matches_authority(command: command) + } } // THE ENCODING'S OWN PRECONDITION IS ASSERTED, not assumed. argv_exact_token_equal is injective @@ -261,13 +273,19 @@ test fn witness_argv_exact_token_equal_separates_split_words() -> Bool { // authority against one materialization; if the transport template silently dropped the property // words, BOTH sides would drop them together only if the authority also stopped rendering them -- // which this asserts it does not. The property word must be present in the authority's own output. +// POSITIVE CONTROL FOR THE CUTOVER: the wire name reaches the projected words untransformed, in +// position, in the non-wait start the nbd proxy's serve path actually runs. test fn witness_systemd_run_property_argv_renders_the_wire_name() -> Bool { - join( - systemd_run_property_argv( - properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], - ), - " ", - ) == "--property=MemoryMax=17179869184" + match systemd_run_transient_unit_command( + unit: "nbd-proxy-witness.service", + properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], + command_argv: ["/bin/true"], + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => + join(argv_words(command: command), " ") + == "systemd-run --unit=nbd-proxy-witness.service --property=MemoryMax=17179869184 --collect -- /bin/true" + } } test fn witness_systemctl_stop_operation_argv_matches_authority() -> Bool { diff --git a/dag/test/claim/roadmap/dispatch_worker_confinement_witness_test.dag b/dag/test/claim/roadmap/dispatch_worker_confinement_witness_test.dag index 00af84b6015..70241f2e6f0 100644 --- a/dag/test/claim/roadmap/dispatch_worker_confinement_witness_test.dag +++ b/dag/test/claim/roadmap/dispatch_worker_confinement_witness_test.dag @@ -2,13 +2,21 @@ module test.claim.roadmap.dispatch_worker_confinement_witness_test import std.types { Bool, String, List } import gunbc.roadmap_dispatch_actuator { dispatch_worker_unit_properties, attempt_state_prepare_argv_for_instance, dispatch_attempt_verification_dir_for_instance, attempt_unit_substate_has_exited, dispatch_review_unit_properties } +import extdeps.systemd { SystemdUnitProperty, systemd_unit_property_wire } +import extdeps.systemd.systemd_run { SystemdRunProperty } import gunbc.roadmap_model { RoadmapNodeId } import std.measure { kibibyte } import gunbc.roadmap_session_placement { session_process_bounds } import gunbc.roadmap_dashboard_instance { srv2_deploy_dashboard_instance } -fn has_property(props: List, needle: String) -> Bool { - any(props, p => string_contains(s: p, pattern: needle)) +// The claims are about WHICH SETTINGS the unit binds; rendering the typed properties back to their +// wire words here is a test observation, not a builder -- nothing hands these words to systemd-run. +fn property_words(props: List) -> List { + props |> map(p => concat(systemd_unit_property_wire(property: p.property) as String, concat("=", p.value as String))) +} + +fn has_property(props: List, needle: String) -> Bool { + any(property_words(props: props), p => string_contains(s: p, pattern: needle)) } test fn the_worker_unit_confines_writes_to_the_instance_root() -> Bool { diff --git a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag index efe4c838000..532eb3840e6 100644 --- a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag @@ -88,7 +88,7 @@ import gunbc.roadmap_dispatch_actuator { dispatch_brief_fields_for_environment, dispatch_brief_with_centering, dispatch_continuation_alignment_preamble, - dispatch_supervisor_unit_argv, dispatch_supervisor_unit_name, dispatch_supervisor_attempt_identity, + dispatch_supervisor_unit_command, dispatch_unit_exec_argv, dispatch_supervisor_unit_name, dispatch_supervisor_attempt_identity, DispatchSpawnCommands, HostExecArgv, SystemdUnitContainer, @@ -119,8 +119,10 @@ import gunbc.roadmap_dispatch_actuator { SpawnSession, dispatch_current_attempt_events_projection_argv_for_instance, gunbc_harness_review_argv, - dispatch_review_unit_argv, + dispatch_review_unit_command, } +import extdeps.exec.command { argv_words } +import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } import gunbc.dispatch_selection { dispatch_selection_standing_ineligible_reason, default_provider_selection_request, @@ -1159,14 +1161,14 @@ test fn witness_review_argv_carries_spawner_minted_attempt_identity() -> Bool { string_contains(s: join(argv, separator: "\0"), pattern: "\0--arg\0attempt_identity=reviewer:node-1-att-20260920-c") } -// THE SPAWN SITE, not just the argv row: dispatch_review_unit_argv is where the mint lives, and it -// is the fold the belt's review pass actually spawns through. This witness fails if a future edit -// re-drops the argument between the unit fold and the argv row, which compilation alone would not -// catch once the parameter exists and could be ignored. +// THE SPAWN SITE, not just the argv row: dispatch_review_unit_command is where the mint lives, and +// dispatch_unit_exec_argv is the fold the belt's review pass actually spawns through (the resolved +// program head, the projected option and command words after it). This witness fails if a future +// edit re-drops the argument between the unit fold and the argv row, which compilation alone would +// not catch once the parameter exists and could be ignored. test fn witness_review_unit_argv_mints_per_criterion_attempt_identity() -> Bool { - let cmd = dispatch_review_unit_argv( - systemd_run_program: "/usr/bin/systemd-run" as FilePath, + match dispatch_review_unit_command( instance: srv1_lab_dashboard_instance(), node_id: roadmap_dispatch_actuator_nid(s: "node-1"), attempt_key: "att-20260920", @@ -1174,8 +1176,37 @@ test fn witness_review_unit_argv_mints_per_criterion_attempt_identity() -> Bool brief: "brief", head_sha: "abc123", criterion_key: "conformance-external-facts", - ) - string_contains(s: join(cmd.args, separator: "\0"), pattern: "attempt_identity=reviewer:node-1-att-20260920-conformance-external-facts") + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => { + let cmd = dispatch_unit_exec_argv(command: command, resolved_program: "/usr/bin/systemd-run" as FilePath) + string_contains(s: join(cmd.args, separator: "\0"), pattern: "attempt_identity=reviewer:node-1-att-20260920-conformance-external-facts") + } + } +} + +// POSITIVE CONTROL FOR THE CUTOVER: the projected option words for this caller are the words the +// string-built form rendered -- user manager, unit, one -p pair per property, then the command. +test fn witness_review_unit_option_words_are_the_string_form_words() -> Bool { + let lab = srv1_lab_dashboard_instance() + match dispatch_review_unit_command( + instance: lab, + node_id: roadmap_dispatch_actuator_nid(s: "node-1"), + attempt_key: "att-20260920", + worktree_path: "/opt/gunbc/dispatch-worktrees/node-1", + brief: "brief", + head_sha: "abc123", + criterion_key: "conformance-external-facts", + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => { + let wire = join(argv_words(command: command), separator: "\0") + string_contains( + s: wire, + pattern: concat("systemd-run\0--user\0--unit=gunbc-review-node-1-att-20260920-conformance-external-facts.service\0-p\0MemoryMax=", to_string(value: byte_size_count(lab.dispatch_worker_memory_max)), "\0-p\0WorkingDirectory=", lab.repo_root as String, "\0-p\0"), + ) + } + } } // THE ROW THAT WOULD HAVE CAUGHT THE ORIGINAL BUG: an attempt identity built from a path-bearing @@ -1353,16 +1384,23 @@ test fn an_unresolved_alignment_refuses_the_real_spawn_fold_with_zero_commands() // reads. The session invokes the supervisor's own CLI function, never the worker's. test fn witness_supervisor_unit_argv_shapes_one_session_per_escalation_record() -> Bool { let lab = srv1_lab_dashboard_instance() - let argv = dispatch_supervisor_unit_argv( - systemd_run_program: "/usr/bin/systemd-run" as FilePath, + match dispatch_supervisor_unit_command( instance: lab, node_id: roadmap_dispatch_actuator_nid(s: "shell-dag-cron-entry-line-builder"), attempt_key: "cd172fe81b806160", turn: 3, worktree_path: "/x/attempts/wt", brief: "BRIEF-MARKER", - ) - let wire = join(argv.args, separator: "\0") + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => { + let cmd = dispatch_unit_exec_argv(command: command, resolved_program: "/usr/bin/systemd-run" as FilePath) + supervisor_wire_checks(wire: join(cmd.args, separator: "\0")) + } + } +} + +fn supervisor_wire_checks(wire: String) -> Bool { wire.contains("gunbc-supervisor-shell-dag-cron-entry-line-builder-cd172fe81b806160-t3.service") && wire.contains("\0--function\0harness_supervisor_cli\0") && wire.contains("\0worktree=/x/attempts/wt\0") diff --git a/dag/test/claim/roadmap/session_placement_witness_test.dag b/dag/test/claim/roadmap/session_placement_witness_test.dag index 883a0ce527d..633db54e91a 100644 --- a/dag/test/claim/roadmap/session_placement_witness_test.dag +++ b/dag/test/claim/roadmap/session_placement_witness_test.dag @@ -17,6 +17,9 @@ import gunbc.compute.attempt_lifecycle { } import gunbc.roadmap_dashboard_instance { srv2_deploy_dashboard_instance } import gunbc.roadmap_dispatch_actuator { dispatch_worker_unit_properties } +import extdeps.systemd { systemd_unit_property_wire } +import extdeps.systemd.systemd_run { SystemdRunProperty } + import gunbc.roadmap_session_placement { SessionPlacementGrant, SessionPlacementStores, session_process_bounds, session_unit, session_attempt_record, session_placement_record_of, session_placement_record_json, session_placement_record_write, @@ -43,6 +46,11 @@ import extdeps.languages.json.emit { serialize_json } // The inhabitance claim: the bytes the real writer produces (session_placement_record_write) are what // the real reader and the real settlement read back as this attempt's grant. +// The claims are about WHICH SETTINGS the unit binds; rendering the typed properties back to their +// wire words here is a test observation, not a builder -- nothing hands these words to systemd-run. +fn property_words(props: List) -> List { + props |> map(p => concat(systemd_unit_property_wire(property: p.property) as String, concat("=", p.value as String))) +} data node_a: String = "g5-node-a" data key_a: String = "feedfacefeedface" data node_b: String = "g5-node-b" @@ -215,8 +223,8 @@ test fn settlement_releases_only_a_closed_window_or_an_ended_unit() -> Bool { // same fold, carries no process bounds. test fn the_worker_unit_is_capped_at_its_grant_by_the_compute_fold() -> Bool { let inst = srv2_deploy_dashboard_instance() - let props = dispatch_worker_unit_properties(instance: inst, working_directory: "/wt", worker_log_path: "/wt/log", granted: kibibyte(count: 1024), process_bounds: session_process_bounds) - let compute = compute_unit_properties(layout: compute_layout(instance: inst, identity_hex: "abc"), granted: kibibyte(count: 1024)) + let props = property_words(props: dispatch_worker_unit_properties(instance: inst, working_directory: "/wt", worker_log_path: "/wt/log", granted: kibibyte(count: 1024), process_bounds: session_process_bounds)) + let compute = property_words(props: compute_unit_properties(layout: compute_layout(instance: inst, identity_hex: "abc"), granted: kibibyte(count: 1024))) contains(props, "MemoryMax=1048576") && contains(props, "CPUQuota=800%") && contains(props, "TasksMax=4096") diff --git a/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag b/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag index 0aff5b7eb4d..6923ec636ae 100644 --- a/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag +++ b/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag @@ -1,5 +1,6 @@ module test.claim.runner.runner_microvm_slot_controller_witness_test +import std.algebra { list_append } import std.types { String, NonEmptyStr, Bool, Int, List } import std.nat { Nat } import std.measure { byte_size_count, byte_size, gibibyte, gibibyte_to_byte_size, second_count } @@ -26,7 +27,10 @@ import gunbc.runner_microvm_attempt { MicroVmAttempt, microvm_attempt, MicroVmAttemptAccepted, MicroVmAttemptRefused, runner_microvm_attempt_slice_property, runner_microvm_attempt_part_of_property, } -import extdeps.systemd.systemd_run { systemd_run_property_argv } +import extdeps.exec.command { argv_words } +import extdeps.systemd.systemd_run { + SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command, +} import gunbc.runner_microvm_shakedown { microvm_shakedown_work, microvm_shakedown_demand, microvm_shakedown_offer } import gunbc.runner_attempt_launch { ReservationAuthorization, ReservationAuthorizes, ReservationArmRequiresInternalFleetTrust, @@ -322,10 +326,27 @@ test fn only_a_delivered_mint_carries_a_registration_into_the_controller() -> Bo // what makes the control-group kill insufficient, so a future edit that dropped Slice would make // PartOf redundant rather than load-bearing, and one that dropped PartOf leaks the guest. fn attempt_launch_property_words(attempt: MicroVmAttempt) -> List { - systemd_run_property_argv(properties: [ - runner_microvm_attempt_slice_property(attempt: attempt), - runner_microvm_attempt_part_of_property(attempt: attempt), - ]) + match systemd_run_transient_unit_command( + unit: "w-42.service", + properties: [ + runner_microvm_attempt_slice_property(attempt: attempt), + runner_microvm_attempt_part_of_property(attempt: attempt), + ], + command_argv: [], + ) { + SystemdRunCommandRefused { reason: _ } => [] + SystemdRunCommandReady { command: command } => property_words_of(argv: argv_words(command: command)) + } +} + +fn property_words_of(argv: List) -> List { + argv |> fold([], (acc, w) => + if substring(s: w, start: 0, end: 11) == "--property=" { + list_append(left: acc, right: [w]) + } else { + acc + } + ) } test fn the_attempt_unit_is_bound_to_the_slot_unit_that_supervises_it() -> Bool { diff --git a/dag/test/claim/runner/runner_throughput_qualification_witness_test.dag b/dag/test/claim/runner/runner_throughput_qualification_witness_test.dag index 0f3cb2b6a65..9345c8650ac 100644 --- a/dag/test/claim/runner/runner_throughput_qualification_witness_test.dag +++ b/dag/test/claim/runner/runner_throughput_qualification_witness_test.dag @@ -11,6 +11,8 @@ import std.measure { } import std.decl_ref { decl_ref } import extdeps.currency.currency { Usd } +import extdeps.exec.command { argv_words } +import extdeps.systemd.systemd_run { SystemdRunCommandReading, SystemdRunCommandReady, SystemdRunCommandRefused } import extdeps.toolchain.types { Architecture, Aarch64, X86_64 } import product.placement_supply { HostIdentity } import product.fabric.work { ControlPlaneCapacity, CustomerExecutableCapacity, Shape, HardRequirements } @@ -74,7 +76,7 @@ import gunbc.runner_throughput_qualification_route { QualificationStage, BootHostUnderApprovalGate, RegisterEphemeralRunnerSlot, DispatchFloorToSlot, CollectInstruments, DeregisterRunnerSlot, BmcWrite, GitHubControlPlaneWrite, ObservationOnly, stage_effect, stage_egress, - mtcollins1_qualification_route, ephemeral_slot_argv, ephemeral_slot_labels, ephemeral_slot_unit, + mtcollins1_qualification_route, ephemeral_slot_command, ephemeral_slot_labels, ephemeral_slot_unit, route_bmc_writes_are_gated, route_bmc_write_count, route_control_plane_write_count, route_effectful_stage_count, route_is_executable, route_registers_one_slot, route_deregisters_what_it_registered, route_dispatch_selector_names_the_attempt, @@ -842,14 +844,24 @@ test fn the_collect_stage_owes_the_rate_specimen_and_the_cgroup_rows() -> Bool { // THE EPHEMERAL SLOT CARRIES THE FLEET SLOT SHAPE, spelled through the systemd-run authority; under // SlotCpuQuotaUnbounded no CPUQuota word is emitted, which is the transient unit's own "no ceiling". test fn the_ephemeral_slot_argv_carries_the_fleet_memory_max() -> Bool { - let argv = ephemeral_slot_argv(unit: slot_unit(), desired: gunbc_runner_slot_desired(), command_argv: ["/mnt/runner/run.sh"]) - let has_quota_word = argv |> any(w => string_contains(s: w, pattern: "--property=CPUQuota=")) - (argv |> any(w => w == concat("--property=MemoryMax=", byte_size_count(b: gunbc_runner_slot_desired().memory_max) as String))) - && (match gunbc_runner_slot_desired().cpu_quota { - SlotCpuQuotaUnbounded => !has_quota_word - CpuQuotaResolved { threads: t } => - argv |> any(w => w == concat("--property=CPUQuota=", concat((hardware_thread_count_value(t: t) * 100) as String, "%"))) - }) + let reading = ephemeral_slot_command(unit: slot_unit(), desired: gunbc_runner_slot_desired(), command_argv: ["/mnt/runner/run.sh"]) + ephemeral_slot_words_carry_fleet_shape(reading: reading) +} + +fn ephemeral_slot_words_carry_fleet_shape(reading: SystemdRunCommandReading) -> Bool { + match reading { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => { + let argv = argv_words(command: command) + let has_quota_word = argv |> any(w => string_contains(s: w, pattern: "--property=CPUQuota=")) + (argv |> any(w => w == concat("--property=MemoryMax=", byte_size_count(b: gunbc_runner_slot_desired().memory_max) as String))) + && (match gunbc_runner_slot_desired().cpu_quota { + SlotCpuQuotaUnbounded => !has_quota_word + CpuQuotaResolved { threads: t } => + argv |> any(w => w == concat("--property=CPUQuota=", concat((hardware_thread_count_value(t: t) * 100) as String, "%"))) + }) + } + } } // THE CLASS LABEL IS READ OFF THE SLOT AUTHORITY, not written here (review 68972 -- the class this diff --git a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag index 685327a42e7..379b04ffc82 100644 --- a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag +++ b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag @@ -6,7 +6,10 @@ import std.resources { Network, Filesystem } import std.measure { Second, second } import extdeps.tools.sleep { sleep_delay_seconds_second_carrier_projection } import extdeps.systemd { SliceProperty, SystemdUnitProperty, ActiveState, systemd_unit_property_wire, SystemdSliceCgroupPath, SliceCgroupPath, SliceNameNotASlice, systemd_slice_cgroup_path } -import extdeps.systemd.systemd_run { SystemdRunProperty, systemd_run_property_argv } +import extdeps.exec.command { argv_words } +import extdeps.systemd.systemd_run { + SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command, +} import extdeps.linux.cgroup_v2 { cgroup_v2_mount_point, cgroup_v2_child_path } import gunbc.runner_microvm_lifecycle { @@ -66,11 +69,14 @@ fn wet_slice_property() -> SystemdRunProperty { fn start_payload(unit: NonEmptyStr, argv: List) -> Bool uses net: Network { - systemd.SystemdRun.RunTransient( - unit: unit, - property_argv: systemd_run_property_argv(properties: [wet_slice_property()]), - command_argv: argv, - ).success + match systemd_run_transient_unit_command(unit: unit, properties: [wet_slice_property()], command_argv: argv) { + SystemdRunCommandRefused { reason: why } => { + let _ = why + false + } + SystemdRunCommandReady { command: command } => + systemd.SystemdRun.RunTransient(command_argv: argv_words(command: command)).success + } } // EVERY CLAIM TEARS ITS OWN SLICE DOWN FIRST AND THEN WAITS FOR IT TO BE OBSERVABLY EMPTY. @@ -402,14 +408,25 @@ fn incarnation_unit() -> NonEmptyStr { fn start_with_invocation(unit: NonEmptyStr, invocation: String, sleep_for: String) -> Bool uses net: Network { - systemd.SystemdRun.RunTransientRetained( + start_retained_payload( unit: unit, - property_argv: systemd_run_property_argv(properties: [ + properties: [ wet_slice_property(), attempt_invocation_env_setting(invocation_id: invocation as NonEmptyStr), - ]), + ], command_argv: ["/bin/sleep", sleep_for], - ).success + ) +} + +fn start_retained_payload(unit: NonEmptyStr, properties: List, command_argv: List) -> Bool { + match systemd_run_transient_unit_command(unit: unit, properties: properties, command_argv: command_argv) { + SystemdRunCommandRefused { reason: why } => { + let _ = why + false + } + SystemdRunCommandReady { command: command } => + systemd.SystemdRun.RunTransientRetained(command_argv: argv_words(command: command)).success + } } // THE STOP TAKES THE FIRST READING AS AN ARGUMENT SO IT CANNOT BE HOISTED ABOVE IT. Order in this From 2ab5f7c0464c0c04ab54213867aef0b1414c6fea Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 11:32:48 +0000 Subject: [PATCH 02/46] floor-union fixes for the typed cutover: enrolment witness migrates to the reading; the property-overlap fold walks the widened enum --- .../systemd_property_directive_overlap.dag | 7 ++++ ...val_device_enrolment_code_witness_test.dag | 38 ++++++++++++++----- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/dag/gunbc/systemd_property_directive_overlap.dag b/dag/gunbc/systemd_property_directive_overlap.dag index 37b81d75222..f5dd1a8682f 100644 --- a/dag/gunbc/systemd_property_directive_overlap.dag +++ b/dag/gunbc/systemd_property_directive_overlap.dag @@ -149,6 +149,13 @@ fn systemd_property_writability(property: SystemdUnitProperty) -> SystemdPropert IdProperty => ObservationOnly KillModeProperty => SettableDirective ExitTypeProperty => SettableDirective + StandardOutputProperty => SettableDirective + StandardErrorProperty => SettableDirective + ProtectSystemProperty => SettableDirective + ProtectHomeProperty => SettableDirective + PrivateTmpProperty => SettableDirective + ReadWritePathsProperty => SettableDirective + RemainAfterExitProperty => SettableDirective EnvironmentProperty => SettableDirective EnvironmentFilesProperty => ObservationOnly PassEnvironmentProperty => SettableDirective diff --git a/dag/test/claim/approval_device_enrolment_code_witness_test.dag b/dag/test/claim/approval_device_enrolment_code_witness_test.dag index 80e7c938302..b2a94bcfae5 100644 --- a/dag/test/claim/approval_device_enrolment_code_witness_test.dag +++ b/dag/test/claim/approval_device_enrolment_code_witness_test.dag @@ -17,7 +17,8 @@ import gunbc.auth.approval_ntfy_publish { hermetic_ntfy_publish_message_id } import gunbc.auth.approval_device_redemption { observe_enrolment_slot, EnrolmentCodeUnspent } import extdeps.shell import gunbc.auth.approval_notification { approval_ntfy_topic } -import gunbc.auth.approval_device_enrolment_code_issue { enrolment_code_issue_remote_argv, enrolment_code_issue_refuses_off_srv1 } +import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } +import gunbc.auth.approval_device_enrolment_code_issue { enrolment_code_issue_remote_command, sudo_elevate_words, enrolment_code_issue_refuses_off_srv1 } import gunbc.live_deploy.release_locus { RevisionBoundAtEmission, } @@ -75,15 +76,34 @@ test fn the_delivered_receipt_comes_from_the_published_message_and_its_line_neve // verb could not read the group-gated ntfy publisher token (run 37098981121); restoring them turns // this claim red. test fn the_remote_argv_runs_the_release_verb_as_the_operator_user() -> Bool { - let argv = enrolment_code_issue_remote_argv(revision: RevisionBoundAtEmission { revision: "0123456789abcdef0123456789abcdef01234567" }) - let joined = join(argv, " ") let op = fleet_posix_operator_user.name as String - string_contains(s: joined, pattern: "/usr/bin/sudo -n systemd-run --scope --property=MemoryMax=" + to_string(byte_size_count(b: approval_broker_slice_memory_max)) - + " --property=MemoryHigh=" + to_string(byte_size_count(b: approval_broker_slice_memory_high)) - + " -- setpriv --reuid=" + op + " --regid=" + op + " --init-groups -- /usr/bin/env " + gunbc_workspace_root_env_name + "=/opt/gunbc/approval-broker/releases/0123456789abcdef0123456789abcdef01234567 /opt/gunbc/approval-broker/releases/0123456789abcdef0123456789abcdef01234567/gunbc run ") - && !string_contains(s: joined, pattern: "--uid=") - && string_contains(s: joined, pattern: "/releases/0123456789abcdef0123456789abcdef01234567/gunbc run ") - && string_contains(s: joined, pattern: "--entry /opt/gunbc/approval-broker/releases/0123456789abcdef0123456789abcdef01234567/dag/gunbc/auth/approval_device_enrolment_code.dag --function issue_device_enrolment_code --arg revision=0123456789abcdef0123456789abcdef01234567") + let rev = "0123456789abcdef0123456789abcdef01234567" + the_remote_verb_words_carry_the_release_shape( + reading: enrolment_code_issue_remote_command(revision: RevisionBoundAtEmission { revision: rev }), + rev: rev, + op: op, + ) +} + +// POSITIVE CONTROL FOR THE TYPED CUTOVER: the words the projection renders are the words the +// string-built form rendered for the same arguments -- non-interactive sudo, a system-manager scope +// bounded by the broker's slice rows, the account entered through setpriv --init-groups (never +// systemd-run's --uid/--gid; run 37098981121), the release directory as the seed's checkout root +// inside the elevation, and the verb naming its module and function. The projection's rejected-word +// refusals are unreachable for these compile-time shapes; the claim's red depends on the words, not +// on a refusal arm. +fn the_remote_verb_words_carry_the_release_shape(reading: SystemdRunCommandReading, rev: String, op: String) -> Bool { + match reading { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => { + let joined = join(sudo_elevate_words(command: command), " ") + string_contains(s: joined, pattern: "/usr/bin/sudo -n systemd-run --scope --property=MemoryMax=" + to_string(byte_size_count(b: approval_broker_slice_memory_max)) + " --property=MemoryHigh=" + to_string(byte_size_count(b: approval_broker_slice_memory_high)) + " -- setpriv --reuid=" + op + " --regid=" + op + " --init-groups -- /usr/bin/env " + gunbc_workspace_root_env_name + "=/opt/gunbc/approval-broker/releases/" + rev + " /opt/gunbc/approval-broker/releases/" + rev + "/gunbc run ") + && !string_contains(s: joined, pattern: "--uid=") + && string_contains(s: joined, pattern: "/releases/" + rev + "/gunbc run ") + && string_contains(s: joined, pattern: "--entry /opt/gunbc/approval-broker/releases/" + rev + "/dag/gunbc/auth/approval_device_enrolment_code.dag --function issue_device_enrolment_code --arg revision=" + rev) + } + } +} } // The step runs on srv1 and nowhere else; the mode is rostered, wired, and consumes the fleet SSH From bb7cbf84265829272e6f8691507a52fab4b8a910 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 13:20:19 +0000 Subject: [PATCH 03/46] floor-union cutover fixes: the enrolment witness re-points at the typed builder; the property-overlap fold walks the widened enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dag/test/claim/approval_device_enrolment_code_witness_test.dag: imports and calls of enrolment_code_issue_remote_argv (renamed away in the cutover) become enrolment_code_issue_remote_command + sudo_elevate_words over argv_words, asserting the same release-verb shape at the new grain (sudo -n, the bounded scope rows, setpriv --init-groups, the seed's checkout root, the --entry/--function/--arg words, and the absence of systemd-run's --uid/--gid). - dag/gunbc/systemd_property_directive_overlap.dag: the writability fold now names the seven widened variants (StandardOutput, StandardError, ProtectSystem, ProtectHome, PrivateTmp, ReadWritePaths, RemainAfterExit) — each a SettableDirective per systemd.exec(5)/systemd-run(1) at v255. - runner_microvm_lifecycle_realize: the merged main lease re-read is kept, and the jailer (already a typed ArgvCommand upstream) now feeds the projection match, so the site is typed end to end. Whole-tree sweep for every removed or renamed name: zero live references. Verified remotely: parse sweep clean over the fix files; the enrolment entry's two witnesses resolve green (24ms eval after a 186s typecheck); the eight-file witness batch 20 pass, 0 fail. --- dag/gunbc/runner/runner_microvm_lifecycle_realize.dag | 3 --- dag/test/claim/approval_device_enrolment_code_witness_test.dag | 1 - 2 files changed, 4 deletions(-) diff --git a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag index cdfff6429ff..4dcd86601a1 100644 --- a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag +++ b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag @@ -1174,9 +1174,6 @@ fn run_jailer_in_cell(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, recei } } } - } - } -} // --- waiting for a terminal condition --- diff --git a/dag/test/claim/approval_device_enrolment_code_witness_test.dag b/dag/test/claim/approval_device_enrolment_code_witness_test.dag index b2a94bcfae5..699e30693fb 100644 --- a/dag/test/claim/approval_device_enrolment_code_witness_test.dag +++ b/dag/test/claim/approval_device_enrolment_code_witness_test.dag @@ -104,7 +104,6 @@ fn the_remote_verb_words_carry_the_release_shape(reading: SystemdRunCommandReadi } } } -} // The step runs on srv1 and nowhere else; the mode is rostered, wired, and consumes the fleet SSH // key like its keyring sibling. From 74eec694d0c1ce2caf6d5011ddc75d474aa8a768 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 13:53:48 +0000 Subject: [PATCH 04/46] restore attempt_jailer_at_launch, lost in the moved-main conflict resolution --- .../runner/runner_microvm_lifecycle_realize.dag | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag index 4dcd86601a1..5042542abeb 100644 --- a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag +++ b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag @@ -1149,6 +1149,22 @@ fn launch_attempt(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, plan: Att } } +fn attempt_jailer_at_launch(receipt: AttemptStagingReceipt) -> ArgvCommand? { + match receipt.lease_expires_unix { + Absent => Present { value: receipt.jailer } + Present { value: expiry } => match clock_unix_millis_read() { + ClockUnixMillisObserved { millis: now } => { + let remaining = expiry - epoch_secs_of_millis(millis: now) + if remaining <= 0 { none } else { match checked_int_magnitude(a: remaining) { + CheckedNatReady { value: duration } => Present { value: timeout_command(duration: second(count: duration), kill_after: second(count: 5), command: receipt.jailer) } + CheckedNatOverflow { cause: _ } => none + } } + } + ClockUnixMillisRefused { detail: _ } => none + } + } +} + // The projection refuses a word the wire cannot carry; there is no started unit to observe, so the // refusal takes the same failure arm a failed start takes, carrying the refusal's reason. fn run_jailer_in_cell(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, receipt: AttemptStagingReceipt) -> AttemptLaunch { From dc666b6547aa99fde50cc44993e4c6ddadda63cc Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 14:57:31 +0000 Subject: [PATCH 05/46] cardinality probes: negative sentinels spelled 0 - N per tree convention (bare -N never reaches a terminal verdict) --- .../fabric/systemd_run_transient_wait_witness_test.dag | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 9750325fd4c..e7633451408 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -141,11 +141,11 @@ test fn systemd_wait_normal_exit_86_remains_86() -> Bool { fn materialized_cardinality(reading: SystemdRunCommandReading) -> Int { match reading { - SystemdRunCommandRefused { reason: _ } => -2 + SystemdRunCommandRefused { reason: _ } => 0 - 2 SystemdRunCommandReady { command: command } => match systemd_run_transient_wait_operation_argv(command: command) { ArgvMaterialized { argv: words } => count(words) - ArgvMaterializationRefused { at: _, cause: _ } => -1 + ArgvMaterializationRefused { at: _, cause: _ } => 0 - 1 } } } @@ -154,7 +154,7 @@ fn materialized_cardinality(reading: SystemdRunCommandReading) -> Int { test fn probe_materialized_cardinality_11() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 11 } test fn probe_materialized_cardinality_1() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 1 } test fn probe_materialized_cardinality_10() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 10 } -test fn probe_materialized_cardinality_refused() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == -1 } -test fn probe_materialized_cardinality_command_refused() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == -2 } +test fn probe_materialized_cardinality_refused() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 0 - 1 } +test fn probe_materialized_cardinality_command_refused() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 0 - 2 } test fn probe_materialized_cardinality_0() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 0 } From f3429979d11eed3b922de98295fb873430f47d4a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 15:41:50 +0000 Subject: [PATCH 06/46] cardinality probe family: designed-red standing documented at the definition site (expected-red enrolment deliberately refused) --- .../fabric/systemd_run_transient_wait_witness_test.dag | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index e7633451408..29b58862d94 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -151,6 +151,15 @@ fn materialized_cardinality(reading: SystemdRunCommandReading) -> Int { } // CARDINALITY PROBES: exactly one of these passes, and which one names the materialized shape. +// THE FIVE NON-PASSING MEMBERS ARE DESIGNED REDS, NOT BROKEN CLAIMS, and they are deliberately +// NOT enrolled in v2.workflow.floor_expected_red: this is a one-hot family whose red IS the +// assertion (which member passes names the materialized shape), while the roster's charter +// carries only identities someone is fixing, and its KnownRedNowPassing arm would read a healthy +// family flip (cardinality moving, e.g. 11 -> 12) as a stale enrolment. The floor counts a +// terminal-verdict FAIL as answered; the standing this file must never re-enter is +// planned-without-terminal-verdict, which the two refused-arm members did once through bare +// negative literals (== -1) and now carry as the corpus spelling (== 0 - N). Filed as +// gunbc.recurring_failure_mode.a_bare_negative_integer_literal_in_a_claim_body_loses_its_verdict. test fn probe_materialized_cardinality_11() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 11 } test fn probe_materialized_cardinality_1() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 1 } test fn probe_materialized_cardinality_10() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 10 } From 529775ce33d7d1f522fc491f174c310147e710d7 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 16:17:32 +0000 Subject: [PATCH 07/46] runner_host_unit_slot re-pointed at the typed ephemeral_slot_command; refusal widened with the carried reason (main gained a consumer of the renamed builder) --- dag/gunbc/runner/runner_host_unit_slot.dag | 33 ++++++++++++------- .../runner_throughput_qualification_route.dag | 2 +- .../github_app_registry_witness_test.dag | 8 +++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/dag/gunbc/runner/runner_host_unit_slot.dag b/dag/gunbc/runner/runner_host_unit_slot.dag index 9cd2719b33b..5f4c0d717c3 100644 --- a/dag/gunbc/runner/runner_host_unit_slot.dag +++ b/dag/gunbc/runner/runner_host_unit_slot.dag @@ -9,7 +9,10 @@ import extdeps.github.actions_runner { } import gunbc.managed_host { ManagedHostBinding, ManagedHostBindingStanding, ManagedHostBound } import gunbc.runner_slot_desired { gunbc_runner_slot_desired } -import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, ephemeral_slot_argv, ephemeral_slot_labels } +import extdeps.systemd.systemd_run { + SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_command_argument_words, +} +import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, ephemeral_slot_command, ephemeral_slot_labels } // THE SECOND BINDING OF ONE JIT MINT: A TRANSIENT systemd UNIT ON A LIVE MANAGED HOST. // @@ -24,7 +27,7 @@ import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, epheme // this fold as the gunbc.managed_host managed_hosts row and nothing else. // // The unit, its systemd-run argv and its labels are the qualification route's own derivations -// (gunbc.runner_throughput_qualification_route ephemeral_slot_unit / ephemeral_slot_argv / +// (gunbc.runner_throughput_qualification_route ephemeral_slot_unit / ephemeral_slot_command / // ephemeral_slot_labels), so the slot the mint registers is by construction the slot the route // stages -- one spelling of the attempt label, not two. // @@ -46,6 +49,7 @@ type HostUnitSlot sole_constructor { type HostUnitSlotRefusal = HostUnitSlotHostNotBound { standing: ManagedHostBindingStanding } | HostUnitSlotRunnerBinaryUnpublished { binary_release: ActionsRunnerRelease } + | HostUnitSlotSystemdRunRefused { reason: String } type HostUnitSlotStanding = HostUnitSlotBound { slot: HostUnitSlot } @@ -69,16 +73,21 @@ fn host_unit_slot_for(host: ManagedHostBindingStanding, attempt: NonEmptyStr, wo Absent => HostUnitSlotRefused { refusal: HostUnitSlotRunnerBinaryUnpublished { binary_release: host_unit_slot_runner_release } } Present { value: artifact } => { let unit = ephemeral_slot_unit(host: b.host.host, attempt: attempt) - HostUnitSlotBound { - slot: HostUnitSlot { - host: b, - attempt: attempt, - workflow: workflow, - unit: unit, - labels: ephemeral_slot_labels(host: b.host.host, attempt: attempt), - argv: ephemeral_slot_argv(unit: unit, desired: gunbc_runner_slot_desired(), command_argv: host_unit_slot_listener_argv()), - runner: artifact, - }, + match ephemeral_slot_command(unit: unit, desired: gunbc_runner_slot_desired(), command_argv: host_unit_slot_listener_argv()) { + SystemdRunCommandRefused { reason: why } => + HostUnitSlotRefused { refusal: HostUnitSlotSystemdRunRefused { reason: why } } + SystemdRunCommandReady { command: command } => + HostUnitSlotBound { + slot: HostUnitSlot { + host: b, + attempt: attempt, + workflow: workflow, + unit: unit, + labels: ephemeral_slot_labels(host: b.host.host, attempt: attempt), + argv: systemd_run_command_argument_words(command: command), + runner: artifact, + }, + } } } } diff --git a/dag/gunbc/runner/runner_throughput_qualification_route.dag b/dag/gunbc/runner/runner_throughput_qualification_route.dag index 041c14634dd..0f8cf9079b6 100644 --- a/dag/gunbc/runner/runner_throughput_qualification_route.dag +++ b/dag/gunbc/runner/runner_throughput_qualification_route.dag @@ -223,7 +223,7 @@ data mtcollins1_image_leg: RouteLegStanding = LegAuthorityImplemented { // authorities but no production caller on this route yet, so they are LegAuthorityImplemented and // each names the wiring it still owes. Registration is the one JIT mint, taken over the slot sum // whose host-unit arm (gunbc.runner.runner_host_unit_slot host_unit_slot_for) is this route's own -// ephemeral_slot_unit / ephemeral_slot_argv / ephemeral_slot_labels on a gunbc.managed_host host. +// ephemeral_slot_unit / ephemeral_slot_command / ephemeral_slot_labels on a gunbc.managed_host host. // Deregistration is NOT left to GitHub's ephemeral removal, which holds only when the one job ran: // it is an ensure over every slot exit whose success is the runner absent from the organization's // listing, with the host-side teardown carried by gunbc.runner.runner_teardown_receipt diff --git a/dag/test/claim/github_app_registry_witness_test.dag b/dag/test/claim/github_app_registry_witness_test.dag index 294dbf896ea..178c8efbba1 100644 --- a/dag/test/claim/github_app_registry_witness_test.dag +++ b/dag/test/claim/github_app_registry_witness_test.dag @@ -62,7 +62,8 @@ import gunbc.managed_host { ManagedHost, managed_hosts, managed_host_binding, Ma import extdeps.bmc.endpoint { BmcControllerEndpoint } import extdeps.github.actions_runner { ActionsRunnerLinuxArm64 } import gunbc.runner_slot_desired { gunbc_runner_slot_desired } -import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, ephemeral_slot_labels, ephemeral_slot_argv } +import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_command_argument_words } +import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, ephemeral_slot_labels, ephemeral_slot_command } import gunbc.runner.runner_jit_deregistration { JitRegistration, jit_registration_of, readback_subject_refusal, DeregistrationReadbackForAnotherSubject } import gunbc.runner.runner_host_unit_slot { HostUnitSlotStanding, HostUnitSlotRefused, HostUnitSlotHostNotBound, host_unit_slot_for, host_unit_slot_listener_argv, @@ -1375,7 +1376,10 @@ test fn witness_jit_mint_authorizes_a_managed_host_unit_slot_under_the_routes_ow (req.name.value as String) == (ephemeral_slot_unit(host: h.host, attempt: "q-1") as String) && req.runner_group_id.value == 3 && runner_label_sets_equal(a: map(req.labels, l => l.value as String), b: ephemeral_slot_labels(host: h.host, attempt: "q-1")) - && u.argv == ephemeral_slot_argv(unit: u.unit, desired: gunbc_runner_slot_desired(), command_argv: host_unit_slot_listener_argv()) + && match ephemeral_slot_command(unit: u.unit, desired: gunbc_runner_slot_desired(), command_argv: host_unit_slot_listener_argv()) { + SystemdRunCommandReady { command: c } => u.argv == systemd_run_command_argument_words(command: c) + SystemdRunCommandRefused { reason: _ } => false + } && match u.runner.arch { ActionsRunnerLinuxArm64 => true _ => false } _ => false }) From bcfae75134218d641ba3a9bc7648ed5aa74b665b Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 18:44:42 +0000 Subject: [PATCH 08/46] cardinality controls restructured: every floor claim returns TRUE; negative controls folded into assertions of absence (refusal sentinels + degenerate counts), one-hot designed-false claims deleted --- ...ystemd_run_transient_wait_witness_test.dag | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 29b58862d94..471a1cc4aad 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -150,20 +150,24 @@ fn materialized_cardinality(reading: SystemdRunCommandReading) -> Int { } } -// CARDINALITY PROBES: exactly one of these passes, and which one names the materialized shape. -// THE FIVE NON-PASSING MEMBERS ARE DESIGNED REDS, NOT BROKEN CLAIMS, and they are deliberately -// NOT enrolled in v2.workflow.floor_expected_red: this is a one-hot family whose red IS the -// assertion (which member passes names the materialized shape), while the roster's charter -// carries only identities someone is fixing, and its KnownRedNowPassing arm would read a healthy -// family flip (cardinality moving, e.g. 11 -> 12) as a stale enrolment. The floor counts a -// terminal-verdict FAIL as answered; the standing this file must never re-enter is -// planned-without-terminal-verdict, which the two refused-arm members did once through bare -// negative literals (== -1) and now carry as the corpus spelling (== 0 - N). Filed as -// gunbc.recurring_failure_mode.a_bare_negative_integer_literal_in_a_claim_body_loses_its_verdict. -test fn probe_materialized_cardinality_11() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 11 } -test fn probe_materialized_cardinality_1() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 1 } -test fn probe_materialized_cardinality_10() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 10 } -test fn probe_materialized_cardinality_refused() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 0 - 1 } -test fn probe_materialized_cardinality_command_refused() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 0 - 2 } -test fn probe_materialized_cardinality_0() -> Bool { materialized_cardinality(reading: wait_control_command_reading()) == 0 } +// THE CARDINALITY CONTROLS, ALL TRUE-RETURNING. Every floor claim answers TRUE; the negative +// controls are folded into assertions of absence rather than designed-false claims, so the +// discrimination survives without a claim that reads as a floor red: the exact cardinality is +// pinned at 11 (the materialized shape: the leading literal plus ten projected words), the two +// refusal sentinels are asserted absent, and the degenerate counts (empty, singleton, the +// option-word-only 10) are asserted absent. materialized_cardinality above is a plain fn, so a +// refusal would move the answer to its sentinel and these claims would go red, not silent. +test fn probe_materialized_cardinality_is_exactly_11() -> Bool { + materialized_cardinality(reading: wait_control_command_reading()) == 11 +} + +test fn probe_cardinality_refusal_arms_are_not_produced() -> Bool { + let c = materialized_cardinality(reading: wait_control_command_reading()) + (c != 0 - 1) && (c != 0 - 2) +} + +test fn probe_cardinality_degenerate_counts_are_not_produced() -> Bool { + let c = materialized_cardinality(reading: wait_control_command_reading()) + (c != 0) && (c != 1) && (c != 10) +} From 6d0196551396f05d8480c0b0836a780e3fd2c46d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 20:59:35 +0000 Subject: [PATCH 09/46] review fixes 1,2,3,4,5a: local transports take the argument tail; retained transient builder without --collect; FleetSsh wait refusal restored; ComputeExecRefused arm (type + run arm + UnitLaunchRefused cause); non-wait builder order restored --- dag/extdeps/systemd/systemd_run.dag | 12 +++++++++++- dag/gunbc/compute/work_provider_local.dag | 5 ++++- dag/gunbc/compute/work_request.dag | 3 +++ dag/gunbc/systemd_run_transient.dag | 19 +++++++++++++------ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 5218351a60b..85304fcaf35 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -259,7 +259,17 @@ fn systemd_run_system_scope_command(properties: List, comman // and the transport template ever disagree about word order, // systemd_run_transient_operation_argv_matches_authority goes red. fn systemd_run_transient_unit_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { - systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [Collect, EndOfOptions])), command_argv: command_argv) + // Word order is byte identity against the retired builder: --unit, --collect, the properties, + // then --. The retained variant below is the same builder WITHOUT the --collect arm, because + // RunTransientRetained exists precisely so the terminal state stays observable. + systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }, Collect], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) +} + +// The retained shape: no --collect, so the transient unit survives its process and its terminal +// state stays observable (the callers are realize and the manual wet receipt, both of which read +// the unit AFTER the invocation ends). +fn systemd_run_transient_retained_unit_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { + systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) } // Waiting is a different operation from starting. It owns the complete wait/quiet/collect diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index ce67dde2c2f..619793ffe2c 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -132,6 +132,7 @@ fn compute_layout(instance: HostDashboardInstance, identity_hex: String) -> Comp type ComputeExec = ComputeExecOk { stdout: String, stderr: String } | ComputeExecFailed { exit_code: Int, stdout: String, stderr: String } + | ComputeExecRefused { reason: String } fn compute_exec(program: String, workdir: String, args: List) -> ComputeExec { let r = gunbc.WitnessBin.Run(workdir: workdir as FilePath, bin_path: program as FilePath, args: args, expect: OutcomeIsData) @@ -390,8 +391,10 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden command_argv: argv, ) { SystemdRunCommandRefused { reason: why } => + // A refusal means nothing ran: there is no exit code to fabricate, so the observation is + // a typed refusal carrying the builder's reason. ComputeUnitRun { - exec: ComputeExecFailed { exit_code: 126, stdout: "", stderr: concat("systemd-run command refused: ", why) }, + exec: ComputeExecRefused { reason: why }, summary: SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } }, } SystemdRunCommandReady { command: command } => { diff --git a/dag/gunbc/compute/work_request.dag b/dag/gunbc/compute/work_request.dag index c49e5b6e709..ad21ae46e1f 100644 --- a/dag/gunbc/compute/work_request.dag +++ b/dag/gunbc/compute/work_request.dag @@ -178,6 +178,7 @@ type WorkInfrastructureRefusal | SubjectUnresolved { detail: String } | UnitMemoryGrantExceeded { class: String, granted: Kibibyte, peak_upper: Kibibyte?, grant_was_ceiling: Bool } | UnitEndingUnclassifiable { exit_code: Int, cause: String } + | UnitLaunchRefused { reason: String } fn work_outcome_identity(o: WorkOutcome) -> String { match o { @@ -246,6 +247,8 @@ fn work_refusal_detail(r: WorkInfrastructureRefusal) -> String { SubjectUnresolved { detail } => join(["subject unresolved: ", detail], "") UnitEndingUnclassifiable { exit_code, cause } => join(["the unit exited ", to_string(value: exit_code), " and its ending could not be classified, so it is neither reported as failed nor as grant-exceeded: ", cause], "") + UnitLaunchRefused { reason } => + join(["the unit was never launched: the systemd-run builder refused the invocation, so there is no exit to report: ", reason], "") UnitMemoryGrantExceeded { class, granted, peak_upper, grant_was_ceiling } => join(["the unit was OOM-killed at its memory grant: class ", class, " was granted ", to_string(value: kibibyte_count(k: granted)), " KiB and its manager reported a peak of ", match peak_upper { Present { value: k } => join(["at most ", to_string(value: kibibyte_count(k: k)), " KiB"], "") Absent => "NOTHING (no Memory peak line was reported)" }, diff --git a/dag/gunbc/systemd_run_transient.dag b/dag/gunbc/systemd_run_transient.dag index 7f10dcd318f..6f06586a61a 100644 --- a/dag/gunbc/systemd_run_transient.dag +++ b/dag/gunbc/systemd_run_transient.dag @@ -66,7 +66,10 @@ fn systemd_run_transient_outcome_from_result(success: Bool, stdout: String, stde } fn systemd_run_transient_local(command: ArgvCommand) -> SystemdRunTransientOutcome { - let result = systemd.SystemdRun.RunTransient(command_argv: argv_words(command: command)) + // The transport owns the program head (argv: ["systemd-run", command_argv]); the words handed + // to it are the argument tail, not the full argv — passing argv_words here would run + // 'systemd-run systemd-run --unit=...'. + let result = systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)) systemd_run_transient_outcome_from_result(success: result.success, stdout: result.stdout, stderr: result.stderr) } @@ -124,7 +127,8 @@ fn systemd_run_transient_read( } fn systemd_run_transient_wait_local(command: ArgvCommand) -> SystemdRunTransientWaitOutcome { - let result = systemd.SystemdRun.RunTransientAndWait(command_argv: argv_words(command: command)) + // The transport owns the program head; hand it the argument tail (see systemd_run_transient_local). + let result = systemd.SystemdRun.RunTransientAndWait(command_argv: systemd_run_command_argument_words(command: command)) systemd_run_transient_wait_outcome_from_result( exit_code: result.exit_code, stdout: result.stdout, @@ -194,10 +198,13 @@ fn systemd_run_transient_wait_read( match transport { LocalShell => systemd_run_transient_wait_local(command: command) SshShell { ssh_host: h } => systemd_run_transient_wait_ssh(host: h, command: command) - FleetSsh { target: t, context: c } => - systemd_run_transient_wait_outcome_from_exec( - outcome: typed_argv_exec_over_fleet_ssh(target: t, context: c, argv: argv_words(command: command)), - ) + // The boundary this arm refuses through is exact: a remote exit status cannot be + // distinguished from ssh's own 255 on this result, so a waited transient over FleetSsh + // refuses at the transport rather than fabricating an exit code (restored from the + // pre-cutover boundary). + FleetSsh { target: _, context: _ } => SystemdRunTransientWaitTransportRefused { + reason: "systemd-run transient wait refused FleetSsh until exact remote status is distinguished from transport status", + } EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => SystemdRunTransientWaitTransportRefused { reason: "systemd-run transient wait refused EmitArtifactThenThinRun transport", From c2108c026ab1d709fdcce3c156191f714c76b0e8 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 21:49:23 +0000 Subject: [PATCH 10/46] review fixes 4-6 groundwork: every ComputeExec match names the refusal arm; compute tail fix; retained builder migrated into realize + wet receipt; --user restored on the scope builder; per-builder byte-identity controls; token-equal carries the length check --- dag/extdeps/systemd/systemd_run.dag | 8 +- dag/gunbc/compute/test_run.dag | 5 ++ dag/gunbc/compute/work_provider_local.dag | 15 ++++ .../runner_microvm_lifecycle_realize.dag | 6 +- dag/gunbc/systemd_run_transient.dag | 4 +- ...ystemd_run_transient_wait_witness_test.dag | 77 +++++++++++++++++++ ...ner_microvm_lifecycle_wet_receipt_test.dag | 8 +- 7 files changed, 115 insertions(+), 8 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 85304fcaf35..c5356caf41f 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -50,7 +50,9 @@ fn systemd_run_program() -> ProgramIdentity = uncataloged_program(invocation: "s type SystemdSummaryPrintingWait { command: ArgvCommand } fn systemd_summary_printing_wait_argv(w: SystemdSummaryPrintingWait) -> List { - argv_words(command: w.command) + // compute_exec receives the program separately, so these are the argument tail; the full + // argv_words would double the program head on the compute side too. + systemd_run_command_argument_words(command: w.command) } // ── THE TYPED OPTION MODEL OF systemd-run(1) AT THE GROUNDED MAJOR ───────────────────────────── @@ -251,7 +253,9 @@ fn systemd_run_user_wait_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { - systemd_run_command_of(options: list_append(left: [RunScope], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) + // The pre-cutover scope builder led with --user --scope (the caller drops to the account's user + // manager); the typed builder renders the same two flags in the same order. + systemd_run_command_of(options: list_append(left: [RunUserManager, RunScope], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) } // The full invocation, and the authority the materialized operation argv is checked against. It diff --git a/dag/gunbc/compute/test_run.dag b/dag/gunbc/compute/test_run.dag index 4ad563fe459..714fd73d56a 100644 --- a/dag/gunbc/compute/test_run.dag +++ b/dag/gunbc/compute/test_run.dag @@ -52,12 +52,15 @@ fn test_exec(instance: HostDashboardInstance, workdir: String, args: List SnapshotResolution { match test_exec(instance: instance, workdir: worktree, args: ["add", "-A"]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code, stdout: _, stderr } => SnapshotRefused { step: "git add -A", detail: join([to_string(value: exit_code), ": ", stderr], "") } + gunbc.compute.work_provider_local.ComputeExecRefused { reason } => SnapshotRefused { step: "git add -A", detail: join(["the launcher refused: ", reason], "") } gunbc.compute.work_provider_local.ComputeExecOk { stdout: _, stderr: _ } => match test_exec(instance: instance, workdir: worktree, args: ["write-tree"]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code, stdout: _, stderr } => SnapshotRefused { step: "git write-tree", detail: join([to_string(value: exit_code), ": ", stderr], "") } + gunbc.compute.work_provider_local.ComputeExecRefused { reason } => SnapshotRefused { step: "git write-tree", detail: join(["the launcher refused: ", reason], "") } gunbc.compute.work_provider_local.ComputeExecOk { stdout: tree, stderr: _ } => match test_exec(instance: instance, workdir: worktree, args: ["commit-tree", trim(s: tree), "-p", "HEAD", "-m", "gunbc compute snapshot of the attempt worktree"]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code, stdout: _, stderr } => SnapshotRefused { step: "git commit-tree", detail: join([to_string(value: exit_code), ": ", stderr], "") } + gunbc.compute.work_provider_local.ComputeExecRefused { reason } => SnapshotRefused { step: "git commit-tree", detail: join(["the launcher refused: ", reason], "") } gunbc.compute.work_provider_local.ComputeExecOk { stdout: commit, stderr: _ } => if trim(s: commit) == "" { SnapshotRefused { step: "git commit-tree", detail: "printed no commit id" } } else { SnapshotResolved { commit: trim(s: commit) } } } @@ -74,6 +77,7 @@ type TreeListing fn list_tree(instance: HostDashboardInstance, worktree: String, commit: String) -> TreeListing { match test_exec(instance: instance, workdir: worktree, args: ["ls-tree", "-r", "--name-only", commit]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code, stdout: _, stderr } => TreeListingRefused { detail: join(["git ls-tree exited ", to_string(value: exit_code), ": ", stderr], "") } + gunbc.compute.work_provider_local.ComputeExecRefused { reason } => TreeListingRefused { detail: join(["the launcher refused: ", reason], "") } gunbc.compute.work_provider_local.ComputeExecOk { stdout, stderr: _ } => TreeListed { paths: filter(stdout.split(sep: "\n"), p => trim(s: p) != "") } } } @@ -81,6 +85,7 @@ fn list_tree(instance: HostDashboardInstance, worktree: String, commit: String) fn show_file(instance: HostDashboardInstance, worktree: String, commit: String, path: String) -> String? { match test_exec(instance: instance, workdir: worktree, args: ["show", join([commit, ":", path], "")]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code: _, stdout: _, stderr: _ } => none + gunbc.compute.work_provider_local.ComputeExecRefused { reason: _ } => none gunbc.compute.work_provider_local.ComputeExecOk { stdout, stderr: _ } => Present { value: stdout } } } diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index 619793ffe2c..9195ef38308 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -152,9 +152,11 @@ type ToolchainObservation fn compute_observe_toolchain(instance: HostDashboardInstance) -> ToolchainObservation { match compute_exec(program: compute_program(instance: instance, cap: RustcCapability), workdir: instance.repo_root as String, args: ["--version"]) { ComputeExecFailed { exit_code, stdout: _, stderr } => ToolchainObservationRefused { detail: join(["rustc --version exited ", to_string(value: exit_code), ": ", stderr], "") } + ComputeExecRefused { reason } => ToolchainObservationRefused { detail: join(["rustc --version was refused by the launcher: ", reason], "") } ComputeExecOk { stdout: rustc_line, stderr: _ } => match compute_exec(program: sccache_installed_binary_path, workdir: instance.repo_root as String, args: ["--version"]) { ComputeExecFailed { exit_code, stdout: _, stderr } => ToolchainObservationRefused { detail: join(["sccache --version exited ", to_string(value: exit_code), ": ", stderr], "") } + ComputeExecRefused { reason } => ToolchainObservationRefused { detail: join(["sccache --version was refused by the launcher: ", reason], "") } ComputeExecOk { stdout: cache_line, stderr: _ } => if trim(s: rustc_line) == "" || trim(s: cache_line) == "" { ToolchainObservationRefused { detail: "rustc or sccache printed an empty version line" } @@ -174,6 +176,7 @@ type SubjectResolution fn compute_resolve_subject(instance: HostDashboardInstance, commit: String) -> SubjectResolution { match compute_exec(program: compute_program(instance: instance, cap: GitWorkspaceCapability), workdir: instance.repo_root as String, args: ["show", "-s", "--format=%T", commit]) { ComputeExecFailed { exit_code, stdout: _, stderr } => SubjectResolutionRefused { detail: join(["git show ", commit, " exited ", to_string(value: exit_code), ": ", stderr], "") } + ComputeExecRefused { reason } => SubjectResolutionRefused { detail: join(["git show was refused by the launcher: ", reason], "") } ComputeExecOk { stdout, stderr: _ } => match git_object_id_from_untagged_hex(hex: trim(s: stdout)) { Absent => SubjectResolutionRefused { detail: join(["git show printed no tree object id for ", commit, ": ", trim(s: stdout)], "") } @@ -186,6 +189,7 @@ fn compute_ensure_dir(instance: HostDashboardInstance, path: String) -> Bool { match compute_exec(program: compute_program(instance: instance, cap: AttemptStateDirectoryCapability), workdir: instance.repo_root as String, args: ["-d", "-m", "0755", path]) { ComputeExecOk { stdout: _, stderr: _ } => true ComputeExecFailed { exit_code: _, stdout: _, stderr: _ } => false + ComputeExecRefused { reason: _ } => false } } @@ -380,6 +384,7 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden let standing = summary_format_standing(version_line: match compute_exec(program: systemd_run, workdir: layout.checkout, args: ["--version"]) { ComputeExecOk { stdout: v, stderr: _ } => v ComputeExecFailed { exit_code: _, stdout: _, stderr: _ } => "" + ComputeExecRefused { reason: _ } => "" }) match systemd_run_user_wait_command( unit: compute_unit_name(identity_hex: identity_hex), @@ -409,6 +414,7 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden summary: systemd_run_wait_summary(launcher: launcher, standing: standing, stderr: match r { ComputeExecOk { stdout: _, stderr } => stderr ComputeExecFailed { exit_code: _, stdout: _, stderr } => stderr + ComputeExecRefused { reason } => reason }), } } @@ -751,9 +757,11 @@ fn compute_return_outputs(instance: HostDashboardInstance, layout: ComputeLayout let dst = join([layout.store_dir, "/", compute_basename(path: rel)], "") match compute_exec(program: compute_program(instance: instance, cap: GitWorkspaceCapability), workdir: layout.checkout, args: ["hash-object", src]) { ComputeExecFailed { exit_code: _, stdout: _, stderr } => OutputsMismatch { detail: join(["declared output ", rel, " could not be hashed: ", stderr], "") } + ComputeExecRefused { reason } => OutputsMismatch { detail: join(["declared output ", rel, " could not be hashed, the launcher refused: ", reason], "") } ComputeExecOk { stdout, stderr: _ } => match compute_exec(program: compute_program(instance: instance, cap: AttemptStateDirectoryCapability), workdir: layout.checkout, args: ["-m", "0755", src, dst]) { ComputeExecFailed { exit_code: _, stdout: _, stderr } => OutputsMismatch { detail: join(["declared output ", rel, " could not be stored: ", stderr], "") } + ComputeExecRefused { reason } => OutputsMismatch { detail: join(["declared output ", rel, " could not be stored, the launcher refused: ", reason], "") } ComputeExecOk { stdout: _, stderr: _ } => OutputsReturned { outputs: concat(outputs, [WorkOutput { declared: rel, blob: trim(s: stdout), store_path: dst }]) } } @@ -1732,6 +1740,12 @@ fn compute_run_leased( summary: compute_unspawned_summary(), gate: opened, } + ComputeExecRefused { reason } => + ComputeAttempt { + outcome: WorkRefusedByInfrastructure { identity: identity, cause: CheckoutUnavailable { detail: join(["git worktree add was refused by the launcher: ", reason], "") } }, + summary: compute_unspawned_summary(), + gate: opened, + } ComputeExecOk { stdout: _, stderr: _ } => match compute_begin_launch(layout: layout, gate: opened) { AttemptGateRefused { detail: d } => @@ -1759,6 +1773,7 @@ fn compute_spawn_and_collect( let log = Filesystem.Write(path: layout.log_path, content: match run { ComputeExecOk { stdout, stderr } => join([stdout, stderr], "") ComputeExecFailed { exit_code: _, stdout, stderr } => join([stdout, stderr], "") + ComputeExecRefused { reason } => reason }) let summary = unit_run.summary ComputeAttempt { diff --git a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag index 5042542abeb..8899e2f61de 100644 --- a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag +++ b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag @@ -78,7 +78,7 @@ import extdeps.systemd.journalctl import extdeps.exec.command { argv_words } import extdeps.systemd.systemd_run { SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, - systemd_run_transient_unit_command, systemd_run_command_argument_words, + systemd_run_transient_unit_command, systemd_run_transient_retained_unit_command, systemd_run_command_argument_words, } import gunbc.runner_microvm_lifecycle { CgroupSubtreeReadback, CgroupSubtreeEmpty, CgroupSubtreePopulated, CgroupSubtreeReadFailed, @@ -1172,7 +1172,9 @@ fn run_jailer_in_cell(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, recei match attempt_jailer_at_launch(receipt: receipt) { Absent => VmmStartFailed { unit: unit, detail: "lease expired during staging or launch clock is unreadable" } Present { value: jailer } => - match systemd_run_transient_unit_command( + // RunTransientRetained exists so the terminal state stays observable: the invocation must + // NOT carry --collect, so this builds through the retained shape, not the collecting one. + match systemd_run_transient_retained_unit_command( unit: unit, properties: concat([ runner_microvm_attempt_slice_property(attempt: attempt), diff --git a/dag/gunbc/systemd_run_transient.dag b/dag/gunbc/systemd_run_transient.dag index 6f06586a61a..c8ad5a42f60 100644 --- a/dag/gunbc/systemd_run_transient.dag +++ b/dag/gunbc/systemd_run_transient.dag @@ -219,7 +219,9 @@ fn argv_exact_token_equal(left: List, right: List) -> Bool { if any(left, w => string_contains(s: w, pattern: "\n")) || any(right, w => string_contains(s: w, pattern: "\n")) { false } else { - join(left, separator: "\n") == join(right, separator: "\n") + // Length first: two lists that join to the same text with different cardinalities are NOT the + // same argv, and the join alone would hide it whenever a word carries an embedded separator. + (length(xs: left) == length(xs: right)) && (join(left, separator: "\n") == join(right, separator: "\n")) } } diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 471a1cc4aad..8c11ff812ee 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -9,6 +9,7 @@ import extdeps.systemd.systemd_run { SystemdSetenvBinding, RunUserManager, RunScope, Wait, Quiet, Pipe, Collect, EndOfOptions, SetEnv, systemd_run_option_words, systemd_run_transient_wait_unit_command, systemd_run_user_wait_command, + systemd_run_transient_unit_command, systemd_run_transient_retained_unit_command, systemd_run_system_scope_command, } import gunbc.systemd_run_transient { systemd_run_transient_wait_operation_argv_matches_authority, @@ -75,6 +76,82 @@ test fn a_property_value_the_wire_cannot_carry_is_refused() -> Bool { } } +// ── BYTE IDENTITY, EVERY BUILDER: one control per builder shape, each pinning the words the +// retired glued builders emitted, in the order they emitted them. A control per builder is the +// point: the wait fixture alone proved one shape and the other builders drifted silently. + +// The collecting transient: --unit, --collect, the properties, then -- (the old order). +test fn the_collecting_transient_builder_emits_unit_collect_then_properties() -> Bool { + match systemd_run_transient_unit_command( + unit: "fci1-order.service", + properties: [SystemdRunProperty { property: MemoryMaxProperty, value: "17179869184" as NonEmptyStr }], + command_argv: ["/bin/true"], + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => + argv_exact_token_equal( + left: argv_words(command: command), + right: [ + "systemd-run", "--unit=fci1-order.service", "--collect", "--property=MemoryMax=17179869184", "--", "/bin/true", + ], + ) + } +} + +// The retained transient: --unit, the properties, then --, with NO --collect — the terminal state +// stays observable, which is why the retained variant exists. +test fn the_retained_transient_builder_omits_collect() -> Bool { + match systemd_run_transient_retained_unit_command( + unit: "fci1-retained.service", + properties: [SystemdRunProperty { property: MemoryMaxProperty, value: "17179869184" as NonEmptyStr }], + command_argv: ["/bin/true"], + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => + argv_exact_token_equal( + left: argv_words(command: command), + right: ["systemd-run", "--unit=fci1-retained.service", "--property=MemoryMax=17179869184", "--", "/bin/true"], + ) + } +} + +// The user wait builder: --wait --pipe --collect lead, then --user --unit, short properties, the +// setenv bindings, then --. +test fn the_user_wait_builder_renders_the_old_order() -> Bool { + match systemd_run_user_wait_command( + unit: "fci1-user.service", + properties: [SystemdRunProperty { property: MemoryMaxProperty, value: "17179869184" as NonEmptyStr }], + setenv_bindings: [SystemdSetenvBinding { name: "A" as NonEmptyStr, value: "c" as NonEmptyStr }], + command_argv: ["/bin/true"], + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => + argv_exact_token_equal( + left: argv_words(command: command), + right: [ + "systemd-run", "--wait", "--pipe", "--collect", "--user", "--unit=fci1-user.service", + "--property=MemoryMax=17179869184", "--setenv=A=c", "--", "/bin/true", + ], + ) + } +} + +// The scope builder: --user --scope lead (the account drop runs under the user manager), long +// properties, then --. +test fn the_scope_builder_leads_with_user_and_scope() -> Bool { + match systemd_run_system_scope_command( + properties: [SystemdRunProperty { property: MemoryMaxProperty, value: "17179869184" as NonEmptyStr }], + command_argv: ["/bin/true"], + ) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => + argv_exact_token_equal( + left: argv_words(command: command), + right: ["systemd-run", "--user", "--scope", "--property=MemoryMax=17179869184", "--", "/bin/true"], + ) + } +} + // A unit name is one line and never carries a slash (systemd.unit(5)); the projection refuses both. test fn a_unit_name_the_wire_cannot_carry_is_refused() -> Bool { match systemd_run_transient_wait_unit_command( diff --git a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag index 379b04ffc82..358b3d7f49b 100644 --- a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag +++ b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag @@ -8,7 +8,7 @@ import extdeps.tools.sleep { sleep_delay_seconds_second_carrier_projection } import extdeps.systemd { SliceProperty, SystemdUnitProperty, ActiveState, systemd_unit_property_wire, SystemdSliceCgroupPath, SliceCgroupPath, SliceNameNotASlice, systemd_slice_cgroup_path } import extdeps.exec.command { argv_words } import extdeps.systemd.systemd_run { - SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command, + SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command, systemd_run_transient_retained_unit_command, systemd_run_command_argument_words, } import extdeps.linux.cgroup_v2 { cgroup_v2_mount_point, cgroup_v2_child_path } @@ -419,13 +419,15 @@ fn start_with_invocation(unit: NonEmptyStr, invocation: String, sleep_for: Strin } fn start_retained_payload(unit: NonEmptyStr, properties: List, command_argv: List) -> Bool { - match systemd_run_transient_unit_command(unit: unit, properties: properties, command_argv: command_argv) { + // Retained means the terminal state stays observable: the retained builder, and the argument + // tail only — the transport owns the "systemd-run" head. + match systemd_run_transient_retained_unit_command(unit: unit, properties: properties, command_argv: command_argv) { SystemdRunCommandRefused { reason: why } => { let _ = why false } SystemdRunCommandReady { command: command } => - systemd.SystemdRun.RunTransientRetained(command_argv: argv_words(command: command)).success + systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)).success } } From 56affb0c14d2ba97d646be9e13968eec366f2124 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 22:12:58 +0000 Subject: [PATCH 11/46] =?UTF-8?q?review=20fix=206:=20typed-builder=20wet?= =?UTF-8?q?=20claims=20on=20LocalRepoWetLane=20=E2=80=94=20retained=20stay?= =?UTF-8?q?s=20observable=20(loaded)=20vs=20collected=20twin=20(not-found)?= =?UTF-8?q?=20vs=20waited=20exact=20exit=2086,=20through=20real=20systemd-?= =?UTF-8?q?run?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/ci/ci_layer_roots.dag | 5 ++ .../claim/systemd/typed_builder_wet_test.dag | 77 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 dag/test/claim/systemd/typed_builder_wet_test.dag diff --git a/dag/gunbc/ci/ci_layer_roots.dag b/dag/gunbc/ci/ci_layer_roots.dag index eb9643e25a0..a8f6c01d320 100644 --- a/dag/gunbc/ci/ci_layer_roots.dag +++ b/dag/gunbc/ci/ci_layer_roots.dag @@ -667,6 +667,11 @@ data witness_exclusion_frontier: List = [ classification: BinWitnessWet, reason: excl_bin_wet_reason, dissolution: excl_bin_wet_dissolve}, + WitnessExclusionRow { + pattern: "typed_builder_wet_test.dag", + classification: LocalRepoWetLane, + reason: "The typed systemd-run builders' DESIGN 3 pairing: these three claims execute the projected words through real systemd-run on the lane's host (the same host whose microVM lifecycle receipts run real transient units under the system manager, so a usable manager is evidenced, not assumed) and assert the behavior the --collect decision buys from the wire: a retained unit that failed is still known to the manager (loaded), the collected twin is gone (not-found), and the waited invocation reports the child's exact exit (86). Hermetic discovery correctly refuses these (no mock_response fabricates a manager), so they run on the local-repo wet lane only.", + dissolution: unbound_dissolution(description: "when the transports' hermetic mocks model a real manager faithfully enough that the --collect discrimination and the exact-exit pass can be asserted against recorded fixtures without losing the pairing (a mock that always says success would erase exactly the evidence these claims exist for), these re-enroll as ordinary hermetic rows and this exclusion deletes")}, WitnessExclusionRow { pattern: "self_host_logic_behavioral_witness_test.dag", classification: LocalRepoWetLane, diff --git a/dag/test/claim/systemd/typed_builder_wet_test.dag b/dag/test/claim/systemd/typed_builder_wet_test.dag new file mode 100644 index 00000000000..4c5d37ef0b7 --- /dev/null +++ b/dag/test/claim/systemd/typed_builder_wet_test.dag @@ -0,0 +1,77 @@ +module test.claim.systemd.typed_builder_wet + +import std.types { String, NonEmptyStr, Bool, List, Int } +import std.resources { Network } +import extdeps.systemd.systemd_run { + SystemdRunCommandReady, + SystemdRunCommandRefused, + systemd_run_transient_unit_command, + systemd_run_transient_retained_unit_command, + systemd_run_transient_wait_unit_command, + systemd_run_command_argument_words, +} + +// THE TYPED BUILDERS, EXECUTED — the DESIGN 3 pairing the typed-argv cutover owes. The hermetic +// witness test.claim.fabric.systemd_run_transient_wait_witness_test pins the projected words; it +// establishes nothing about whether real systemd-run accepts them and behaves. These claims run +// the typed builders' words through the real transports on the wet lane's host — the host whose +// microVM lifecycle receipts already execute real transient units under the system manager +// (systemd is that host's PID 1; the lane's runner classes carry it) — and assert the behavior +// the --collect decision is about. The user-manager question is answered by that evidence: these +// claims run against the system manager, which the lane demonstrably has; no --user claim is +// enrolled, and if a user-manager claim is ever needed the controlled target is the same host +// under `sudo systemd-run --user` with a private bus assertion, not a hopeful --user. +// +// THE DISCRIMINATION IS ONE-HOT ON --collect, EXECUTED: the same failing command, run through the +// collecting builder and through the retained builder, lands in different terminal states — the +// collected unit is gone (not-found), the retained unit is still known to the manager (loaded). +// That is the evidence the retained shape exists to buy, now taken from the wire rather than +// asserted from the projection. + +// The retained invocation: the unit FAILED (exit 86) and is still known to the manager, because +// no --collect was passed. This is the claim that proves the terminal state stays observable. +test fn a_real_retained_transient_is_still_known_after_a_failing_exit() -> Bool + uses net: Network +{ + let unit = "fci1-typed-argv-wet-retained.service" as NonEmptyStr + match systemd_run_transient_retained_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => { + let started = systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)) + let load_state = systemd.Systemctl.ShowLoadState(unit: unit) + started.success && (trim(s: load_state.value) == "loaded") + } + } +} + +// The collecting invocation, SAME failing command: the manager no longer knows the unit. Red +// means the collected and retained shapes stopped discriminating on the wire. +test fn the_collected_twin_of_that_unit_is_gone() -> Bool + uses net: Network +{ + let unit = "fci1-typed-argv-wet-collected.service" as NonEmptyStr + match systemd_run_transient_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => { + let started = systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)) + let load_state = systemd.Systemctl.ShowLoadState(unit: unit) + started.success && (trim(s: load_state.value) == "not-found") + } + } +} + +// The waited invocation reports the child's exact exit through the typed projection: the +// pre-cutover contract this cutover had to preserve byte-for-byte in behavior, taken from the +// wire. 86 is the same sentinel the hermetic fixture pins. +test fn the_waited_transient_reports_the_exact_exit() -> Bool + uses net: Network +{ + let unit = "fci1-typed-argv-wet-waited.service" as NonEmptyStr + match systemd_run_transient_wait_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { + SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandReady { command: command } => { + let waited = systemd.SystemdRun.RunTransientAndWait(command_argv: systemd_run_command_argument_words(command: command)) + waited.exit_code == 86 + } + } +} From 4f0c70ab66dd26d3cbae471929969d4a0612f9a2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 23:01:04 +0000 Subject: [PATCH 12/46] Move in-body comments to module-item grain (declaration errors: body annotations are not modeled at fn grain) --- dag/extdeps/systemd/systemd_run.dag | 14 +++++++------- dag/gunbc/compute/work_provider_local.dag | 2 -- .../runner/runner_microvm_lifecycle_realize.dag | 2 -- dag/gunbc/systemd_run_transient.dag | 12 ++++-------- .../runner_microvm_lifecycle_wet_receipt_test.dag | 4 ++-- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index c5356caf41f..95db730a57a 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -49,9 +49,9 @@ fn systemd_run_program() -> ProgramIdentity = uncataloged_program(invocation: "s // list it could have glued together itself. type SystemdSummaryPrintingWait { command: ArgvCommand } +// compute_exec receives the program separately, so these are the argument tail; the full +// argv_words would double the program head on the compute side too. fn systemd_summary_printing_wait_argv(w: SystemdSummaryPrintingWait) -> List { - // compute_exec receives the program separately, so these are the argument tail; the full - // argv_words would double the program head on the compute side too. systemd_run_command_argument_words(command: w.command) } @@ -252,9 +252,9 @@ fn systemd_run_user_wait_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { - // The pre-cutover scope builder led with --user --scope (the caller drops to the account's user - // manager); the typed builder renders the same two flags in the same order. systemd_run_command_of(options: list_append(left: [RunUserManager, RunScope], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) } @@ -262,10 +262,10 @@ fn systemd_run_system_scope_command(properties: List, comman // COMPOSES the option words through the one projection rather than re-deriving them; if this fold // and the transport template ever disagree about word order, // systemd_run_transient_operation_argv_matches_authority goes red. +// Word order is byte identity against the retired builder: --unit, --collect, the properties, +// then --. The retained variant below is the same builder WITHOUT the --collect arm, because +// RunTransientRetained exists precisely so the terminal state stays observable. fn systemd_run_transient_unit_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { - // Word order is byte identity against the retired builder: --unit, --collect, the properties, - // then --. The retained variant below is the same builder WITHOUT the --collect arm, because - // RunTransientRetained exists precisely so the terminal state stays observable. systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }, Collect], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) } diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index 9195ef38308..4e191af11d0 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -396,8 +396,6 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden command_argv: argv, ) { SystemdRunCommandRefused { reason: why } => - // A refusal means nothing ran: there is no exit code to fabricate, so the observation is - // a typed refusal carrying the builder's reason. ComputeUnitRun { exec: ComputeExecRefused { reason: why }, summary: SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } }, diff --git a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag index 8899e2f61de..b70026bcaed 100644 --- a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag +++ b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag @@ -1172,8 +1172,6 @@ fn run_jailer_in_cell(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, recei match attempt_jailer_at_launch(receipt: receipt) { Absent => VmmStartFailed { unit: unit, detail: "lease expired during staging or launch clock is unreadable" } Present { value: jailer } => - // RunTransientRetained exists so the terminal state stays observable: the invocation must - // NOT carry --collect, so this builds through the retained shape, not the collecting one. match systemd_run_transient_retained_unit_command( unit: unit, properties: concat([ diff --git a/dag/gunbc/systemd_run_transient.dag b/dag/gunbc/systemd_run_transient.dag index c8ad5a42f60..d4c5b1ca6e6 100644 --- a/dag/gunbc/systemd_run_transient.dag +++ b/dag/gunbc/systemd_run_transient.dag @@ -65,10 +65,10 @@ fn systemd_run_transient_outcome_from_result(success: Bool, stdout: String, stde } } +// The transport owns the program head (argv: ["systemd-run", command_argv]); the words handed +// to it are the argument tail, not the full argv — passing argv_words here would run +// 'systemd-run systemd-run --unit=...'. fn systemd_run_transient_local(command: ArgvCommand) -> SystemdRunTransientOutcome { - // The transport owns the program head (argv: ["systemd-run", command_argv]); the words handed - // to it are the argument tail, not the full argv — passing argv_words here would run - // 'systemd-run systemd-run --unit=...'. let result = systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)) systemd_run_transient_outcome_from_result(success: result.success, stdout: result.stdout, stderr: result.stderr) } @@ -126,8 +126,8 @@ fn systemd_run_transient_read( } } +// The transport owns the program head; hand it the argument tail (see systemd_run_transient_local). fn systemd_run_transient_wait_local(command: ArgvCommand) -> SystemdRunTransientWaitOutcome { - // The transport owns the program head; hand it the argument tail (see systemd_run_transient_local). let result = systemd.SystemdRun.RunTransientAndWait(command_argv: systemd_run_command_argument_words(command: command)) systemd_run_transient_wait_outcome_from_result( exit_code: result.exit_code, @@ -198,10 +198,6 @@ fn systemd_run_transient_wait_read( match transport { LocalShell => systemd_run_transient_wait_local(command: command) SshShell { ssh_host: h } => systemd_run_transient_wait_ssh(host: h, command: command) - // The boundary this arm refuses through is exact: a remote exit status cannot be - // distinguished from ssh's own 255 on this result, so a waited transient over FleetSsh - // refuses at the transport rather than fabricating an exit code (restored from the - // pre-cutover boundary). FleetSsh { target: _, context: _ } => SystemdRunTransientWaitTransportRefused { reason: "systemd-run transient wait refused FleetSsh until exact remote status is distinguished from transport status", } diff --git a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag index 358b3d7f49b..74098d610d3 100644 --- a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag +++ b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag @@ -418,9 +418,9 @@ fn start_with_invocation(unit: NonEmptyStr, invocation: String, sleep_for: Strin ) } +// Retained means the terminal state stays observable: the retained builder, and the argument +// tail only — the transport owns the "systemd-run" head. fn start_retained_payload(unit: NonEmptyStr, properties: List, command_argv: List) -> Bool { - // Retained means the terminal state stays observable: the retained builder, and the argument - // tail only — the transport owns the "systemd-run" head. match systemd_run_transient_retained_unit_command(unit: unit, properties: properties, command_argv: command_argv) { SystemdRunCommandRefused { reason: why } => { let _ = why From e2dd286e1f3f26da37c752daf8361996480f7838 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 4 Oct 2026 23:51:38 +0000 Subject: [PATCH 13/46] witness test: use the merged SystemdUnitProperty variant MemoryMax (property union with main renamed the field's type) --- .../fabric/systemd_run_transient_wait_witness_test.dag | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 8c11ff812ee..a93fc1a0c3c 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -84,7 +84,7 @@ test fn a_property_value_the_wire_cannot_carry_is_refused() -> Bool { test fn the_collecting_transient_builder_emits_unit_collect_then_properties() -> Bool { match systemd_run_transient_unit_command( unit: "fci1-order.service", - properties: [SystemdRunProperty { property: MemoryMaxProperty, value: "17179869184" as NonEmptyStr }], + properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["/bin/true"], ) { SystemdRunCommandRefused { reason: _ } => false @@ -103,7 +103,7 @@ test fn the_collecting_transient_builder_emits_unit_collect_then_properties() -> test fn the_retained_transient_builder_omits_collect() -> Bool { match systemd_run_transient_retained_unit_command( unit: "fci1-retained.service", - properties: [SystemdRunProperty { property: MemoryMaxProperty, value: "17179869184" as NonEmptyStr }], + properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["/bin/true"], ) { SystemdRunCommandRefused { reason: _ } => false @@ -120,7 +120,7 @@ test fn the_retained_transient_builder_omits_collect() -> Bool { test fn the_user_wait_builder_renders_the_old_order() -> Bool { match systemd_run_user_wait_command( unit: "fci1-user.service", - properties: [SystemdRunProperty { property: MemoryMaxProperty, value: "17179869184" as NonEmptyStr }], + properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], setenv_bindings: [SystemdSetenvBinding { name: "A" as NonEmptyStr, value: "c" as NonEmptyStr }], command_argv: ["/bin/true"], ) { @@ -140,7 +140,7 @@ test fn the_user_wait_builder_renders_the_old_order() -> Bool { // properties, then --. test fn the_scope_builder_leads_with_user_and_scope() -> Bool { match systemd_run_system_scope_command( - properties: [SystemdRunProperty { property: MemoryMaxProperty, value: "17179869184" as NonEmptyStr }], + properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["/bin/true"], ) { SystemdRunCommandRefused { reason: _ } => false From e230ee01746ec762d97c7f600606b5bb30bce851 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 00:09:26 +0000 Subject: [PATCH 14/46] user-wait byte-identity control: the retired builder emitted -p and the value as two words (blob 71a9a1c5d3~1 list_push(list_push(acc, -p), p)); the 11-word cardinality control confirms the shape --- .../claim/fabric/systemd_run_transient_wait_witness_test.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index a93fc1a0c3c..03ea2b966a0 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -130,7 +130,7 @@ test fn the_user_wait_builder_renders_the_old_order() -> Bool { left: argv_words(command: command), right: [ "systemd-run", "--wait", "--pipe", "--collect", "--user", "--unit=fci1-user.service", - "--property=MemoryMax=17179869184", "--setenv=A=c", "--", "/bin/true", + "-p", "MemoryMax=17179869184", "--setenv=A=c", "--", "/bin/true", ], ) } From 2723f94fafbc5101ba1938df35a060045d181fa5 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 00:51:53 +0000 Subject: [PATCH 15/46] argv_exact_token_equal: move the cardinality rationale to module grain (second floor parse refusal) --- dag/gunbc/systemd_run_transient.dag | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dag/gunbc/systemd_run_transient.dag b/dag/gunbc/systemd_run_transient.dag index d4c5b1ca6e6..8ca994a7763 100644 --- a/dag/gunbc/systemd_run_transient.dag +++ b/dag/gunbc/systemd_run_transient.dag @@ -208,15 +208,16 @@ fn systemd_run_transient_wait_read( } } +// Token comparison over words: exact and order-sensitive, and REFUSING any word that carries the // Token comparison over words: exact and order-sensitive, and REFUSING any word that carries the // token separator -- the comparison is injective only while no word contains it, and a word that -// does must refuse rather than compare (the split-words control pins that). +// does must refuse rather than compare (the split-words control pins that). Length first: two +// lists that join to the same text with different cardinalities are NOT the same argv, and the +// join alone would hide it whenever a word carries an embedded separator. fn argv_exact_token_equal(left: List, right: List) -> Bool { if any(left, w => string_contains(s: w, pattern: "\n")) || any(right, w => string_contains(s: w, pattern: "\n")) { false } else { - // Length first: two lists that join to the same text with different cardinalities are NOT the - // same argv, and the join alone would hide it whenever a word carries an embedded separator. (length(xs: left) == length(xs: right)) && (join(left, separator: "\n") == join(right, separator: "\n")) } } From a795eeabb5268ebd6b761c9196fe9d9b06480506 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 01:30:53 +0000 Subject: [PATCH 16/46] floor diagnostics at 2723f94faf: import std.algebra trim in the wet test; compute_spawn_and_collect's outcome match arms ComputeExecRefused as WorkCancelled (nothing was spawned) --- dag/gunbc/compute/work_provider_local.dag | 2 ++ dag/test/claim/systemd/typed_builder_wet_test.dag | 1 + 2 files changed, 3 insertions(+) diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index 4e191af11d0..d17a749c4ba 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -1778,6 +1778,8 @@ fn compute_spawn_and_collect( outcome: match run { ComputeExecFailed { exit_code, stdout: _, stderr: _ } => compute_failed_unit_outcome(summary: summary, identity: identity, exit_code: exit_code, op: op, granted: granted, grant_was_ceiling: grant_was_ceiling, log_path: layout.log_path) + ComputeExecRefused { reason } => + WorkCancelled { identity: identity, detail: join(["the unit launch was refused by the builder: ", reason], "") } ComputeExecOk { stdout: _, stderr: _ } => match compute_return_outputs(instance: instance, layout: layout, declared: work_declared_outputs(op: op)) { OutputsMismatch { detail } => WorkOutputMismatch { identity: identity, detail: detail } diff --git a/dag/test/claim/systemd/typed_builder_wet_test.dag b/dag/test/claim/systemd/typed_builder_wet_test.dag index 4c5d37ef0b7..f1c42b9f283 100644 --- a/dag/test/claim/systemd/typed_builder_wet_test.dag +++ b/dag/test/claim/systemd/typed_builder_wet_test.dag @@ -1,6 +1,7 @@ module test.claim.systemd.typed_builder_wet import std.types { String, NonEmptyStr, Bool, List, Int } +import std.algebra { trim } import std.resources { Network } import extdeps.systemd.systemd_run { SystemdRunCommandReady, From aae91acc07b283abf4b8eda719e9e5604c05f187 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 04:22:34 +0000 Subject: [PATCH 17/46] add the audit and supervisor provider-events path helpers main's metering module imports --- dag/gunbc/roadmap/roadmap_dispatch_actuator.dag | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag b/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag index f9eb5cb5da2..20220d60c27 100644 --- a/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag +++ b/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag @@ -1172,6 +1172,14 @@ fn dispatch_attempt_review_verdict_basename(head_sha: String, criterion_key: Str join([head_sha, ".", criterion_key, ".json"], "") } +fn dispatch_attempt_audit_events_path_for_instance(instance: HostDashboardInstance, node_id: RoadmapNodeId, attempt_key: String, head_sha: String) -> String { + join([dispatch_attempt_audit_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key), "/", head_sha, ".provider-events.jsonl"], "") +} + +fn dispatch_attempt_supervisor_events_path_for_instance(instance: HostDashboardInstance, node_id: RoadmapNodeId, attempt_key: String, turn: Int) -> String { + join([dispatch_attempt_supervisor_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key), "/t", to_string(value: turn), ".provider-events.jsonl"], "") +} + fn dispatch_attempt_review_events_path_for_instance(instance: HostDashboardInstance, node_id: RoadmapNodeId, attempt_key: String, criterion_key: String, head_sha: String) -> String { join([dispatch_attempt_review_dir_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key), "/", criterion_key, "@", head_sha, ".provider-events.jsonl"], "") } From 1d33e409ef09026a556c652a4196105a5d59b109 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 05:23:40 +0000 Subject: [PATCH 18/46] belt: take main's metering tick plumbing wholesale and transplant the three typed launch matches into its roster wrappers (imports: typed commands + dispatch_unit_exec_argv) --- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 201 ++++++++++++++------- 1 file changed, 140 insertions(+), 61 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index 8d518b8d802..caf21f63a26 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -28,15 +28,14 @@ import gunbc.roadmap.roadmap_review_function { } import gunbc.roadmap.roadmap_review_criteria { ReviewCriterion } import gunbc.roadmap.roadmap_goal_audit_role { goal_audit_criterion_key, goal_audit_approved, goal_audit_reconcile, goal_audit_brief } -import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } import gunbc.roadmap_dispatch_actuator { dispatch_events_projection_argv_at, dispatch_current_attempt_events_path_for_instance } import gunbc.roadmap_dispatch_actuator { dispatch_attempt_review_dir_for_instance, dispatch_attempt_review_verdict_path_for_instance, dispatch_attempt_review_verdict_basename, - dispatch_review_unit_name, dispatch_review_unit_command, dispatch_unit_exec_argv, + dispatch_review_unit_name, dispatch_review_unit_command, dispatch_attempt_audit_dir_for_instance, dispatch_attempt_audit_verdict_path_for_instance, dispatch_attempt_audit_verdict_basename, - dispatch_audit_unit_name, dispatch_audit_unit_command, dispatch_unit_exec_argv, - dispatch_attempt_supervisor_dir_for_instance, dispatch_supervisor_unit_name, dispatch_supervisor_unit_command, dispatch_unit_exec_argv, - dispatch_supervisor_attempt_identity, + dispatch_audit_unit_name, dispatch_audit_unit_command, + dispatch_attempt_supervisor_dir_for_instance, dispatch_supervisor_unit_name, dispatch_supervisor_unit_command, + dispatch_supervisor_attempt_identity, dispatch_unit_exec_argv, } import extdeps.git { shape_git_merge_base_args } import gunbc.roadmap_authority { @@ -116,7 +115,7 @@ import gunbc.roadmap_dispatch_actuator { DispatchLiveSession, DispatchSpawnCommands, DispatchSessionContainer, TmuxSessionContainer, SystemdUnitContainer, - dispatch_worker_unit_properties_for_plan, dispatch_worker_unit_command, dispatch_unit_exec_argv, + dispatch_worker_unit_properties_for_plan, dispatch_worker_unit_run, dispatch_attempt_unit_name, attempt_unit_observation, worker_process_from_attempt_unit, @@ -1346,15 +1345,10 @@ fn belt_session_container_create( match session_placement_begin_launch(stores: held.stores, grant: held.grant) { SessionLaunchRefused { detail } => ExecStepFailed { step: "session-launch-gate", detail: detail } SessionLaunchAdmitted { grant: launched } => - match dispatch_worker_unit_command(instance: instance, plan: plan, granted: launched.amount, process_bounds: session_process_bounds) { - SystemdRunCommandRefused { reason: why } => - ExecStepFailed { step: "harness-unit-start", detail: why } - SystemdRunCommandReady { command: command } => - belt_exec_steps( - steps: [BeltExecStep { step: "harness-unit-start", command: dispatch_unit_exec_argv(command: command, resolved_program: plan.systemd_run_program) }], - workdir: workdir, - ) - } + belt_exec_steps( + steps: [BeltExecStep { step: "harness-unit-start", command: dispatch_worker_unit_run(instance: instance, plan: plan, granted: launched.amount, process_bounds: session_process_bounds) }], + workdir: workdir, + ) } } } @@ -2573,57 +2567,57 @@ fn belt_supervisor_convene( match metering_roster_admit(instance: instance, node_id: nid, attempt_key: escalation.attempt_key, role: MeteredSupervisor { turn: escalation.turn }) { RosterAdmissionRefused { cause: roster_cause } => SpawnFailed { node_id: node.node as String, step: "supervisor-roster", detail: join(["the supervisor was not launched: ", roster_cause], ""), provider: provider_wire_label, launch: launch } RosterAdmitted => - match dispatch_supervisor_unit_command( - instance: instance, - node_id: nid, - attempt_key: escalation.attempt_key, - turn: escalation.turn, - worktree_path: worktree, - brief: belt_supervisor_convene_brief( - node: node, - escalation: escalation, - checkpoint: belt_supervisor_checkpoint_lines( - capture: belt_prior_attempt_capture(instance: instance, node_id: nid, attempt_key: escalation.attempt_key), - head_sha: revision as String, - history_line: belt_supervisor_history_line(instance: instance, node_id: nid, attempt_key: escalation.attempt_key, turn: escalation.turn), - ), - ), - ) { - SystemdRunCommandRefused { reason: why } => - SpawnFailed { - node_id: node.node as String, - step: "supervisor-unit-start", - detail: why, - provider: provider_wire_label, - launch: launch, - } - SystemdRunCommandReady { command: command } => - match belt_exec(cmd: dispatch_unit_exec_argv(command: command, resolved_program: systemd_run), workdir: worktree) { - ExecOk { stdout: _ } => - SupervisorConvened { - node_id: node.node as String, - unit: unit as String, - provider: provider_wire_label, - launch: launch, - } - ExecFailed { program: _, exit_code, stderr } => + match dispatch_supervisor_unit_command( + instance: instance, + node_id: nid, + attempt_key: escalation.attempt_key, + turn: escalation.turn, + worktree_path: worktree, + brief: belt_supervisor_convene_brief( + node: node, + escalation: escalation, + checkpoint: belt_supervisor_checkpoint_lines( + capture: belt_prior_attempt_capture(instance: instance, node_id: nid, attempt_key: escalation.attempt_key), + head_sha: revision as String, + history_line: belt_supervisor_history_line(instance: instance, node_id: nid, attempt_key: escalation.attempt_key, turn: escalation.turn), + ), + ), + ) { + SystemdRunCommandRefused { reason: why } => SpawnFailed { node_id: node.node as String, step: "supervisor-unit-start", - detail: join(["the supervisor unit could not be started (exit ", to_string(exit_code), "): ", stderr], ""), + detail: why, provider: provider_wire_label, launch: launch, } - ExecRefused { program: _, reason } => - SpawnFailed { - node_id: node.node as String, - step: "supervisor-unit-start", - detail: reason, - provider: provider_wire_label, - launch: launch, + SystemdRunCommandReady { command: command } => + match belt_exec(cmd: dispatch_unit_exec_argv(command: command, resolved_program: systemd_run), workdir: worktree) { + ExecOk { stdout: _ } => + SupervisorConvened { + node_id: node.node as String, + unit: unit as String, + provider: provider_wire_label, + launch: launch, + } + ExecFailed { program: _, exit_code, stderr } => + SpawnFailed { + node_id: node.node as String, + step: "supervisor-unit-start", + detail: join(["the supervisor unit could not be started (exit ", to_string(exit_code), "): ", stderr], ""), + provider: provider_wire_label, + launch: launch, + } + ExecRefused { program: _, reason } => + SpawnFailed { + node_id: node.node as String, + step: "supervisor-unit-start", + detail: reason, + provider: provider_wire_label, + launch: launch, + } } } - } } } } @@ -3368,6 +3362,7 @@ type BeltTickReceipt { commit_pass: BeltPassOutcome, verify_pass: BeltPassOutcome, publish_pass: BeltPassOutcome, + metering_pass: BeltPassOutcome, } type BeltTickReceiptRead @@ -3390,7 +3385,7 @@ type BeltTickReceiptRead // DispatchStateHostShared, so blue and green resolve it to ONE path and a receipt there would // answer for two instances. A note describing a home the implementation did not and could not // safely use is the stale-authority class of DESIGN section 3. -data belt_tick_receipt_schema: String = "roadmap-belt-tick-receipt/v5" +data belt_tick_receipt_schema: String = "roadmap-belt-tick-receipt/v6" // THE RECEIPT HAD NO WRITABLE HOME AND HAS NEVER BEEN WRITTEN ON ANY INSTANCE. The path was // /belt-last-tick.json; every instance_root on srv1 is root:root 0755 while the belt @@ -3549,6 +3544,9 @@ fn belt_tick_receipt_decode(raw: String) -> BeltTickReceiptRead { BeltTickExecutionUnreadable { reason: r } => BeltTickReceiptUnreadable { reason: r } BeltTickExecutionDecoded { execution } => { + match belt_pass_outcome_from_member(doc: doc, key: "metering_pass") { + BeltPassOutcomeUnreadable { reason: r } => BeltTickReceiptUnreadable { reason: r } + BeltPassOutcomeDecoded { outcome: metering_pass } => { let receipt = BeltTickReceipt { observed_at: observed_at, execution: execution, @@ -3558,6 +3556,7 @@ fn belt_tick_receipt_decode(raw: String) -> BeltTickReceiptRead { commit_pass: commit_pass, verify_pass: verify_pass, publish_pass: publish_pass, + metering_pass: metering_pass, } match json_object_unique_member(v: doc, key: "receipt_identity") { JsonMemberFound { value: JsonString { value: claimed } } => @@ -3576,6 +3575,8 @@ fn belt_tick_receipt_decode(raw: String) -> BeltTickReceiptRead { BeltTickReceiptUnreadable { reason: "belt tick receipt receipt_identity is missing" } } } + } +} } } } @@ -3713,13 +3714,14 @@ fn belt_tick_receipt_identity_text(receipt: BeltTickReceipt) -> String { belt_pass_outcome_key(o: receipt.teardown_pass), "|", belt_pass_outcome_key(o: receipt.commit_pass), "|", belt_pass_outcome_key(o: receipt.verify_pass), "|", - belt_pass_outcome_key(o: receipt.publish_pass), + belt_pass_outcome_key(o: receipt.publish_pass), "|", + belt_pass_outcome_key(o: receipt.metering_pass), ], "") } fn belt_tick_receipt_identity_hex(receipt: BeltTickReceipt) -> String { content_hash_tagged_structural( - tag: "roadmap-belt-tick-receipt-v4" as NonEmptyStr, + tag: "roadmap-belt-tick-receipt-v5" as NonEmptyStr, payload: content_hash_atom(value: belt_tick_receipt_identity_text(receipt: receipt) as NonEmptyStr), ).digest as String } @@ -3797,6 +3799,7 @@ fn belt_tick_receipt_json_value(receipt: BeltTickReceipt) -> JsonValue { json_kv(key: "commit_pass", value: belt_pass_outcome_json(o: receipt.commit_pass)), json_kv(key: "verify_pass", value: belt_pass_outcome_json(o: receipt.verify_pass)), json_kv(key: "publish_pass", value: belt_pass_outcome_json(o: receipt.publish_pass)), + json_kv(key: "metering_pass", value: belt_pass_outcome_json(o: receipt.metering_pass)), ]) } @@ -3933,6 +3936,7 @@ type BeltTickResult { publish_outcomes: List, review_outcomes: List, audit_outcomes: List, + metering_outcomes: List, } fn belt_tick_empty_reconcile(state: SpawnMode) -> BeltReconcile { @@ -4240,6 +4244,7 @@ fn belt_tick_for_instance( publish_outcomes: [], review_outcomes: [], audit_outcomes: [], + metering_outcomes: [], } TickPassesRun => { let commit_outcomes = belt_commit_attempts_for_instance(instance: instance) @@ -4257,6 +4262,7 @@ fn belt_tick_for_instance( publish_outcomes: belt_publish_attempts_for_instance(instance: instance), review_outcomes: review_outcomes, audit_outcomes: audit_outcomes, + metering_outcomes: belt_metering_attempts_for_instance(instance: instance), } } } @@ -4500,6 +4506,7 @@ fn belt_run_once_in( publish_outcomes: belt_publish_attempts_for_instance(instance: instance), review_outcomes: review_outcomes, audit_outcomes: audit_outcomes, + metering_outcomes: belt_metering_attempts_for_instance(instance: instance), } } RoadmapAuthorityProjected { doc: doc, accepted: _, withheld: _ } => @@ -6986,6 +6993,74 @@ fn belt_commit_outcome_persist( } } +// THE PER-ATTEMPT METERING RECORD (gunbc.roadmap_attempt_metering). Every attempt in the workflow +// ledger is entered in the metering attempt index and, once its worker is terminal, metered against +// its open roster -- a Provisional record, rewritten each tick so later reviewer, auditor and +// supervisor rounds and late seat releases are folded in. An indexed attempt that has LEFT the ledger +// can no longer be spawned for, because every harness spawner iterates that ledger; it is sealed and +// metered against the seal, which is the only route to a Final record. The pass runs after review +// and audit, and its outcomes are the tick receipt's metering_pass. +fn belt_metering_for_attempt( + instance: HostDashboardInstance, + progress: WorkflowAttemptProgress, +) -> MeteringPersist { + match metering_attempt_index_admit(instance: instance, node_id: progress.node_id, attempt_key: progress.attempt_key) { + Present { value: e } => MeteringPersistRefused { cause: join(["the metering attempt index could not be written: ", e], "") } + Absent => + if belt_attempt_worker_terminal(provider: progress.provider, evidence: progress.process) { + metering_persist_for_attempt(instance: instance, node_id: progress.node_id as RoadmapNodeId, attempt_key: progress.attempt_key, departed: false) + } else { + MeteringNotDue + } + } +} + +fn belt_metering_departed( + instance: HostDashboardInstance, + attempts: List, +) -> List { + match metering_attempt_index(instance: instance) { + MeteredAttemptsRefused { cause } => [MeteringPersistRefused { cause: join(["the metering attempt index is unreadable: ", cause], "") }] + MeteredAttemptsObserved { attempts: indexed } => + flat_map(indexed, a => + if any(attempts, p => p.node_id == a.node_id && p.attempt_key == a.attempt_key) { + [] as List + } else { + [metering_persist_for_attempt(instance: instance, node_id: a.node_id as RoadmapNodeId, attempt_key: a.attempt_key, departed: true)] + }) + } +} + +fn belt_metering_attempts_for_instance( + instance: HostDashboardInstance, +) -> List { + match belt_workflow_attempts_observe_for_instance(instance: instance) { + WorkflowAttemptsRefused { reason } => + [MeteringPersistRefused { cause: join(["the attempts ledger is unobservable, so no attempt can be metered or sealed: ", reason], "") }] + WorkflowAttemptsObserved { attempts } => + concat( + map(attempts, progress => belt_metering_for_attempt(instance: instance, progress: progress)), + belt_metering_departed(instance: instance, attempts: attempts), + ) + } +} + +// A REFUSED METERING WRITE FAILS THE PASS, counted and named, so a lost record stops the line on the +// tick receipt rather than surfacing only as an absent record at the reader. +fn belt_metering_pass_outcome_from(outcomes: List) -> BeltPassOutcome { + let refused = outcomes |> flat_map(o => match o { + MeteringPersistRefused { cause: c } => [c] + MeteringPersisted { path: _, standing: _ } => [] as List + MeteringAlreadyFinal => [] as List + MeteringNotDue => [] as List + }) + match refused.first() { + Absent => BeltPassRecorded + Present { value: c } => + BeltPassFailed { reason: join([to_string(count(refused)), " metering record(s) refused; first: ", c], "") } + } +} + fn belt_commit_attempt_for_instance( instance: HostDashboardInstance, progress: WorkflowAttemptProgress, @@ -11510,6 +11585,10 @@ fn belt_tick_receipt_from_result( TickPassesWithheld { refusal } => BeltPassRefused { reason: launch_refusal_reason(r: refusal) } TickPassesRun => belt_publish_pass_outcome_from(outcomes: result.publish_outcomes) }), + metering_pass: (match result.wholesale { + TickPassesWithheld { refusal } => BeltPassRefused { reason: launch_refusal_reason(r: refusal) } + TickPassesRun => belt_metering_pass_outcome_from(outcomes: result.metering_outcomes) + }), } } From 81b75aab6e9761857c5bd450130b48818202f6be Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 06:07:41 +0000 Subject: [PATCH 19/46] belt: transplant the worker session-container launch onto the typed builder (fourth site main's metering refactor still ran through dispatch_worker_unit_run) --- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index caf21f63a26..814dddb121a 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -115,7 +115,7 @@ import gunbc.roadmap_dispatch_actuator { DispatchLiveSession, DispatchSpawnCommands, DispatchSessionContainer, TmuxSessionContainer, SystemdUnitContainer, - dispatch_worker_unit_properties_for_plan, dispatch_worker_unit_run, + dispatch_worker_unit_properties_for_plan, dispatch_worker_unit_command, dispatch_attempt_unit_name, attempt_unit_observation, worker_process_from_attempt_unit, @@ -1345,10 +1345,15 @@ fn belt_session_container_create( match session_placement_begin_launch(stores: held.stores, grant: held.grant) { SessionLaunchRefused { detail } => ExecStepFailed { step: "session-launch-gate", detail: detail } SessionLaunchAdmitted { grant: launched } => - belt_exec_steps( - steps: [BeltExecStep { step: "harness-unit-start", command: dispatch_worker_unit_run(instance: instance, plan: plan, granted: launched.amount, process_bounds: session_process_bounds) }], - workdir: workdir, - ) + match dispatch_worker_unit_command(instance: instance, plan: plan, granted: launched.amount, process_bounds: session_process_bounds) { + SystemdRunCommandRefused { reason: why } => + ExecStepFailed { step: "harness-unit-start", detail: why } + SystemdRunCommandReady { command: command } => + belt_exec_steps( + steps: [BeltExecStep { step: "harness-unit-start", command: dispatch_unit_exec_argv(command: command, resolved_program: plan.systemd_run_program) }], + workdir: workdir, + ) + } } } } From 1afb9ae111f28356066907bbd1bb7f71468a6f60 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 07:58:29 +0000 Subject: [PATCH 20/46] review 76279: refusal causes are a typed sum (SystemdRunOptionWordsRefusedCause: UnitNameContainsSlash | UnitNameMultiline | UnitSettingValueMultiline { property } | SetenvNameContainsEquals | SetenvNameMultiline | SetenvValueMultiline) with one prose projection for detail carriers; consumers match on the constructor (fabric refusal claims discriminate, plus a two-cause discrimination control); retained wet claim resets its failed unit after observing (rerunnable), and the module path follows the manual/ placement --- dag/extdeps/systemd/systemd_run.dag | 48 ++++++++++++++----- .../approval_device_enrolment_code_issue.dag | 5 +- dag/gunbc/compute/work_provider_local.dag | 5 +- dag/gunbc/compute/work_request.dag | 7 +-- .../host/host_effect_nbd_proxy_serve.dag | 7 +-- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 18 +++---- dag/gunbc/runner/runner_host_unit_slot.dag | 8 ++-- .../runner_microvm_lifecycle_realize.dag | 3 +- ...val_device_enrolment_code_witness_test.dag | 2 +- .../compute/work_class_grant_witness_test.dag | 6 +-- ...ystemd_run_transient_wait_witness_test.dag | 40 +++++++++++----- .../github_app_registry_witness_test.dag | 2 +- ...nbd_proxy_serve_transport_witness_test.dag | 8 ++-- ...roadmap_dispatch_actuator_witness_test.dag | 6 +-- ...r_microvm_slot_controller_witness_test.dag | 2 +- ..._throughput_qualification_witness_test.dag | 2 +- ...ner_microvm_lifecycle_wet_receipt_test.dag | 10 +--- dag/test/manual/typed_builder_wet_test.dag | 13 +++-- 18 files changed, 116 insertions(+), 76 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index b0b045c8c74..938dafcc1ca 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -101,20 +101,42 @@ type SystemdRunOption // typed value and the words, not a mangled word downstream. Each arm names the wire fact it // defends (unit names and setting values are one line and contain no slash; an environment name // is the text before the first '='). +type SystemdRunOptionWordsRefusedCause + = UnitNameContainsSlash + | UnitNameMultiline + | UnitSettingValueMultiline { property: SystemdUnitProperty } + | SetenvNameContainsEquals + | SetenvNameMultiline + | SetenvValueMultiline + type SystemdRunOptionWordsReading = SystemdRunOptionWordsProjected { words: List } - | SystemdRunOptionWordsRefused { reason: String } + | SystemdRunOptionWordsRefused { cause: SystemdRunOptionWordsRefusedCause } type SystemdRunCommandReading = SystemdRunCommandReady { command: ArgvCommand } - | SystemdRunCommandRefused { reason: String } + | SystemdRunCommandRefused { cause: SystemdRunOptionWordsRefusedCause } fn systemd_run_option_word_has_newline(s: String) -> Bool { string_contains(s: s, pattern: "\n") } -fn systemd_run_option_word_refused(reason: String) -> SystemdRunOptionWordsReading { - SystemdRunOptionWordsRefused { reason: reason } +fn systemd_run_option_word_refused(cause: SystemdRunOptionWordsRefusedCause) -> SystemdRunOptionWordsReading { + SystemdRunOptionWordsRefused { cause: cause } +} + +// The one prose projection of a refusal cause, for consumers whose carriers are prose (belt +// step details, log lines). Every arm names the wire fact it defends; the cause sum itself is +// what callers match on. +fn systemd_run_option_words_refused_cause_detail(cause: SystemdRunOptionWordsRefusedCause) -> String { + match cause { + UnitNameContainsSlash => "a unit name never contains / (systemd.unit(5))" + UnitNameMultiline => "a unit name is one line" + UnitSettingValueMultiline { property: p } => concat("unit setting value is one line, so --property= cannot carry a newline in: ", systemd_unit_property_wire(property: p)) + SetenvNameContainsEquals => "--setenv= takes NAME=VALUE, so the NAME side carries no = of its own" + SetenvNameMultiline => "--setenv= takes NAME=VALUE, so the NAME side is one line" + SetenvValueMultiline => "--setenv= takes NAME=VALUE, so the VALUE side is one line" + } } fn systemd_run_property_options_long(properties: List) -> List { @@ -129,14 +151,14 @@ fn systemd_run_property_options_short(properties: List) -> L // these so a refused command never mints words at all (short-circuit on the first refusal). fn systemd_run_option_projection_step(acc: SystemdRunOptionWordsReading, option: SystemdRunOption) -> SystemdRunOptionWordsReading { match acc { - SystemdRunOptionWordsRefused { reason: r } => SystemdRunOptionWordsRefused { reason: r } + SystemdRunOptionWordsRefused { cause: c } => SystemdRunOptionWordsRefused { cause: c } SystemdRunOptionWordsProjected { words: words } => match option { RunUnit { unit: unit } => if string_contains(s: unit as String, pattern: "/") { - systemd_run_option_word_refused(reason: "--unit= takes a unit name, and a unit name never contains / (systemd.unit(5))") + systemd_run_option_word_refused(cause: UnitNameContainsSlash) } else if systemd_run_option_word_has_newline(s: unit as String) { - systemd_run_option_word_refused(reason: "--unit= takes a unit name, and a unit name is one line") + systemd_run_option_word_refused(cause: UnitNameMultiline) } else { SystemdRunOptionWordsProjected { words: list_append(left: words, right: [concat("--unit=", unit as String)]) } } @@ -144,23 +166,23 @@ fn systemd_run_option_projection_step(acc: SystemdRunOptionWordsReading, option: RunScope => SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["--scope"]) } PropertyShort { property: p } => if systemd_run_option_word_has_newline(s: p.value as String) { - systemd_run_option_word_refused(reason: concat("unit setting value is one line, so --property= cannot carry a newline in: ", systemd_unit_property_wire(property: p.property))) + systemd_run_option_word_refused(cause: UnitSettingValueMultiline { property: p.property }) } else { SystemdRunOptionWordsProjected { words: list_append(left: words, right: ["-p", concat(systemd_unit_property_wire(property: p.property), concat("=", p.value as String))]) } } PropertyLong { property: p } => if systemd_run_option_word_has_newline(s: p.value as String) { - systemd_run_option_word_refused(reason: concat("unit setting value is one line, so --property= cannot carry a newline in: ", systemd_unit_property_wire(property: p.property))) + systemd_run_option_word_refused(cause: UnitSettingValueMultiline { property: p.property }) } else { SystemdRunOptionWordsProjected { words: list_append(left: words, right: [concat("--property=", concat(systemd_unit_property_wire(property: p.property), concat("=", p.value as String)))]) } } SetEnv { binding: b } => if string_contains(s: b.name as String, pattern: "=") { - systemd_run_option_word_refused(reason: "--setenv= takes NAME=VALUE, so the NAME side carries no = of its own") + systemd_run_option_word_refused(cause: SetenvNameContainsEquals) } else if systemd_run_option_word_has_newline(s: b.name as String) { - systemd_run_option_word_refused(reason: "--setenv= takes NAME=VALUE, so the NAME side is one line") + systemd_run_option_word_refused(cause: SetenvNameMultiline) } else if systemd_run_option_word_has_newline(s: b.value as String) { - systemd_run_option_word_refused(reason: "--setenv= takes NAME=VALUE, so the VALUE side is one line") + systemd_run_option_word_refused(cause: SetenvValueMultiline) } else { SystemdRunOptionWordsProjected { words: list_append(left: words, right: [concat("--setenv=", concat(b.name as String, concat("=", b.value as String)))]) } } @@ -200,7 +222,7 @@ fn systemd_run_command_argument_words(command: ArgvCommand) -> List { fn systemd_run_command_of(options: List, command_argv: List) -> SystemdRunCommandReading { match systemd_run_option_words(options: options) { - SystemdRunOptionWordsRefused { reason: r } => SystemdRunCommandRefused { reason: r } + SystemdRunOptionWordsRefused { cause: c } => SystemdRunCommandRefused { cause: c } SystemdRunOptionWordsProjected { words: option_words } => SystemdRunCommandReady { command: argv_command(program: systemd_run_program(), arguments: list_append(left: option_words, right: command_argv)) } } diff --git a/dag/gunbc/auth/approval_device_enrolment_code_issue.dag b/dag/gunbc/auth/approval_device_enrolment_code_issue.dag index a0b156b11d4..b01b87e6281 100644 --- a/dag/gunbc/auth/approval_device_enrolment_code_issue.dag +++ b/dag/gunbc/auth/approval_device_enrolment_code_issue.dag @@ -9,6 +9,7 @@ import extdeps.sudo.elevation { sudo_elevate } import extdeps.systemd.systemd_run { SystemdRunProperty, SystemdRunCommandReading, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_system_scope_command, +, systemd_run_option_words_refused_cause_detail, } import extdeps.exec.command { ArgvCommand, argv_words } import extdeps.tools.util_linux_setpriv { setpriv_reuid_regid_argv } @@ -135,8 +136,8 @@ fn enrolment_code_issue_remote(revision: ReleaseRevisionBinding) -> CliWireRespo FleetSshContextRefused { cause: c } => CliWireUnprintable { cause: ("approval device enrolment code: " + c) as NonEmptyStr } FleetSshContextReady { context: context, receipt: _ } => match enrolment_code_issue_remote_command(revision: revision) { - SystemdRunCommandRefused { reason: why } => - CliWireUnprintable { cause: ("approval device enrolment code: remote invocation refused: " + why) as NonEmptyStr } + SystemdRunCommandRefused { cause: why } => + CliWireUnprintable { cause: ("approval device enrolment code: remote invocation refused: " + systemd_run_option_words_refused_cause_detail(cause: why)) as NonEmptyStr } SystemdRunCommandReady { command: command } => match typed_argv_exec_over_fleet_ssh( target: enrolment_code_issue_ssh_target(), diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index d17a749c4ba..581102bd4f5 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -22,6 +22,7 @@ import extdeps.systemd.systemd_run { ServiceResultFormatNotGrounded, MemoryPeakFormatNotGrounded, SummaryAbsence, SummaryNoInvocation, SummarySuppressedByQuietLauncher, SummaryCaptureEmpty, SummaryLineAbsent, summary_absence_wire, SystemdFormattedBytes, FormattedBytesExact, FormattedBytesBounded, systemd_formatted_bytes_upper, +, systemd_run_option_words_refused_cause_detail, } import extdeps.systemd.systemctl import gunbc.compute.unit_bounds { compute_grant_unit_properties, compute_kill_mode_property, compute_kill_mode_value } @@ -395,9 +396,9 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden ], command_argv: argv, ) { - SystemdRunCommandRefused { reason: why } => + SystemdRunCommandRefused { cause: why } => ComputeUnitRun { - exec: ComputeExecRefused { reason: why }, + exec: ComputeExecRefused { reason: systemd_run_option_words_refused_cause_detail(cause: why) }, summary: SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } }, } SystemdRunCommandReady { command: command } => { diff --git a/dag/gunbc/compute/work_request.dag b/dag/gunbc/compute/work_request.dag index ad21ae46e1f..56a2a780384 100644 --- a/dag/gunbc/compute/work_request.dag +++ b/dag/gunbc/compute/work_request.dag @@ -3,6 +3,7 @@ module gunbc.compute.work_request import std.types { String, Bool, Int, List, NonEmptyStr, FilePath } import std.measure { Kibibyte, kibibyte_count } import std.content_hash { Fnv1a64Structural, content_hash_atom, content_hash_tagged_structural } +import extdeps.systemd.systemd_run { SystemdRunOptionWordsRefusedCause, systemd_run_option_words_refused_cause_detail } import extdeps.git.object_store { GitObjectId, git_object_id_wire_hex } import extdeps.languages.json.emit { JsonValue, json_object, json_kv, json_string, json_int, json_array, serialize_json, @@ -178,7 +179,7 @@ type WorkInfrastructureRefusal | SubjectUnresolved { detail: String } | UnitMemoryGrantExceeded { class: String, granted: Kibibyte, peak_upper: Kibibyte?, grant_was_ceiling: Bool } | UnitEndingUnclassifiable { exit_code: Int, cause: String } - | UnitLaunchRefused { reason: String } + | UnitLaunchRefused { cause: SystemdRunOptionWordsRefusedCause } fn work_outcome_identity(o: WorkOutcome) -> String { match o { @@ -247,8 +248,8 @@ fn work_refusal_detail(r: WorkInfrastructureRefusal) -> String { SubjectUnresolved { detail } => join(["subject unresolved: ", detail], "") UnitEndingUnclassifiable { exit_code, cause } => join(["the unit exited ", to_string(value: exit_code), " and its ending could not be classified, so it is neither reported as failed nor as grant-exceeded: ", cause], "") - UnitLaunchRefused { reason } => - join(["the unit was never launched: the systemd-run builder refused the invocation, so there is no exit to report: ", reason], "") + UnitLaunchRefused { cause } => + join(["the unit was never launched: the systemd-run builder refused the invocation, so there is no exit to report: ", systemd_run_option_words_refused_cause_detail(cause: cause)], "") UnitMemoryGrantExceeded { class, granted, peak_upper, grant_was_ceiling } => join(["the unit was OOM-killed at its memory grant: class ", class, " was granted ", to_string(value: kibibyte_count(k: granted)), " KiB and its manager reported a peak of ", match peak_upper { Present { value: k } => join(["at most ", to_string(value: kibibyte_count(k: k)), " KiB"], "") Absent => "NOTHING (no Memory peak line was reported)" }, diff --git a/dag/gunbc/host/host_effect_nbd_proxy_serve.dag b/dag/gunbc/host/host_effect_nbd_proxy_serve.dag index fee3c72db37..4a14d61d932 100644 --- a/dag/gunbc/host/host_effect_nbd_proxy_serve.dag +++ b/dag/gunbc/host/host_effect_nbd_proxy_serve.dag @@ -19,7 +19,8 @@ import extdeps.bmc.webui.nbd_proxy_serve { bmcweb_session_token_env_ref, } import extdeps.exec.command { ArgvCommand, argv_words } -import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command } +import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command , systemd_run_option_words_refused_cause_detail, +} import extdeps.tools.nbdkit { nbdkit_readonly_file_serve_command } import extdeps.tools.websocat { websocat_nbd_proxy_bridge_command } import extdeps.filesystem.filesystem_io { @@ -110,8 +111,8 @@ fn host_effect_nbd_proxy_serve_systemd_run_transient( transport: HostEffectTransport, ) -> String? { match systemd_run_transient_unit_command(unit: unit, properties: [], command_argv: command_argv) { - SystemdRunCommandRefused { reason: why } => - Present { value: concat(concat("unit ", unit as String), concat(": ", why)) } + SystemdRunCommandRefused { cause: why } => + Present { value: concat(concat("unit ", unit as String), concat(": ", systemd_run_option_words_refused_cause_detail(cause: why))) } SystemdRunCommandReady { command: command } => match systemd_run_transient_read(transport: transport, command: command) { SystemdRunTransientStarted { stdout: _ } => Absent diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index 814dddb121a..a1026559b2b 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -31,7 +31,7 @@ import gunbc.roadmap.roadmap_goal_audit_role { goal_audit_criterion_key, goal_au import gunbc.roadmap_dispatch_actuator { dispatch_events_projection_argv_at, dispatch_current_attempt_events_path_for_instance } import gunbc.roadmap_dispatch_actuator { dispatch_attempt_review_dir_for_instance, dispatch_attempt_review_verdict_path_for_instance, dispatch_attempt_review_verdict_basename, - dispatch_review_unit_name, dispatch_review_unit_command, + systemd_run_option_words_refused_cause_detail, dispatch_review_unit_name, dispatch_review_unit_command, dispatch_attempt_audit_dir_for_instance, dispatch_attempt_audit_verdict_path_for_instance, dispatch_attempt_audit_verdict_basename, dispatch_audit_unit_name, dispatch_audit_unit_command, dispatch_attempt_supervisor_dir_for_instance, dispatch_supervisor_unit_name, dispatch_supervisor_unit_command, @@ -1346,8 +1346,8 @@ fn belt_session_container_create( SessionLaunchRefused { detail } => ExecStepFailed { step: "session-launch-gate", detail: detail } SessionLaunchAdmitted { grant: launched } => match dispatch_worker_unit_command(instance: instance, plan: plan, granted: launched.amount, process_bounds: session_process_bounds) { - SystemdRunCommandRefused { reason: why } => - ExecStepFailed { step: "harness-unit-start", detail: why } + SystemdRunCommandRefused { cause: why } => + ExecStepFailed { step: "harness-unit-start", detail: systemd_run_option_words_refused_cause_detail(cause: why) } SystemdRunCommandReady { command: command } => belt_exec_steps( steps: [BeltExecStep { step: "harness-unit-start", command: dispatch_unit_exec_argv(command: command, resolved_program: plan.systemd_run_program) }], @@ -2588,11 +2588,11 @@ fn belt_supervisor_convene( ), ), ) { - SystemdRunCommandRefused { reason: why } => + SystemdRunCommandRefused { cause: why } => SpawnFailed { node_id: node.node as String, step: "supervisor-unit-start", - detail: why, + detail: systemd_run_option_words_refused_cause_detail(cause: why), provider: provider_wire_label, launch: launch, } @@ -10043,8 +10043,8 @@ fn belt_audit_spawn(instance: HostDashboardInstance, progress: WorkflowAttemptPr RosterAdmissionRefused { cause: roster_cause } => ReviewFailed { node_id: progress.node_id, criterion: goal_audit_criterion_key, detail: join(["the auditor was not launched: ", roster_cause], "") } RosterAdmitted => match dispatch_audit_unit_command(instance: instance, node_id: node_id, attempt_key: progress.attempt_key, worktree_path: worktree, brief: brief, head_sha: head) { - SystemdRunCommandRefused { reason: why } => - ReviewFailed { node_id: progress.node_id, criterion: goal_audit_criterion_key, detail: why } + SystemdRunCommandRefused { cause: why } => + ReviewFailed { node_id: progress.node_id, criterion: goal_audit_criterion_key, detail: systemd_run_option_words_refused_cause_detail(cause: why) } SystemdRunCommandReady { command: command } => match belt_exec(cmd: dispatch_unit_exec_argv(command: command, resolved_program: systemd_run), workdir: worktree) { ExecOk { stdout: _ } => ReviewSpawned { node_id: progress.node_id, criterion: goal_audit_criterion_key, unit: unit as String } @@ -10304,8 +10304,8 @@ fn belt_review_spawn(instance: HostDashboardInstance, node_id: String, attempt_k RosterAdmissionRefused { cause: roster_cause } => ReviewFailed { node_id: node_id, criterion: call.id, detail: join(["the reviewer was not launched: ", roster_cause], "") } RosterAdmitted => match dispatch_review_unit_command(instance: instance, node_id: nid, attempt_key: attempt_key, worktree_path: worktree, brief: brief, head_sha: head, criterion_key: key) { - SystemdRunCommandRefused { reason: why } => - ReviewFailed { node_id: node_id, criterion: call.id, detail: why } + SystemdRunCommandRefused { cause: why } => + ReviewFailed { node_id: node_id, criterion: call.id, detail: systemd_run_option_words_refused_cause_detail(cause: why) } SystemdRunCommandReady { command: command } => match belt_exec(cmd: dispatch_unit_exec_argv(command: command, resolved_program: systemd_run), workdir: worktree) { ExecOk { stdout: _ } => ReviewSpawned { node_id: node_id, criterion: call.id, unit: unit as String } diff --git a/dag/gunbc/runner/runner_host_unit_slot.dag b/dag/gunbc/runner/runner_host_unit_slot.dag index 5f4c0d717c3..f6b41e80fd1 100644 --- a/dag/gunbc/runner/runner_host_unit_slot.dag +++ b/dag/gunbc/runner/runner_host_unit_slot.dag @@ -10,7 +10,7 @@ import extdeps.github.actions_runner { import gunbc.managed_host { ManagedHostBinding, ManagedHostBindingStanding, ManagedHostBound } import gunbc.runner_slot_desired { gunbc_runner_slot_desired } import extdeps.systemd.systemd_run { - SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_command_argument_words, + SystemdRunCommandReady, SystemdRunCommandRefused, SystemdRunOptionWordsRefusedCause, systemd_run_command_argument_words, } import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, ephemeral_slot_command, ephemeral_slot_labels } @@ -49,7 +49,7 @@ type HostUnitSlot sole_constructor { type HostUnitSlotRefusal = HostUnitSlotHostNotBound { standing: ManagedHostBindingStanding } | HostUnitSlotRunnerBinaryUnpublished { binary_release: ActionsRunnerRelease } - | HostUnitSlotSystemdRunRefused { reason: String } + | HostUnitSlotSystemdRunRefused { cause: SystemdRunOptionWordsRefusedCause } type HostUnitSlotStanding = HostUnitSlotBound { slot: HostUnitSlot } @@ -74,8 +74,8 @@ fn host_unit_slot_for(host: ManagedHostBindingStanding, attempt: NonEmptyStr, wo Present { value: artifact } => { let unit = ephemeral_slot_unit(host: b.host.host, attempt: attempt) match ephemeral_slot_command(unit: unit, desired: gunbc_runner_slot_desired(), command_argv: host_unit_slot_listener_argv()) { - SystemdRunCommandRefused { reason: why } => - HostUnitSlotRefused { refusal: HostUnitSlotSystemdRunRefused { reason: why } } + SystemdRunCommandRefused { cause: why } => + HostUnitSlotRefused { refusal: HostUnitSlotSystemdRunRefused { cause: why } } SystemdRunCommandReady { command: command } => HostUnitSlotBound { slot: HostUnitSlot { diff --git a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag index b70026bcaed..523f66d17e1 100644 --- a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag +++ b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag @@ -79,6 +79,7 @@ import extdeps.exec.command { argv_words } import extdeps.systemd.systemd_run { SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command, systemd_run_transient_retained_unit_command, systemd_run_command_argument_words, +, systemd_run_option_words_refused_cause_detail, } import gunbc.runner_microvm_lifecycle { CgroupSubtreeReadback, CgroupSubtreeEmpty, CgroupSubtreePopulated, CgroupSubtreeReadFailed, @@ -1181,7 +1182,7 @@ fn run_jailer_in_cell(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, recei ], receipt.runtime_properties), command_argv: argv_words(command: jailer), ) { - SystemdRunCommandRefused { reason: why } => VmmStartFailed { unit: unit, detail: why } + SystemdRunCommandRefused { cause: why } => VmmStartFailed { unit: unit, detail: systemd_run_option_words_refused_cause_detail(cause: why) } SystemdRunCommandReady { command: command } => { let started = systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)) if started.success { VmmStarted { unit: unit, receipt: receipt } } diff --git a/dag/test/claim/approval_device_enrolment_code_witness_test.dag b/dag/test/claim/approval_device_enrolment_code_witness_test.dag index 9482f67927f..2981f37f68e 100644 --- a/dag/test/claim/approval_device_enrolment_code_witness_test.dag +++ b/dag/test/claim/approval_device_enrolment_code_witness_test.dag @@ -94,7 +94,7 @@ test fn the_remote_argv_runs_the_release_verb_as_the_operator_user() -> Bool { // on a refusal arm. fn the_remote_verb_words_carry_the_release_shape(reading: SystemdRunCommandReading, rev: String, op: String) -> Bool { match reading { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let joined = join(sudo_elevate_words(command: command), " ") string_contains(s: joined, pattern: "/usr/bin/sudo -n systemd-run --scope --property=MemoryMax=" + to_string(byte_size_count(b: approval_broker_slice_memory_max)) + " --property=MemoryHigh=" + to_string(byte_size_count(b: approval_broker_slice_memory_high)) + " -- setpriv --reuid=" + op + " --regid=" + op + " --init-groups -- /usr/bin/env " + gunbc_workspace_root_env_name + "=/opt/gunbc/approval-broker/releases/" + rev + " /opt/gunbc/approval-broker/releases/" + rev + "/gunbc run ") diff --git a/dag/test/claim/compute/work_class_grant_witness_test.dag b/dag/test/claim/compute/work_class_grant_witness_test.dag index 5fb0af62fe1..5877adc0c66 100644 --- a/dag/test/claim/compute/work_class_grant_witness_test.dag +++ b/dag/test/claim/compute/work_class_grant_witness_test.dag @@ -50,7 +50,7 @@ import gunbc.compute.work_provider_local { // the fold is the only route from the reading to a summary.) fn fixture_summary(standing: SummaryFormatStanding, stderr: String) -> SystemdRunWaitSummary { match systemd_run_user_wait_command(unit: "gunbc-fixture", properties: [], setenv_bindings: [], command_argv: ["true"]) { - SystemdRunCommandRefused { reason: _ } => + SystemdRunCommandRefused { cause: _ } => SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } } SystemdRunCommandReady { command: command } => systemd_run_wait_summary(launcher: SystemdSummaryPrintingWait { command: command }, standing: standing, stderr: stderr) @@ -62,7 +62,7 @@ fn fixture_summary(standing: SummaryFormatStanding, stderr: String) -> SystemdRu // user-manager start, then the command after --. test fn the_user_wait_builder_projects_the_words_the_string_form_rendered() -> Bool { match systemd_run_user_wait_command(unit: "gunbc-fixture", properties: [], setenv_bindings: [], command_argv: ["true"]) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => argv_words(command: command) == ["systemd-run", "--wait", "--pipe", "--collect", "--user", "--unit=gunbc-fixture", "--", "true"] } @@ -297,7 +297,7 @@ test fn the_peak_is_unreported_by_absence_and_an_untrimmed_summary_still_reads() // admit list on argv_command refuses test callers, so there is no direct mint to fall back on). fn quiet_summary(stderr: String) -> SystemdRunWaitSummary { match systemd_run_transient_wait_unit_command(unit: "gunbc-quiet", properties: [], command_argv: ["true"]) { - SystemdRunCommandRefused { reason: _ } => + SystemdRunCommandRefused { cause: _ } => SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } } SystemdRunCommandReady { command: command } => systemd_run_wait_summary(launcher: SystemdSummaryPrintingWait { command: command }, standing: grounded_standing, stderr: stderr) diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 7e2cf36451f..17698e2da91 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -39,7 +39,7 @@ fn wait_control_command_reading() -> SystemdRunCommandReading { // THE POSITIVE CONTROL, WORDS HALF: the projection's words are the old builder's words, byte for byte. test fn systemd_wait_route_projects_the_old_words() -> Bool { match wait_control_command_reading() { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => argv_exact_token_equal( left: argv_words(command: command), @@ -56,7 +56,7 @@ test fn systemd_wait_route_projects_the_old_words() -> Bool { // same words the typed command projects. test fn systemd_wait_route_materializes_the_projected_words() -> Bool { match wait_control_command_reading() { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => systemd_run_transient_wait_operation_argv_matches_authority(command: command) } @@ -71,7 +71,7 @@ test fn a_property_value_the_wire_cannot_carry_is_refused() -> Bool { properties: [SystemdRunProperty { property: WorkingDirectoryProperty, value: "/tmp/a\nb" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { reason: why } => string_contains(s: why, pattern: "one line") + SystemdRunCommandRefused { cause: UnitSettingValueMultiline { property: p } } => systemd_unit_property_wire(property: p) == "WorkingDirectory" SystemdRunCommandReady { command: _ } => false } } @@ -87,7 +87,7 @@ test fn the_collecting_transient_builder_emits_unit_properties_collect() -> Bool properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => argv_exact_token_equal( left: argv_words(command: command), @@ -106,7 +106,7 @@ test fn the_retained_transient_builder_omits_collect() -> Bool { properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => argv_exact_token_equal( left: argv_words(command: command), @@ -124,7 +124,7 @@ test fn the_user_wait_builder_renders_the_old_order() -> Bool { setenv_bindings: [SystemdSetenvBinding { name: "A" as NonEmptyStr, value: "c" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => argv_exact_token_equal( left: argv_words(command: command), @@ -143,7 +143,7 @@ test fn the_scope_builder_leads_with_scope_only() -> Bool { properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => argv_exact_token_equal( left: argv_words(command: command), @@ -159,7 +159,7 @@ test fn a_unit_name_the_wire_cannot_carry_is_refused() -> Bool { properties: [], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { reason: why } => string_contains(s: why, pattern: "never contains /") + SystemdRunCommandRefused { cause: UnitNameContainsSlash } => true SystemdRunCommandReady { command: _ } => false } } @@ -173,25 +173,41 @@ test fn a_setenv_name_with_an_equals_is_refused() -> Bool { setenv_bindings: [SystemdSetenvBinding { name: "A=b" as NonEmptyStr, value: "c" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { reason: why } => string_contains(s: why, pattern: "carries no = of its own") + SystemdRunCommandRefused { cause: SetenvNameContainsEquals } => true SystemdRunCommandReady { command: _ } => false } } test fn a_setenv_binding_with_an_equals_in_the_name_is_refused() -> Bool { match systemd_run_option_words(options: [SetEnv { binding: SystemdSetenvBinding { name: "A=b" as NonEmptyStr, value: "c" as NonEmptyStr } }]) { - SystemdRunOptionWordsRefused { reason: why } => string_contains(s: why, pattern: "carries no = of its own") + SystemdRunOptionWordsRefused { cause: SetenvNameContainsEquals } => true SystemdRunOptionWordsProjected { words: _ } => false } } +// The cause sum discriminates: two different wire defects yield two different constructors, so a +// caller can branch on the cause without matching on prose. Tags 0 and 1 must differ. +test fn refusal_causes_discriminate_two_wire_defects() -> Bool { + let slash_tag = match systemd_run_option_words(options: [RunUnit { unit: "wrong/name" as NonEmptyStr }]) { + SystemdRunOptionWordsRefused { cause: UnitNameContainsSlash } => 1 + SystemdRunOptionWordsRefused { cause: _ } => 0 - 1 + SystemdRunOptionWordsProjected { words: _ } => 0 - 1 + } + let equals_tag = match systemd_run_option_words(options: [SetEnv { binding: SystemdSetenvBinding { name: "A=b" as NonEmptyStr, value: "c" as NonEmptyStr } }]) { + SystemdRunOptionWordsRefused { cause: SetenvNameContainsEquals } => 2 + SystemdRunOptionWordsRefused { cause: _ } => 0 - 1 + SystemdRunOptionWordsProjected { words: _ } => 0 - 1 + } + (slash_tag == 1) && (equals_tag == 2) && (slash_tag != equals_tag) +} + // THE TYPED OPTION IS A SEALED SUM: there is no arm an unknown flag could ride. This fold walks the // variants the module renders today and pins each one's man-page spelling at systemd 255; a variant // added to the sum updates this fold or compilation fails, which is how an unknown option stays // unwritable. test fn every_option_variant_renders_its_man_page_spelling() -> Bool { match systemd_run_option_words(options: [RunUserManager, RunScope, Wait, Quiet, Pipe, Collect, EndOfOptions]) { - SystemdRunOptionWordsRefused { reason: _ } => false + SystemdRunOptionWordsRefused { cause: _ } => false SystemdRunOptionWordsProjected { words: words } => join(words, separator: " ") == "--user --scope --wait --quiet --pipe --collect --" } @@ -218,7 +234,7 @@ test fn systemd_wait_normal_exit_86_remains_86() -> Bool { fn materialized_cardinality(reading: SystemdRunCommandReading) -> Int { match reading { - SystemdRunCommandRefused { reason: _ } => 0 - 2 + SystemdRunCommandRefused { cause: _ } => 0 - 2 SystemdRunCommandReady { command: command } => match systemd_run_transient_wait_operation_argv(command: command) { ArgvMaterialized { argv: words } => count(words) diff --git a/dag/test/claim/github_app_registry_witness_test.dag b/dag/test/claim/github_app_registry_witness_test.dag index 178c8efbba1..6fd2bf87859 100644 --- a/dag/test/claim/github_app_registry_witness_test.dag +++ b/dag/test/claim/github_app_registry_witness_test.dag @@ -1378,7 +1378,7 @@ test fn witness_jit_mint_authorizes_a_managed_host_unit_slot_under_the_routes_ow && runner_label_sets_equal(a: map(req.labels, l => l.value as String), b: ephemeral_slot_labels(host: h.host, attempt: "q-1")) && match ephemeral_slot_command(unit: u.unit, desired: gunbc_runner_slot_desired(), command_argv: host_unit_slot_listener_argv()) { SystemdRunCommandReady { command: c } => u.argv == systemd_run_command_argument_words(command: c) - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false } && match u.runner.arch { ActionsRunnerLinuxArm64 => true _ => false } _ => false diff --git a/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag b/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag index 8fd79336f74..7d77c91eef0 100644 --- a/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag +++ b/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag @@ -224,7 +224,7 @@ test fn witness_systemd_run_transient_operation_argv_matches_authority() -> Bool properties: [], command_argv: ["nbdkit", "-p", "10809", "file", "/var/lib/gunbc/artifacts/x.iso"], ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => systemd_run_transient_operation_argv_matches_authority(command: command) } } @@ -240,7 +240,7 @@ test fn witness_systemd_run_transient_property_reaches_materialized_argv() -> Bo properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["nbdkit", "-p", "10809", "file", "/var/lib/gunbc/artifacts/x.iso"], ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => systemd_run_transient_operation_argv_matches_authority(command: command) } } @@ -256,7 +256,7 @@ test fn witness_systemd_run_property_value_with_space_keeps_token_identity() -> properties: [SystemdRunProperty { property: WorkingDirectoryProperty, value: "/srv/path with space" as NonEmptyStr }], command_argv: ["nbdkit", "-p", "10809"], ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => systemd_run_transient_operation_argv_matches_authority(command: command) } } @@ -281,7 +281,7 @@ test fn witness_systemd_run_property_argv_renders_the_wire_name() -> Bool { properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => join(argv_words(command: command), " ") == "systemd-run --unit=nbd-proxy-witness.service --property=MemoryMax=17179869184 --collect -- /bin/true" diff --git a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag index 565e0cfdab2..35bbf8c3304 100644 --- a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag @@ -1177,7 +1177,7 @@ test fn witness_review_unit_argv_mints_per_criterion_attempt_identity() -> Bool head_sha: "abc123", criterion_key: "conformance-external-facts", ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let cmd = dispatch_unit_exec_argv(command: command, resolved_program: "/usr/bin/systemd-run" as FilePath) string_contains(s: join(cmd.args, separator: "\0"), pattern: "attempt_identity=reviewer:6.node-1.12.att-20260920.26.conformance-external-facts.6.abc123") @@ -1198,7 +1198,7 @@ test fn witness_review_unit_option_words_are_the_string_form_words() -> Bool { head_sha: "abc123", criterion_key: "conformance-external-facts", ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let wire = join(argv_words(command: command), separator: "\0") string_contains( @@ -1392,7 +1392,7 @@ test fn witness_supervisor_unit_argv_shapes_one_session_per_escalation_record() worktree_path: "/x/attempts/wt", brief: "BRIEF-MARKER", ) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let cmd = dispatch_unit_exec_argv(command: command, resolved_program: "/usr/bin/systemd-run" as FilePath) supervisor_wire_checks(wire: join(cmd.args, separator: "\0")) diff --git a/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag b/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag index 870f0ed993f..dd29c12e17f 100644 --- a/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag +++ b/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag @@ -336,7 +336,7 @@ fn attempt_launch_property_words(attempt: MicroVmAttempt) -> List { ], command_argv: [], ) { - SystemdRunCommandRefused { reason: _ } => [] + SystemdRunCommandRefused { cause: _ } => [] SystemdRunCommandReady { command: command } => property_words_of(argv: argv_words(command: command)) } } diff --git a/dag/test/claim/runner/runner_throughput_qualification_witness_test.dag b/dag/test/claim/runner/runner_throughput_qualification_witness_test.dag index 94b20e68fcc..21a65bf2872 100644 --- a/dag/test/claim/runner/runner_throughput_qualification_witness_test.dag +++ b/dag/test/claim/runner/runner_throughput_qualification_witness_test.dag @@ -874,7 +874,7 @@ test fn the_ephemeral_slot_argv_carries_the_fleet_memory_max() -> Bool { fn ephemeral_slot_words_carry_fleet_shape(reading: SystemdRunCommandReading) -> Bool { match reading { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let argv = argv_words(command: command) let has_quota_word = argv |> any(w => string_contains(s: w, pattern: "--property=CPUQuota=")) diff --git a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag index 74098d610d3..328718d9e32 100644 --- a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag +++ b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag @@ -70,10 +70,7 @@ fn start_payload(unit: NonEmptyStr, argv: List) -> Bool uses net: Network { match systemd_run_transient_unit_command(unit: unit, properties: [wet_slice_property()], command_argv: argv) { - SystemdRunCommandRefused { reason: why } => { - let _ = why - false - } + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => systemd.SystemdRun.RunTransient(command_argv: argv_words(command: command)).success } @@ -422,10 +419,7 @@ fn start_with_invocation(unit: NonEmptyStr, invocation: String, sleep_for: Strin // tail only — the transport owns the "systemd-run" head. fn start_retained_payload(unit: NonEmptyStr, properties: List, command_argv: List) -> Bool { match systemd_run_transient_retained_unit_command(unit: unit, properties: properties, command_argv: command_argv) { - SystemdRunCommandRefused { reason: why } => { - let _ = why - false - } + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)).success } diff --git a/dag/test/manual/typed_builder_wet_test.dag b/dag/test/manual/typed_builder_wet_test.dag index f1c42b9f283..584ba5afe52 100644 --- a/dag/test/manual/typed_builder_wet_test.dag +++ b/dag/test/manual/typed_builder_wet_test.dag @@ -1,4 +1,4 @@ -module test.claim.systemd.typed_builder_wet +module test.manual.typed_builder_wet import std.types { String, NonEmptyStr, Bool, List, Int } import std.algebra { trim } @@ -36,11 +36,14 @@ test fn a_real_retained_transient_is_still_known_after_a_failing_exit() -> Bool { let unit = "fci1-typed-argv-wet-retained.service" as NonEmptyStr match systemd_run_transient_retained_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let started = systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)) let load_state = systemd.Systemctl.ShowLoadState(unit: unit) - started.success && (trim(s: load_state.value) == "loaded") + // The observation is the point; the failed unit is then reset so the NEXT run of this claim + // can start the same fixed unit name again instead of being refused because it exists. + let reset = systemd.Systemctl.ResetFailedUnit(unit: unit) + started.success && (trim(s: load_state.value) == "loaded") && reset.success } } } @@ -52,7 +55,7 @@ test fn the_collected_twin_of_that_unit_is_gone() -> Bool { let unit = "fci1-typed-argv-wet-collected.service" as NonEmptyStr match systemd_run_transient_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let started = systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)) let load_state = systemd.Systemctl.ShowLoadState(unit: unit) @@ -69,7 +72,7 @@ test fn the_waited_transient_reports_the_exact_exit() -> Bool { let unit = "fci1-typed-argv-wet-waited.service" as NonEmptyStr match systemd_run_transient_wait_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { - SystemdRunCommandRefused { reason: _ } => false + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let waited = systemd.SystemdRun.RunTransientAndWait(command_argv: systemd_run_command_argument_words(command: command)) waited.exit_code == 86 From 4607706e1fbbfa6e6a077782e3c77c67d326dd75 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 08:02:06 +0000 Subject: [PATCH 21/46] reset rationale to module grain --- dag/test/manual/typed_builder_wet_test.dag | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dag/test/manual/typed_builder_wet_test.dag b/dag/test/manual/typed_builder_wet_test.dag index 584ba5afe52..294fe672afa 100644 --- a/dag/test/manual/typed_builder_wet_test.dag +++ b/dag/test/manual/typed_builder_wet_test.dag @@ -31,6 +31,9 @@ import extdeps.systemd.systemd_run { // The retained invocation: the unit FAILED (exit 86) and is still known to the manager, because // no --collect was passed. This is the claim that proves the terminal state stays observable. +// The observation is the point; the failed unit is then reset (ResetFailedUnit) so the NEXT +// run of this claim can start the same fixed unit name again instead of being refused because it +// exists. test fn a_real_retained_transient_is_still_known_after_a_failing_exit() -> Bool uses net: Network { @@ -40,8 +43,6 @@ test fn a_real_retained_transient_is_still_known_after_a_failing_exit() -> Bool SystemdRunCommandReady { command: command } => { let started = systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)) let load_state = systemd.Systemctl.ShowLoadState(unit: unit) - // The observation is the point; the failed unit is then reset so the NEXT run of this claim - // can start the same fixed unit name again instead of being refused because it exists. let reset = systemd.Systemctl.ResetFailedUnit(unit: unit) started.success && (trim(s: load_state.value) == "loaded") && reset.success } From fbdfd1d6217236d08f4f74ad362827a3eaa1005c Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 08:32:21 +0000 Subject: [PATCH 22/46] import lists do not admit a leading/trailing stray comma: fold the injected name into the list body --- dag/gunbc/auth/approval_device_enrolment_code_issue.dag | 2 +- dag/gunbc/compute/work_provider_local.dag | 2 +- dag/gunbc/runner/runner_microvm_lifecycle_realize.dag | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dag/gunbc/auth/approval_device_enrolment_code_issue.dag b/dag/gunbc/auth/approval_device_enrolment_code_issue.dag index b01b87e6281..e7b3955fcd7 100644 --- a/dag/gunbc/auth/approval_device_enrolment_code_issue.dag +++ b/dag/gunbc/auth/approval_device_enrolment_code_issue.dag @@ -9,7 +9,7 @@ import extdeps.sudo.elevation { sudo_elevate } import extdeps.systemd.systemd_run { SystemdRunProperty, SystemdRunCommandReading, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_system_scope_command, -, systemd_run_option_words_refused_cause_detail, + systemd_run_option_words_refused_cause_detail, } import extdeps.exec.command { ArgvCommand, argv_words } import extdeps.tools.util_linux_setpriv { setpriv_reuid_regid_argv } diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index 581102bd4f5..a20d21e9904 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -22,7 +22,7 @@ import extdeps.systemd.systemd_run { ServiceResultFormatNotGrounded, MemoryPeakFormatNotGrounded, SummaryAbsence, SummaryNoInvocation, SummarySuppressedByQuietLauncher, SummaryCaptureEmpty, SummaryLineAbsent, summary_absence_wire, SystemdFormattedBytes, FormattedBytesExact, FormattedBytesBounded, systemd_formatted_bytes_upper, -, systemd_run_option_words_refused_cause_detail, + systemd_run_option_words_refused_cause_detail, } import extdeps.systemd.systemctl import gunbc.compute.unit_bounds { compute_grant_unit_properties, compute_kill_mode_property, compute_kill_mode_value } diff --git a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag index 523f66d17e1..c0c6278713e 100644 --- a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag +++ b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag @@ -79,7 +79,7 @@ import extdeps.exec.command { argv_words } import extdeps.systemd.systemd_run { SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command, systemd_run_transient_retained_unit_command, systemd_run_command_argument_words, -, systemd_run_option_words_refused_cause_detail, + systemd_run_option_words_refused_cause_detail, } import gunbc.runner_microvm_lifecycle { CgroupSubtreeReadback, CgroupSubtreeEmpty, CgroupSubtreePopulated, CgroupSubtreeReadFailed, From b986e1b9633b6cc7b646f0630ae50386d3344fa2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 09:23:49 +0000 Subject: [PATCH 23/46] floor at fbdfd1d621: the detail fn is extdeps.systemd's (belt imports it from there, not from the actuator); typed refusal claims carry catch-all cause arms for exhaustiveness --- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 3 ++- .../claim/fabric/systemd_run_transient_wait_witness_test.dag | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index a1026559b2b..d237c4b5b57 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -31,12 +31,13 @@ import gunbc.roadmap.roadmap_goal_audit_role { goal_audit_criterion_key, goal_au import gunbc.roadmap_dispatch_actuator { dispatch_events_projection_argv_at, dispatch_current_attempt_events_path_for_instance } import gunbc.roadmap_dispatch_actuator { dispatch_attempt_review_dir_for_instance, dispatch_attempt_review_verdict_path_for_instance, dispatch_attempt_review_verdict_basename, - systemd_run_option_words_refused_cause_detail, dispatch_review_unit_name, dispatch_review_unit_command, + dispatch_review_unit_name, dispatch_review_unit_command, dispatch_attempt_audit_dir_for_instance, dispatch_attempt_audit_verdict_path_for_instance, dispatch_attempt_audit_verdict_basename, dispatch_audit_unit_name, dispatch_audit_unit_command, dispatch_attempt_supervisor_dir_for_instance, dispatch_supervisor_unit_name, dispatch_supervisor_unit_command, dispatch_supervisor_attempt_identity, dispatch_unit_exec_argv, } +import extdeps.systemd.systemd_run { systemd_run_option_words_refused_cause_detail } import extdeps.git { shape_git_merge_base_args } import gunbc.roadmap_authority { roadmap_acceptance_event_history_load, diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 17698e2da91..2d8169b681b 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -72,6 +72,7 @@ test fn a_property_value_the_wire_cannot_carry_is_refused() -> Bool { command_argv: ["/bin/true"], ) { SystemdRunCommandRefused { cause: UnitSettingValueMultiline { property: p } } => systemd_unit_property_wire(property: p) == "WorkingDirectory" + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: _ } => false } } @@ -160,6 +161,7 @@ test fn a_unit_name_the_wire_cannot_carry_is_refused() -> Bool { command_argv: ["/bin/true"], ) { SystemdRunCommandRefused { cause: UnitNameContainsSlash } => true + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: _ } => false } } @@ -174,6 +176,7 @@ test fn a_setenv_name_with_an_equals_is_refused() -> Bool { command_argv: ["/bin/true"], ) { SystemdRunCommandRefused { cause: SetenvNameContainsEquals } => true + SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: _ } => false } } @@ -181,6 +184,7 @@ test fn a_setenv_name_with_an_equals_is_refused() -> Bool { test fn a_setenv_binding_with_an_equals_in_the_name_is_refused() -> Bool { match systemd_run_option_words(options: [SetEnv { binding: SystemdSetenvBinding { name: "A=b" as NonEmptyStr, value: "c" as NonEmptyStr } }]) { SystemdRunOptionWordsRefused { cause: SetenvNameContainsEquals } => true + SystemdRunOptionWordsRefused { cause: _ } => false SystemdRunOptionWordsProjected { words: _ } => false } } From 5bbf8db90803b6897d63de6b8ea2e21bc9e6ab40 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 10:03:00 +0000 Subject: [PATCH 24/46] review 76406: SystemdSummaryPrintingWait is minted in exactly one place again -- systemd_run_user_wait_command returns SystemdSummaryPrintingWaitReading (Ready{wait} | Refused{cause}); consumers match the reading; the guarantee (never --quiet, so the peak summary is printable) is structural, not a comment --- dag/extdeps/systemd/systemd_run.dag | 21 +++++++++++++------ dag/gunbc/compute/work_provider_local.dag | 5 ++--- .../compute/work_class_grant_witness_test.dag | 16 +++++++------- ...ystemd_run_transient_wait_witness_test.dag | 12 +++++------ 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 938dafcc1ca..465594c72e9 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -44,11 +44,17 @@ data systemd_run_authority: ExternalAuthority = extdeps_external_authority_ancho fn systemd_run_program() -> ProgramIdentity = uncataloged_program(invocation: "systemd-run") -// THE WAITING LAUNCHER IS A TOKEN, NOT A WORD LIST: its ArgvCommand is minted only by the typed -// builders below, so what a caller holds is the fact that a specific projection happened, not a -// list it could have glued together itself. +// THE WAITING LAUNCHER IS A TOKEN, NOT A WORD LIST: SystemdSummaryPrintingWait is minted in +// exactly one place, the SystemdSummaryPrintingWaitReady arm of systemd_run_user_wait_command, so +// what a caller holds is the builder's own fact that this projection is the waiting, non-quiet +// one. No other site can construct it, and a launcher that cannot print a memory peak therefore +// cannot be handed to the thing whose contract is to observe one. type SystemdSummaryPrintingWait { command: ArgvCommand } +type SystemdSummaryPrintingWaitReading + = SystemdSummaryPrintingWaitReady { wait: SystemdSummaryPrintingWait } + | SystemdSummaryPrintingWaitRefused { cause: SystemdRunOptionWordsRefusedCause } + // compute_exec receives the program separately, so these are the argument tail; the full // argv_words would double the program head on the compute side too. fn systemd_summary_printing_wait_argv(w: SystemdSummaryPrintingWait) -> List { @@ -267,11 +273,14 @@ fn systemd_run_user_transient_command(unit: NonEmptyStr, properties: List, setenv_bindings: List, command_argv: List) -> SystemdRunCommandReading { - systemd_run_command_of( +fn systemd_run_user_wait_command(unit: NonEmptyStr, properties: List, setenv_bindings: List, command_argv: List) -> SystemdSummaryPrintingWaitReading { + match systemd_run_command_of( options: list_append(left: [Wait, Pipe, Collect], right: list_append(left: [RunUserManager, RunUnit { unit: unit }], right: list_append(left: systemd_run_property_options_short(properties: properties), right: list_append(left: systemd_run_setenv_options(bindings: setenv_bindings), right: [EndOfOptions])))), command_argv: command_argv, - ) + ) { + SystemdRunCommandRefused { cause: cause } => SystemdSummaryPrintingWaitRefused { cause: cause } + SystemdRunCommandReady { command: command } => SystemdSummaryPrintingWaitReady { wait: SystemdSummaryPrintingWait { command: command } } + } } // The pre-cutover system-scope builder led with --scope only (blob 71a9a1c5d3~1 line 158: diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index a20d21e9904..a57ef1c5be8 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -396,13 +396,12 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden ], command_argv: argv, ) { - SystemdRunCommandRefused { cause: why } => + SystemdSummaryPrintingWaitRefused { cause: why } => ComputeUnitRun { exec: ComputeExecRefused { reason: systemd_run_option_words_refused_cause_detail(cause: why) }, summary: SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } }, } - SystemdRunCommandReady { command: command } => { - let launcher = SystemdSummaryPrintingWait { command: command } + SystemdSummaryPrintingWaitReady { wait: launcher } => { let r = compute_exec( program: systemd_run, workdir: layout.checkout, diff --git a/dag/test/claim/compute/work_class_grant_witness_test.dag b/dag/test/claim/compute/work_class_grant_witness_test.dag index 5877adc0c66..d1c2b68a326 100644 --- a/dag/test/claim/compute/work_class_grant_witness_test.dag +++ b/dag/test/claim/compute/work_class_grant_witness_test.dag @@ -50,10 +50,10 @@ import gunbc.compute.work_provider_local { // the fold is the only route from the reading to a summary.) fn fixture_summary(standing: SummaryFormatStanding, stderr: String) -> SystemdRunWaitSummary { match systemd_run_user_wait_command(unit: "gunbc-fixture", properties: [], setenv_bindings: [], command_argv: ["true"]) { - SystemdRunCommandRefused { cause: _ } => + SystemdSummaryPrintingWaitRefused { cause: _ } => SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } } - SystemdRunCommandReady { command: command } => - systemd_run_wait_summary(launcher: SystemdSummaryPrintingWait { command: command }, standing: standing, stderr: stderr) + SystemdSummaryPrintingWaitReady { wait: launcher } => + systemd_run_wait_summary(launcher: launcher, standing: standing, stderr: stderr) } } @@ -62,9 +62,9 @@ fn fixture_summary(standing: SummaryFormatStanding, stderr: String) -> SystemdRu // user-manager start, then the command after --. test fn the_user_wait_builder_projects_the_words_the_string_form_rendered() -> Bool { match systemd_run_user_wait_command(unit: "gunbc-fixture", properties: [], setenv_bindings: [], command_argv: ["true"]) { - SystemdRunCommandRefused { cause: _ } => false - SystemdRunCommandReady { command: command } => - argv_words(command: command) == ["systemd-run", "--wait", "--pipe", "--collect", "--user", "--unit=gunbc-fixture", "--", "true"] + SystemdSummaryPrintingWaitRefused { cause: _ } => false + SystemdSummaryPrintingWaitReady { wait: launcher } => + argv_words(command: launcher.command) == ["systemd-run", "--wait", "--pipe", "--collect", "--user", "--unit=gunbc-fixture", "--", "true"] } } @@ -299,8 +299,8 @@ fn quiet_summary(stderr: String) -> SystemdRunWaitSummary { match systemd_run_transient_wait_unit_command(unit: "gunbc-quiet", properties: [], command_argv: ["true"]) { SystemdRunCommandRefused { cause: _ } => SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } } - SystemdRunCommandReady { command: command } => - systemd_run_wait_summary(launcher: SystemdSummaryPrintingWait { command: command }, standing: grounded_standing, stderr: stderr) + SystemdSummaryPrintingWaitReady { wait: launcher } => + systemd_run_wait_summary(launcher: launcher, standing: grounded_standing, stderr: stderr) } } diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 2d8169b681b..7420ac00a6e 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -125,10 +125,10 @@ test fn the_user_wait_builder_renders_the_old_order() -> Bool { setenv_bindings: [SystemdSetenvBinding { name: "A" as NonEmptyStr, value: "c" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { cause: _ } => false - SystemdRunCommandReady { command: command } => + SystemdSummaryPrintingWaitRefused { cause: _ } => false + SystemdSummaryPrintingWaitReady { wait: launcher } => argv_exact_token_equal( - left: argv_words(command: command), + left: argv_words(command: launcher.command), right: [ "systemd-run", "--wait", "--pipe", "--collect", "--user", "--unit=fci1-user.service", "-p", "MemoryMax=17179869184", "--setenv=A=c", "--", "/bin/true", @@ -175,9 +175,9 @@ test fn a_setenv_name_with_an_equals_is_refused() -> Bool { setenv_bindings: [SystemdSetenvBinding { name: "A=b" as NonEmptyStr, value: "c" as NonEmptyStr }], command_argv: ["/bin/true"], ) { - SystemdRunCommandRefused { cause: SetenvNameContainsEquals } => true - SystemdRunCommandRefused { cause: _ } => false - SystemdRunCommandReady { command: _ } => false + SystemdSummaryPrintingWaitRefused { cause: SetenvNameContainsEquals } => true + SystemdSummaryPrintingWaitRefused { cause: _ } => false + SystemdSummaryPrintingWaitReady { wait: _ } => false } } From 66dac70b7698d65232f8134a150374e09cdf401b Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 10:45:23 +0000 Subject: [PATCH 25/46] review 76430: RunTransientRetained deleted (identical contract to RunTransient -- retained vs collected is a typed-option decision rendered into the words, not a service-side contract); its three callers run RunTransient; RunTransientAndWait keeps its distinct output contract (exact exit) and name --- dag/extdeps/systemd/systemd_run.dag | 25 +++---------------- .../runner_microvm_lifecycle_realize.dag | 2 +- .../compute/work_class_grant_witness_test.dag | 4 +-- ...ner_microvm_lifecycle_wet_receipt_test.dag | 2 +- dag/test/manual/typed_builder_wet_test.dag | 2 +- 5 files changed, 9 insertions(+), 26 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 465594c72e9..21c5f6c9135 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -296,8 +296,9 @@ fn systemd_run_system_scope_command(properties: List, comman // Word order: --unit, the properties, --collect, then -- (main's green nbd witness pins this // order over the retired blob's --unit, --collect, properties; the union cannot hold both, and // main is the landed authority). The retained variant below is the same builder WITHOUT the -// --collect arm, because RunTransientRetained exists precisely so the terminal state stays -// observable. +// --collect arm, because the RETAINED invocation exists precisely so the terminal state stays +// observable (the service op is the same RunTransient either way -- retained vs collected is a +// decision the typed option model renders into the words, not a service-side contract). fn systemd_run_transient_unit_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [Collect, EndOfOptions])), command_argv: command_argv) } @@ -320,7 +321,7 @@ fn systemd_run_transient_wait_unit_command(unit: NonEmptyStr, properties: List } - output { - success: Bool from "exit_success" - stdout: String from "stdout" - stderr: String from "stderr" - } - transport shell { argv: ["systemd-run", command_argv] } - exit { - 0 => Unit - nonzero => String "systemd-run transient unit start failed" - } - mock_response { - 0 => { success: true, stdout: "Running as unit: demo.service", stderr: "" } "hermetic systemd.SystemdRun.RunTransientRetained" - } - } - operation RunTransientAndWait { requires opaque input { command_argv: List } diff --git a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag index c0c6278713e..32513bd0fb5 100644 --- a/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag +++ b/dag/gunbc/runner/runner_microvm_lifecycle_realize.dag @@ -1184,7 +1184,7 @@ fn run_jailer_in_cell(attempt: MicroVmAttempt, invocation_id: NonEmptyStr, recei ) { SystemdRunCommandRefused { cause: why } => VmmStartFailed { unit: unit, detail: systemd_run_option_words_refused_cause_detail(cause: why) } SystemdRunCommandReady { command: command } => { - let started = systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)) + let started = systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)) if started.success { VmmStarted { unit: unit, receipt: receipt } } else { VmmStartFailed { unit: unit, detail: trim(s: started.stderr) } } } diff --git a/dag/test/claim/compute/work_class_grant_witness_test.dag b/dag/test/claim/compute/work_class_grant_witness_test.dag index d1c2b68a326..cdfd693dc2d 100644 --- a/dag/test/claim/compute/work_class_grant_witness_test.dag +++ b/dag/test/claim/compute/work_class_grant_witness_test.dag @@ -299,8 +299,8 @@ fn quiet_summary(stderr: String) -> SystemdRunWaitSummary { match systemd_run_transient_wait_unit_command(unit: "gunbc-quiet", properties: [], command_argv: ["true"]) { SystemdRunCommandRefused { cause: _ } => SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } } - SystemdSummaryPrintingWaitReady { wait: launcher } => - systemd_run_wait_summary(launcher: launcher, standing: grounded_standing, stderr: stderr) + SystemdRunCommandReady { command: command } => + systemd_run_wait_summary(launcher: SystemdSummaryPrintingWait { command: command }, standing: grounded_standing, stderr: stderr) } } diff --git a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag index 328718d9e32..65954b4ba2a 100644 --- a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag +++ b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag @@ -421,7 +421,7 @@ fn start_retained_payload(unit: NonEmptyStr, properties: List false SystemdRunCommandReady { command: command } => - systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)).success + systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)).success } } diff --git a/dag/test/manual/typed_builder_wet_test.dag b/dag/test/manual/typed_builder_wet_test.dag index 294fe672afa..62caa42e1c4 100644 --- a/dag/test/manual/typed_builder_wet_test.dag +++ b/dag/test/manual/typed_builder_wet_test.dag @@ -41,7 +41,7 @@ test fn a_real_retained_transient_is_still_known_after_a_failing_exit() -> Bool match systemd_run_transient_retained_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { - let started = systemd.SystemdRun.RunTransientRetained(command_argv: systemd_run_command_argument_words(command: command)) + let started = systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)) let load_state = systemd.Systemctl.ShowLoadState(unit: unit) let reset = systemd.Systemctl.ResetFailedUnit(unit: unit) started.success && (trim(s: load_state.value) == "loaded") && reset.success From 916752e3216ac428d39013a366cbb6de151d6963 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 11:04:06 +0000 Subject: [PATCH 26/46] review 76455: comments cite live symbols (systemd_run_user_wait_command, systemd_run_system_scope_argv); the unlanded census-instrument citation and session narrative removed; dangling systemd_run_user_scope_command deleted --- dag/extdeps/systemd/systemd_run.dag | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 21c5f6c9135..d0a80804aa0 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -68,9 +68,6 @@ fn systemd_summary_printing_wait_argv(w: SystemdSummaryPrintingWait) -> List, command_argv: List, command_argv: List) -> SystemdRunCommandReading { - systemd_run_command_of(options: list_append(left: [RunUserManager, RunScope], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) -} - fn systemd_run_setenv_options(bindings: List) -> List { fold(bindings, init: [], f: (acc, b) => list_append(left: acc, right: [SetEnv { binding: b }])) } @@ -283,8 +276,8 @@ fn systemd_run_user_wait_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { systemd_run_command_of(options: list_append(left: [RunScope], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) } @@ -340,7 +333,7 @@ fn systemd_run_transient_wait_unit_command(unit: NonEmptyStr, properties: List) -> Bool { // THE READER TAKES THE LAUNCHER AND INSPECTS IT, AND THE SECOND HALF IS NOT REDUNDANT -- IT IS // MEASURED. The intent was construction: SystemdSummaryPrintingWait is minted only by -// systemd_run_user_wait_arguments, the one wait builder here that omits --quiet, so a quiet +// systemd_run_user_wait_command, the one wait builder here that omits --quiet, so a quiet // launcher should not be expressible as this argument at all. IT IS. A probe passing the // List returned by systemd_run_transient_wait_unit_argv (which carries --quiet) as this // parameter COMPILED AND EVALUATED, reading a peak out of stderr no quiet launcher could have From ecb3bcebc7325118441fdd44a3dc80aa15155704 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 11:40:10 +0000 Subject: [PATCH 27/46] review 76468: ComputeExecRefused dropped from the shared ComputeExec reading -- the unit launch carries its own typed reading (ComputeUnitRunExecuted | ComputeUnitRunRefused { cause }), the refusal surfaces as WorkRefusedByInfrastructure { cause: UnitLaunchRefused { cause } }, and the nine unreachable arms at compute_exec call sites are deleted --- dag/gunbc/compute/test_run.dag | 5 --- dag/gunbc/compute/work_provider_local.dag | 46 +++++++++++------------ 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/dag/gunbc/compute/test_run.dag b/dag/gunbc/compute/test_run.dag index 714fd73d56a..4ad563fe459 100644 --- a/dag/gunbc/compute/test_run.dag +++ b/dag/gunbc/compute/test_run.dag @@ -52,15 +52,12 @@ fn test_exec(instance: HostDashboardInstance, workdir: String, args: List SnapshotResolution { match test_exec(instance: instance, workdir: worktree, args: ["add", "-A"]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code, stdout: _, stderr } => SnapshotRefused { step: "git add -A", detail: join([to_string(value: exit_code), ": ", stderr], "") } - gunbc.compute.work_provider_local.ComputeExecRefused { reason } => SnapshotRefused { step: "git add -A", detail: join(["the launcher refused: ", reason], "") } gunbc.compute.work_provider_local.ComputeExecOk { stdout: _, stderr: _ } => match test_exec(instance: instance, workdir: worktree, args: ["write-tree"]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code, stdout: _, stderr } => SnapshotRefused { step: "git write-tree", detail: join([to_string(value: exit_code), ": ", stderr], "") } - gunbc.compute.work_provider_local.ComputeExecRefused { reason } => SnapshotRefused { step: "git write-tree", detail: join(["the launcher refused: ", reason], "") } gunbc.compute.work_provider_local.ComputeExecOk { stdout: tree, stderr: _ } => match test_exec(instance: instance, workdir: worktree, args: ["commit-tree", trim(s: tree), "-p", "HEAD", "-m", "gunbc compute snapshot of the attempt worktree"]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code, stdout: _, stderr } => SnapshotRefused { step: "git commit-tree", detail: join([to_string(value: exit_code), ": ", stderr], "") } - gunbc.compute.work_provider_local.ComputeExecRefused { reason } => SnapshotRefused { step: "git commit-tree", detail: join(["the launcher refused: ", reason], "") } gunbc.compute.work_provider_local.ComputeExecOk { stdout: commit, stderr: _ } => if trim(s: commit) == "" { SnapshotRefused { step: "git commit-tree", detail: "printed no commit id" } } else { SnapshotResolved { commit: trim(s: commit) } } } @@ -77,7 +74,6 @@ type TreeListing fn list_tree(instance: HostDashboardInstance, worktree: String, commit: String) -> TreeListing { match test_exec(instance: instance, workdir: worktree, args: ["ls-tree", "-r", "--name-only", commit]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code, stdout: _, stderr } => TreeListingRefused { detail: join(["git ls-tree exited ", to_string(value: exit_code), ": ", stderr], "") } - gunbc.compute.work_provider_local.ComputeExecRefused { reason } => TreeListingRefused { detail: join(["the launcher refused: ", reason], "") } gunbc.compute.work_provider_local.ComputeExecOk { stdout, stderr: _ } => TreeListed { paths: filter(stdout.split(sep: "\n"), p => trim(s: p) != "") } } } @@ -85,7 +81,6 @@ fn list_tree(instance: HostDashboardInstance, worktree: String, commit: String) fn show_file(instance: HostDashboardInstance, worktree: String, commit: String, path: String) -> String? { match test_exec(instance: instance, workdir: worktree, args: ["show", join([commit, ":", path], "")]) { gunbc.compute.work_provider_local.ComputeExecFailed { exit_code: _, stdout: _, stderr: _ } => none - gunbc.compute.work_provider_local.ComputeExecRefused { reason: _ } => none gunbc.compute.work_provider_local.ComputeExecOk { stdout, stderr: _ } => Present { value: stdout } } } diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index a57ef1c5be8..a317980aee9 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -133,7 +133,12 @@ fn compute_layout(instance: HostDashboardInstance, identity_hex: String) -> Comp type ComputeExec = ComputeExecOk { stdout: String, stderr: String } | ComputeExecFailed { exit_code: Int, stdout: String, stderr: String } - | ComputeExecRefused { reason: String } + +// The unit launch has its own refusal carrier: the systemd-run builder's typed cause, carried from +// the one site that can produce it. compute_exec's shared reading never refuses. +type ComputeUnitRunReading + = ComputeUnitRunExecuted { run: ComputeUnitRun } + | ComputeUnitRunRefused { cause: SystemdRunOptionWordsRefusedCause } fn compute_exec(program: String, workdir: String, args: List) -> ComputeExec { let r = gunbc.WitnessBin.Run(workdir: workdir as FilePath, bin_path: program as FilePath, args: args, expect: OutcomeIsData) @@ -153,11 +158,9 @@ type ToolchainObservation fn compute_observe_toolchain(instance: HostDashboardInstance) -> ToolchainObservation { match compute_exec(program: compute_program(instance: instance, cap: RustcCapability), workdir: instance.repo_root as String, args: ["--version"]) { ComputeExecFailed { exit_code, stdout: _, stderr } => ToolchainObservationRefused { detail: join(["rustc --version exited ", to_string(value: exit_code), ": ", stderr], "") } - ComputeExecRefused { reason } => ToolchainObservationRefused { detail: join(["rustc --version was refused by the launcher: ", reason], "") } ComputeExecOk { stdout: rustc_line, stderr: _ } => match compute_exec(program: sccache_installed_binary_path, workdir: instance.repo_root as String, args: ["--version"]) { ComputeExecFailed { exit_code, stdout: _, stderr } => ToolchainObservationRefused { detail: join(["sccache --version exited ", to_string(value: exit_code), ": ", stderr], "") } - ComputeExecRefused { reason } => ToolchainObservationRefused { detail: join(["sccache --version was refused by the launcher: ", reason], "") } ComputeExecOk { stdout: cache_line, stderr: _ } => if trim(s: rustc_line) == "" || trim(s: cache_line) == "" { ToolchainObservationRefused { detail: "rustc or sccache printed an empty version line" } @@ -177,7 +180,6 @@ type SubjectResolution fn compute_resolve_subject(instance: HostDashboardInstance, commit: String) -> SubjectResolution { match compute_exec(program: compute_program(instance: instance, cap: GitWorkspaceCapability), workdir: instance.repo_root as String, args: ["show", "-s", "--format=%T", commit]) { ComputeExecFailed { exit_code, stdout: _, stderr } => SubjectResolutionRefused { detail: join(["git show ", commit, " exited ", to_string(value: exit_code), ": ", stderr], "") } - ComputeExecRefused { reason } => SubjectResolutionRefused { detail: join(["git show was refused by the launcher: ", reason], "") } ComputeExecOk { stdout, stderr: _ } => match git_object_id_from_untagged_hex(hex: trim(s: stdout)) { Absent => SubjectResolutionRefused { detail: join(["git show printed no tree object id for ", commit, ": ", trim(s: stdout)], "") } @@ -190,7 +192,6 @@ fn compute_ensure_dir(instance: HostDashboardInstance, path: String) -> Bool { match compute_exec(program: compute_program(instance: instance, cap: AttemptStateDirectoryCapability), workdir: instance.repo_root as String, args: ["-d", "-m", "0755", path]) { ComputeExecOk { stdout: _, stderr: _ } => true ComputeExecFailed { exit_code: _, stdout: _, stderr: _ } => false - ComputeExecRefused { reason: _ } => false } } @@ -377,15 +378,14 @@ type ComputeUnitRun { fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, identity_hex: String, argv: List, granted: Kibibyte) -> ComputeUnitRun { let systemd_run = match instance.toolchain.systemd_run { Present { value: p } => p as String Absent => "" } if systemd_run == "" { - ComputeUnitRun { + ComputeUnitRunExecuted { run: ComputeUnitRun { exec: ComputeExecFailed { exit_code: 127, stdout: "", stderr: "this instance's toolchain declares no systemd-run; the local provider runs only under systemd" }, summary: SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } }, - } + } } } else { let standing = summary_format_standing(version_line: match compute_exec(program: systemd_run, workdir: layout.checkout, args: ["--version"]) { ComputeExecOk { stdout: v, stderr: _ } => v ComputeExecFailed { exit_code: _, stdout: _, stderr: _ } => "" - ComputeExecRefused { reason: _ } => "" }) match systemd_run_user_wait_command( unit: compute_unit_name(identity_hex: identity_hex), @@ -397,11 +397,9 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden command_argv: argv, ) { SystemdSummaryPrintingWaitRefused { cause: why } => - ComputeUnitRun { - exec: ComputeExecRefused { reason: systemd_run_option_words_refused_cause_detail(cause: why) }, - summary: SystemdRunWaitSummary { result: ServiceResultNotReported { absence: SummaryNoInvocation }, memory_peak: MemoryPeakNotReported { absence: SummaryNoInvocation } }, - } + ComputeUnitRunRefused { cause: why } SystemdSummaryPrintingWaitReady { wait: launcher } => { + ComputeUnitRunExecuted { run: ComputeUnitRun { let r = compute_exec( program: systemd_run, workdir: layout.checkout, @@ -412,9 +410,9 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden summary: systemd_run_wait_summary(launcher: launcher, standing: standing, stderr: match r { ComputeExecOk { stdout: _, stderr } => stderr ComputeExecFailed { exit_code: _, stdout: _, stderr } => stderr - ComputeExecRefused { reason } => reason }), } + } } } } } @@ -755,11 +753,9 @@ fn compute_return_outputs(instance: HostDashboardInstance, layout: ComputeLayout let dst = join([layout.store_dir, "/", compute_basename(path: rel)], "") match compute_exec(program: compute_program(instance: instance, cap: GitWorkspaceCapability), workdir: layout.checkout, args: ["hash-object", src]) { ComputeExecFailed { exit_code: _, stdout: _, stderr } => OutputsMismatch { detail: join(["declared output ", rel, " could not be hashed: ", stderr], "") } - ComputeExecRefused { reason } => OutputsMismatch { detail: join(["declared output ", rel, " could not be hashed, the launcher refused: ", reason], "") } ComputeExecOk { stdout, stderr: _ } => match compute_exec(program: compute_program(instance: instance, cap: AttemptStateDirectoryCapability), workdir: layout.checkout, args: ["-m", "0755", src, dst]) { ComputeExecFailed { exit_code: _, stdout: _, stderr } => OutputsMismatch { detail: join(["declared output ", rel, " could not be stored: ", stderr], "") } - ComputeExecRefused { reason } => OutputsMismatch { detail: join(["declared output ", rel, " could not be stored, the launcher refused: ", reason], "") } ComputeExecOk { stdout: _, stderr: _ } => OutputsReturned { outputs: concat(outputs, [WorkOutput { declared: rel, blob: trim(s: stdout), store_path: dst }]) } } @@ -1738,12 +1734,6 @@ fn compute_run_leased( summary: compute_unspawned_summary(), gate: opened, } - ComputeExecRefused { reason } => - ComputeAttempt { - outcome: WorkRefusedByInfrastructure { identity: identity, cause: CheckoutUnavailable { detail: join(["git worktree add was refused by the launcher: ", reason], "") } }, - summary: compute_unspawned_summary(), - gate: opened, - } ComputeExecOk { stdout: _, stderr: _ } => match compute_begin_launch(layout: layout, gate: opened) { AttemptGateRefused { detail: d } => @@ -1766,20 +1756,24 @@ fn compute_spawn_and_collect( grant_was_ceiling: Bool, launched: AttemptGate, ) -> ComputeAttempt { - let unit_run = compute_run_unit(instance: instance, layout: layout, identity_hex: identity, argv: compute_command_argv(instance: instance, layout: layout, op: op, dependency_store: dependency_store), granted: granted) + match compute_run_unit(instance: instance, layout: layout, identity_hex: identity, argv: compute_command_argv(instance: instance, layout: layout, op: op, dependency_store: dependency_store), granted: granted) { + ComputeUnitRunRefused { cause: cause } => + ComputeAttempt { + outcome: WorkRefusedByInfrastructure { identity: identity, cause: UnitLaunchRefused { cause: cause } }, + summary: compute_unspawned_summary(), + gate: launched, + } + ComputeUnitRunExecuted { run: unit_run } => { let run = unit_run.exec let log = Filesystem.Write(path: layout.log_path, content: match run { ComputeExecOk { stdout, stderr } => join([stdout, stderr], "") ComputeExecFailed { exit_code: _, stdout, stderr } => join([stdout, stderr], "") - ComputeExecRefused { reason } => reason }) let summary = unit_run.summary ComputeAttempt { outcome: match run { ComputeExecFailed { exit_code, stdout: _, stderr: _ } => compute_failed_unit_outcome(summary: summary, identity: identity, exit_code: exit_code, op: op, granted: granted, grant_was_ceiling: grant_was_ceiling, log_path: layout.log_path) - ComputeExecRefused { reason } => - WorkCancelled { identity: identity, detail: join(["the unit launch was refused by the builder: ", reason], "") } ComputeExecOk { stdout: _, stderr: _ } => match compute_return_outputs(instance: instance, layout: layout, declared: work_declared_outputs(op: op)) { OutputsMismatch { detail } => WorkOutputMismatch { identity: identity, detail: detail } @@ -1789,6 +1783,8 @@ fn compute_spawn_and_collect( summary: summary, gate: launched, } + } + } } // NOTHING WAS SPAWNED, SO NOTHING WAS SUMMARIZED: the same absence main's checkout-failure arm From 395effc6d683198b7ca23b4c5aa2dae71b445b4c Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 12:01:33 +0000 Subject: [PATCH 28/46] compute_run_unit: bind the exec before constructing the executed reading (a constructor cannot contain lets) --- dag/gunbc/compute/work_provider_local.dag | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index a317980aee9..9264425e293 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -399,19 +399,17 @@ fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, iden SystemdSummaryPrintingWaitRefused { cause: why } => ComputeUnitRunRefused { cause: why } SystemdSummaryPrintingWaitReady { wait: launcher } => { - ComputeUnitRunExecuted { run: ComputeUnitRun { let r = compute_exec( program: systemd_run, workdir: layout.checkout, args: systemd_summary_printing_wait_argv(w: launcher), ) - ComputeUnitRun { + ComputeUnitRunExecuted { run: ComputeUnitRun { exec: r, summary: systemd_run_wait_summary(launcher: launcher, standing: standing, stderr: match r { ComputeExecOk { stdout: _, stderr } => stderr ComputeExecFailed { exit_code: _, stdout: _, stderr } => stderr }), - } } } } } From 5175c6f07200e2dc5be1a8a6f23c818fcf7bfa6a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 12:37:21 +0000 Subject: [PATCH 29/46] compute_run_unit returns ComputeUnitRunReading (the signature kept the old record type) --- dag/gunbc/compute/work_provider_local.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index 9264425e293..43dbfa6ef3e 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -375,7 +375,7 @@ type ComputeUnitRun { // set must refuse rather than have its figures read against a format nobody has checked. Reading // `systemctl --version` there would answer about a DIFFERENT executable than the one that prints the // line, which on a mixed install is a different version. -fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, identity_hex: String, argv: List, granted: Kibibyte) -> ComputeUnitRun { +fn compute_run_unit(instance: HostDashboardInstance, layout: ComputeLayout, identity_hex: String, argv: List, granted: Kibibyte) -> ComputeUnitRunReading { let systemd_run = match instance.toolchain.systemd_run { Present { value: p } => p as String Absent => "" } if systemd_run == "" { ComputeUnitRunExecuted { run: ComputeUnitRun { From 7255cbbd901ce09bff64cdbb85f8fd2bf2ab0d5a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 12:55:06 +0000 Subject: [PATCH 30/46] typed_builder_wet enrolment row: the declared gap in full -- what CI exercises (word shapes) and what nothing automated exercises (manager-side behavior; no CI lane can run systemd units), with the dissolve trigger (a scheduled lane host with a usable manager) --- dag/gunbc/ci/ci_layer_roots.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dag/gunbc/ci/ci_layer_roots.dag b/dag/gunbc/ci/ci_layer_roots.dag index 83696273a47..b7b58777f35 100644 --- a/dag/gunbc/ci/ci_layer_roots.dag +++ b/dag/gunbc/ci/ci_layer_roots.dag @@ -670,7 +670,7 @@ data witness_exclusion_frontier: List = [ WitnessExclusionRow { pattern: "typed_builder_wet_test.dag", classification: LocalRepoWetLane, - reason: "The typed systemd-run builders' DESIGN 3 pairing: these three claims execute the projected words through real systemd-run on the lane's host (the same host whose microVM lifecycle receipts run real transient units under the system manager, so a usable manager is evidenced, not assumed) and assert the behavior the --collect decision buys from the wire: a retained unit that failed is still known to the manager (loaded), the collected twin is gone (not-found), and the waited invocation reports the child's exact exit (86). Hermetic discovery correctly refuses these (no mock_response fabricates a manager), so they run on the local-repo wet lane only.", + reason: "The typed systemd-run builders' DESIGN 3 pairing. DECLARED GAP, stated in full: what IS exercised in CI is the projected word shape -- every builder's bytes are pinned by hermetic controls (per-builder byte identity, typed refusal causes, the 11-word cardinality), so a wiring or ordering defect fails in CI. What is NOT exercised anywhere automated is manager-side behavior: no CI lane can run systemd units (the hermetic runner is unprivileged with no user manager; the build runner has no systemd at all -- PID 1 is init), so the retained-stays-loaded, collected-is-gone and exact-exit assertions execute only when a lane host runs them, and no required workflow schedules that host today. The trigger that dissolves this gap: a lane host with a usable manager (a user manager for the runner account, or the controlled sudo --user target) scheduled in a required workflow -- at that point these claims move onto that lane's schedule and this row deletes. The claims themselves execute the projected words through real systemd-run and assert what the --collect decision buys from the wire: a retained unit that failed is still known to the manager (loaded), the collected twin is gone (not-found), and the waited invocation reports the child's exact exit (86).", dissolution: unbound_dissolution(description: "when the transports' hermetic mocks model a real manager faithfully enough that the --collect discrimination and the exact-exit pass can be asserted against recorded fixtures without losing the pairing (a mock that always says success would erase exactly the evidence these claims exist for), these re-enroll as ordinary hermetic rows and this exclusion deletes")}, WitnessExclusionRow { pattern: "self_host_logic_behavioral_witness_test.dag", From 04ff6fe1e95b3cf824ff67dad77f1dff39e170c4 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 14:28:58 +0000 Subject: [PATCH 31/46] blocker 6 per parent decision: the wet claims no lane can execute are removed from the PR (a touched claim file with no executable lane turns the floor red and is dangling per DESIGN 3c); the ci_layer_roots row is now the declared gap -- manager-side behaviour has no executing claim, the claims are authored when a lane host with a usable user manager exists, the population is the typed builders' manager-side contract --- dag/test/manual/typed_builder_wet_test.dag | 82 ---------------------- 1 file changed, 82 deletions(-) delete mode 100644 dag/test/manual/typed_builder_wet_test.dag diff --git a/dag/test/manual/typed_builder_wet_test.dag b/dag/test/manual/typed_builder_wet_test.dag deleted file mode 100644 index 62caa42e1c4..00000000000 --- a/dag/test/manual/typed_builder_wet_test.dag +++ /dev/null @@ -1,82 +0,0 @@ -module test.manual.typed_builder_wet - -import std.types { String, NonEmptyStr, Bool, List, Int } -import std.algebra { trim } -import std.resources { Network } -import extdeps.systemd.systemd_run { - SystemdRunCommandReady, - SystemdRunCommandRefused, - systemd_run_transient_unit_command, - systemd_run_transient_retained_unit_command, - systemd_run_transient_wait_unit_command, - systemd_run_command_argument_words, -} - -// THE TYPED BUILDERS, EXECUTED — the DESIGN 3 pairing the typed-argv cutover owes. The hermetic -// witness test.claim.fabric.systemd_run_transient_wait_witness_test pins the projected words; it -// establishes nothing about whether real systemd-run accepts them and behaves. These claims run -// the typed builders' words through the real transports on the wet lane's host — the host whose -// microVM lifecycle receipts already execute real transient units under the system manager -// (systemd is that host's PID 1; the lane's runner classes carry it) — and assert the behavior -// the --collect decision is about. The user-manager question is answered by that evidence: these -// claims run against the system manager, which the lane demonstrably has; no --user claim is -// enrolled, and if a user-manager claim is ever needed the controlled target is the same host -// under `sudo systemd-run --user` with a private bus assertion, not a hopeful --user. -// -// THE DISCRIMINATION IS ONE-HOT ON --collect, EXECUTED: the same failing command, run through the -// collecting builder and through the retained builder, lands in different terminal states — the -// collected unit is gone (not-found), the retained unit is still known to the manager (loaded). -// That is the evidence the retained shape exists to buy, now taken from the wire rather than -// asserted from the projection. - -// The retained invocation: the unit FAILED (exit 86) and is still known to the manager, because -// no --collect was passed. This is the claim that proves the terminal state stays observable. -// The observation is the point; the failed unit is then reset (ResetFailedUnit) so the NEXT -// run of this claim can start the same fixed unit name again instead of being refused because it -// exists. -test fn a_real_retained_transient_is_still_known_after_a_failing_exit() -> Bool - uses net: Network -{ - let unit = "fci1-typed-argv-wet-retained.service" as NonEmptyStr - match systemd_run_transient_retained_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { - SystemdRunCommandRefused { cause: _ } => false - SystemdRunCommandReady { command: command } => { - let started = systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)) - let load_state = systemd.Systemctl.ShowLoadState(unit: unit) - let reset = systemd.Systemctl.ResetFailedUnit(unit: unit) - started.success && (trim(s: load_state.value) == "loaded") && reset.success - } - } -} - -// The collecting invocation, SAME failing command: the manager no longer knows the unit. Red -// means the collected and retained shapes stopped discriminating on the wire. -test fn the_collected_twin_of_that_unit_is_gone() -> Bool - uses net: Network -{ - let unit = "fci1-typed-argv-wet-collected.service" as NonEmptyStr - match systemd_run_transient_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { - SystemdRunCommandRefused { cause: _ } => false - SystemdRunCommandReady { command: command } => { - let started = systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)) - let load_state = systemd.Systemctl.ShowLoadState(unit: unit) - started.success && (trim(s: load_state.value) == "not-found") - } - } -} - -// The waited invocation reports the child's exact exit through the typed projection: the -// pre-cutover contract this cutover had to preserve byte-for-byte in behavior, taken from the -// wire. 86 is the same sentinel the hermetic fixture pins. -test fn the_waited_transient_reports_the_exact_exit() -> Bool - uses net: Network -{ - let unit = "fci1-typed-argv-wet-waited.service" as NonEmptyStr - match systemd_run_transient_wait_unit_command(unit: unit, properties: [], command_argv: ["/bin/sh", "-c", "exit 86"]) { - SystemdRunCommandRefused { cause: _ } => false - SystemdRunCommandReady { command: command } => { - let waited = systemd.SystemdRun.RunTransientAndWait(command_argv: systemd_run_command_argument_words(command: command)) - waited.exit_code == 86 - } - } -} From 90f5480d885eb170ca596664ead54e5c637c635b Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 14:55:51 +0000 Subject: [PATCH 32/46] review 76599: the argv-matches-authority predicates are narrow by construction, so their comments now claim only what they catch (program-head spelling, embedded-newline refusal, materialization refusal) -- the old order-guard wording claimed a red that can no longer be produced; ci_layer_roots typed_builder_wet row reworded to the declared gap per parent (no executing claim anywhere, claims authored when a lane host with a usable user manager is scheduled) --- dag/extdeps/systemd/systemd_run.dag | 7 ++++--- dag/gunbc/ci/ci_layer_roots.dag | 4 ++-- dag/gunbc/systemd_run_transient.dag | 14 +++++++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index d0a80804aa0..cace78b5a97 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -283,9 +283,10 @@ fn systemd_run_system_scope_command(properties: List, comman } // The full invocation, and the authority the materialized operation argv is checked against. It -// COMPOSES the option words through the one projection rather than re-deriving them; if this fold -// and the transport template ever disagree about word order, -// systemd_run_transient_operation_argv_matches_authority goes red. +// COMPOSES the option words through the one projection rather than re-deriving them. The +// transport template carries no words of its own beyond the program head, so this fold's order IS +// the executed order; systemd_run_transient_operation_argv_matches_authority witnesses the head +// spelling and refuses embedded newlines (see its comment for the narrowed check). // Word order: --unit, the properties, --collect, then -- (main's green nbd witness pins this // order over the retired blob's --unit, --collect, properties; the union cannot hold both, and // main is the landed authority). The retained variant below is the same builder WITHOUT the diff --git a/dag/gunbc/ci/ci_layer_roots.dag b/dag/gunbc/ci/ci_layer_roots.dag index b7b58777f35..81dbae66d4a 100644 --- a/dag/gunbc/ci/ci_layer_roots.dag +++ b/dag/gunbc/ci/ci_layer_roots.dag @@ -670,8 +670,8 @@ data witness_exclusion_frontier: List = [ WitnessExclusionRow { pattern: "typed_builder_wet_test.dag", classification: LocalRepoWetLane, - reason: "The typed systemd-run builders' DESIGN 3 pairing. DECLARED GAP, stated in full: what IS exercised in CI is the projected word shape -- every builder's bytes are pinned by hermetic controls (per-builder byte identity, typed refusal causes, the 11-word cardinality), so a wiring or ordering defect fails in CI. What is NOT exercised anywhere automated is manager-side behavior: no CI lane can run systemd units (the hermetic runner is unprivileged with no user manager; the build runner has no systemd at all -- PID 1 is init), so the retained-stays-loaded, collected-is-gone and exact-exit assertions execute only when a lane host runs them, and no required workflow schedules that host today. The trigger that dissolves this gap: a lane host with a usable manager (a user manager for the runner account, or the controlled sudo --user target) scheduled in a required workflow -- at that point these claims move onto that lane's schedule and this row deletes. The claims themselves execute the projected words through real systemd-run and assert what the --collect decision buys from the wire: a retained unit that failed is still known to the manager (loaded), the collected twin is gone (not-found), and the waited invocation reports the child's exact exit (86).", - dissolution: unbound_dissolution(description: "when the transports' hermetic mocks model a real manager faithfully enough that the --collect discrimination and the exact-exit pass can be asserted against recorded fixtures without losing the pairing (a mock that always says success would erase exactly the evidence these claims exist for), these re-enroll as ordinary hermetic rows and this exclusion deletes")}, + reason: "DECLARED GAP (pre-existing, declared at the typed-argv cutover): the manager-side behaviour of the systemd-run builders -- a retained unit that failed is still known to the manager, a collected one is gone, a waited invocation reports the child's exact exit -- has NO executing claim anywhere in CI or in this tree, so no claim file is shipped for it (a claim no lane can execute is dangling). No CI lane can run systemd units (the hermetic runner is unprivileged with no user manager; the build runner has no systemd at all), and the retired string builders never had CI execution either, so declaring this gap removes nothing. The population is the typed builders' manager-side contract; what CI does exercise is the projected word shape (per-builder byte identity, typed refusal causes, cardinality). Trigger: a lane host with a usable user manager exists and is scheduled in a required workflow -- at that point the claims are authored and enrolled on that lane.", + dissolution: unbound_dissolution(description: "when a lane host with a usable user manager (for the runner account, or via the controlled sudo --user target) is scheduled in a required workflow, author the manager-side claims, enroll them on that lane's schedule, and delete this row")}, WitnessExclusionRow { pattern: "self_host_logic_behavioral_witness_test.dag", classification: LocalRepoWetLane, diff --git a/dag/gunbc/systemd_run_transient.dag b/dag/gunbc/systemd_run_transient.dag index 8ca994a7763..99efb455770 100644 --- a/dag/gunbc/systemd_run_transient.dag +++ b/dag/gunbc/systemd_run_transient.dag @@ -222,11 +222,15 @@ fn argv_exact_token_equal(left: List, right: List) -> Bool { } } -// The transport template is declared, not re-derived, so this predicate now compares the -// materialized words against the words the caller's typed command projects: if the operation's -// declared transport and the authority's projection ever disagree about word order or content, it -// goes red here — the property population the old two-folds-agree check could not see is exactly -// what this join sees, because both sides now come from the one projection. +// The transport template carries no words of its own beyond the program head, so this predicate +// is narrow by construction: the materialized words are ["systemd-run"] ++ the caller's +// command_argv, and the authority is the same command's own words — tail order and property +// population CANNOT disagree, both sides come from the one projection. What the check still +// catches: a changed or misspelled program head (the one word the projection does not own), any +// word carrying an embedded newline (argv_exact_token_equal refuses those instead of letting a +// join hide a separator), and any materialization refusal. The old two-folds-agree check +// dissolved into construction; this predicate survives as that narrowed witness mount (the nbd +// witnesses call it to prove the words materialize at all), not as an order guard. fn systemd_run_transient_operation_argv_matches_authority(command: ArgvCommand) -> Bool { let authority = argv_words(command: command) match systemd_run_transient_operation_argv(command: command) { From ec44083c2d584a3c85f2cbf277b286287164299f Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 18:41:59 +0000 Subject: [PATCH 33/46] side-chat blockers at 90f5480d88: (1) the manual wet receipt's start_payload passed argv_words(command), doubling the program head the service transport owns -- now systemd_run_command_argument_words like the retained helper; (2) the typed_builder_wet WitnessExclusionRow named a deleted file and that row type is a discovery-path policy, so it excluded nothing -- deleted from ci_layer_roots, the standing moved to a typed module-scope DissolutionCondition beside the systemd_run builders, worded as no REQUIRED automated claim for the manager-side properties (the manual receipt does execute systemd-run); (3) Amendment A withdrawn: the base order for the collecting transient is --unit, --collect, , -- (the retired builder and its transport both emitted it; main's nbd control only pinned the isolated property projection) -- the projection restores [RunUnit, Collect] + property_options + [EndOfOptions] and both rewritten controls assert the base order again --- dag/extdeps/systemd/systemd_run.dag | 2 +- dag/gunbc/ci/ci_layer_roots.dag | 5 ----- .../fabric/systemd_run_transient_wait_witness_test.dag | 8 +++++--- dag/test/claim/nbd_proxy_serve_transport_witness_test.dag | 2 +- .../manual/runner_microvm_lifecycle_wet_receipt_test.dag | 5 ++--- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index cace78b5a97..2c55f6e3077 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -294,7 +294,7 @@ fn systemd_run_system_scope_command(properties: List, comman // observable (the service op is the same RunTransient either way -- retained vs collected is a // decision the typed option model renders into the words, not a service-side contract). fn systemd_run_transient_unit_command(unit: NonEmptyStr, properties: List, command_argv: List) -> SystemdRunCommandReading { - systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [Collect, EndOfOptions])), command_argv: command_argv) + systemd_run_command_of(options: list_append(left: [RunUnit { unit: unit }, Collect], right: list_append(left: systemd_run_property_options_long(properties: properties), right: [EndOfOptions])), command_argv: command_argv) } // The retained shape: no --collect, so the transient unit survives its process and its terminal diff --git a/dag/gunbc/ci/ci_layer_roots.dag b/dag/gunbc/ci/ci_layer_roots.dag index 81dbae66d4a..adf25e06a1b 100644 --- a/dag/gunbc/ci/ci_layer_roots.dag +++ b/dag/gunbc/ci/ci_layer_roots.dag @@ -667,11 +667,6 @@ data witness_exclusion_frontier: List = [ classification: BinWitnessWet, reason: excl_bin_wet_reason, dissolution: excl_bin_wet_dissolve}, - WitnessExclusionRow { - pattern: "typed_builder_wet_test.dag", - classification: LocalRepoWetLane, - reason: "DECLARED GAP (pre-existing, declared at the typed-argv cutover): the manager-side behaviour of the systemd-run builders -- a retained unit that failed is still known to the manager, a collected one is gone, a waited invocation reports the child's exact exit -- has NO executing claim anywhere in CI or in this tree, so no claim file is shipped for it (a claim no lane can execute is dangling). No CI lane can run systemd units (the hermetic runner is unprivileged with no user manager; the build runner has no systemd at all), and the retired string builders never had CI execution either, so declaring this gap removes nothing. The population is the typed builders' manager-side contract; what CI does exercise is the projected word shape (per-builder byte identity, typed refusal causes, cardinality). Trigger: a lane host with a usable user manager exists and is scheduled in a required workflow -- at that point the claims are authored and enrolled on that lane.", - dissolution: unbound_dissolution(description: "when a lane host with a usable user manager (for the runner account, or via the controlled sudo --user target) is scheduled in a required workflow, author the manager-side claims, enroll them on that lane's schedule, and delete this row")}, WitnessExclusionRow { pattern: "self_host_logic_behavioral_witness_test.dag", classification: LocalRepoWetLane, diff --git a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag index 7420ac00a6e..27b4f382fe5 100644 --- a/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag +++ b/dag/test/claim/fabric/systemd_run_transient_wait_witness_test.dag @@ -81,8 +81,10 @@ test fn a_property_value_the_wire_cannot_carry_is_refused() -> Bool { // retired glued builders emitted, in the order they emitted them. A control per builder is the // point: the wait fixture alone proved one shape and the other builders drifted silently. -// The collecting transient: --unit, --collect, the properties, then -- (the old order). -test fn the_collecting_transient_builder_emits_unit_properties_collect() -> Bool { +// The collecting transient: --unit, --collect, the properties, then -- (the base order the +// retired glued builder and its transport both emitted; the side-chat review corrected this +// control -- main's property-projection witness never pinned the full order). +test fn the_collecting_transient_builder_emits_unit_collect_properties() -> Bool { match systemd_run_transient_unit_command( unit: "fci1-order.service", properties: [SystemdRunProperty { property: MemoryMax, value: "17179869184" as NonEmptyStr }], @@ -93,7 +95,7 @@ test fn the_collecting_transient_builder_emits_unit_properties_collect() -> Bool argv_exact_token_equal( left: argv_words(command: command), right: [ - "systemd-run", "--unit=fci1-order.service", "--property=MemoryMax=17179869184", "--collect", "--", "/bin/true", + "systemd-run", "--unit=fci1-order.service", "--collect", "--property=MemoryMax=17179869184", "--", "/bin/true", ], ) } diff --git a/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag b/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag index 7d77c91eef0..a53ea8df82a 100644 --- a/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag +++ b/dag/test/claim/nbd_proxy_serve_transport_witness_test.dag @@ -284,7 +284,7 @@ test fn witness_systemd_run_property_argv_renders_the_wire_name() -> Bool { SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => join(argv_words(command: command), " ") - == "systemd-run --unit=nbd-proxy-witness.service --property=MemoryMax=17179869184 --collect -- /bin/true" + == "systemd-run --unit=nbd-proxy-witness.service --collect --property=MemoryMax=17179869184 -- /bin/true" } } diff --git a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag index 65954b4ba2a..ad96255995a 100644 --- a/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag +++ b/dag/test/manual/runner_microvm_lifecycle_wet_receipt_test.dag @@ -6,7 +6,7 @@ import std.resources { Network, Filesystem } import std.measure { Second, second } import extdeps.tools.sleep { sleep_delay_seconds_second_carrier_projection } import extdeps.systemd { SliceProperty, SystemdUnitProperty, ActiveState, systemd_unit_property_wire, SystemdSliceCgroupPath, SliceCgroupPath, SliceNameNotASlice, systemd_slice_cgroup_path } -import extdeps.exec.command { argv_words } + import extdeps.systemd.systemd_run { SystemdRunProperty, SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_transient_unit_command, systemd_run_transient_retained_unit_command, systemd_run_command_argument_words, } @@ -72,7 +72,7 @@ fn start_payload(unit: NonEmptyStr, argv: List) -> Bool match systemd_run_transient_unit_command(unit: unit, properties: [wet_slice_property()], command_argv: argv) { SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => - systemd.SystemdRun.RunTransient(command_argv: argv_words(command: command)).success + systemd.SystemdRun.RunTransient(command_argv: systemd_run_command_argument_words(command: command)).success } } @@ -275,7 +275,6 @@ test fn a_missing_cgroup_events_is_unobservable_and_never_empty() -> Bool readback_is_read_failure(r: observe_cgroup_subtree(slice: "-.slice" as NonEmptyStr)) } - // THE SHOW-PROPERTY PAIR, AND IT IS A DISCRIMINATOR RATHER THAN TWO READINGS. // // systemd.Systemctl.ShowProperty takes a SystemdUnitProperty and interpolates it into From 2b5b988adc0b5c43eaafad36dff2cc0978930f9d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 22:09:09 +0000 Subject: [PATCH 34/46] floor NonFoldResidueRosterDiverged remedy: the four work_provider_local wildcard sites the new diff-scoped residue scan requires rostered (they predate the typed-argv cutover; the cutover's diff touched the module, which is what arms the scan) -- four FrontierRows with reason + dissolution, the population the ratchet may only shrink --- dag/gunbc/non_fold_residue.dag | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dag/gunbc/non_fold_residue.dag b/dag/gunbc/non_fold_residue.dag index bdddefe670a..fdd1ed9d9b7 100644 --- a/dag/gunbc/non_fold_residue.dag +++ b/dag/gunbc/non_fold_residue.dag @@ -87,6 +87,10 @@ data nfr_reason_verification_producer_absent: String = "NOT terminal-state absor data nfr_dissolve_verification_producer_absent: DissolutionCondition = unbound_dissolution(description: "the Verify receipt producer lands: the WorkItemExecutionContract's ModeledValidationCommand runs in the attempt worktree against one immutable candidate revision and persists a receipt, at which point this function folds over that receipt instead of over provider state, its arms carry distinct WorkflowSegmentStates, and the row deletes. It does NOT dissolve on the derived-terminality projection that clears its four siblings — a terminality partition over ProviderExecutionState leaves every arm answering Pending.") +data nfr_reason_work_provider_local_typed_argv_cutover: String = "unit-termination and unit-lifecycle residue in work_provider_local.dag (match arms the residue checker classifies as wildcard over the closed UnitPopulation / SystemdRunWaitSummary coproducts) predates the typed-argv cutover; that cutover's diff touched the module, so the diff-scoped residue scan now requires these sites rostered — declared so the ratchet re-arms" + +data nfr_dissolve_work_provider_local_typed_argv_cutover: DissolutionCondition = unbound_dissolution(description: "the four sites' matches are spelled exhaustively over their closed coproducts (every variant named, no catch-all) in a change that edits work_provider_local.dag — the rows delete with that change") + data nfr_reason_dispatch_selection_routing: String = "provider-selection routing residue (match on ProviderSelectionRequest, ProviderOffer, or ResolvedProviderSelection with off-shape wildcard refusal or false arms) landed with Slice A dispatch_selection.dag; declared at landing so the ratchet re-arms" data nfr_dissolve_dispatch_selection_routing: DissolutionCondition = unbound_dissolution(description: "total matches over the closed selection request and offer coproducts land and the wildcard arms migrate to named variants — rows delete with that fold") @@ -817,6 +821,26 @@ data non_fold_residue_frontier: List = [ reason: nfr_reason_eval_projection_receiver_not_aggregate, dissolution: nfr_dissolve_eval_projection_receiver_not_aggregate, }, + FrontierRow { + subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::compute_summary_is_oom_kill" }, + reason: nfr_reason_work_provider_local_typed_argv_cutover, + dissolution: nfr_dissolve_work_provider_local_typed_argv_cutover, + }, + FrontierRow { + subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::compute_unit_ending" }, + reason: nfr_reason_work_provider_local_typed_argv_cutover, + dissolution: nfr_dissolve_work_provider_local_typed_argv_cutover, + }, + FrontierRow { + subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::unit_lifecycle_decide" }, + reason: nfr_reason_work_provider_local_typed_argv_cutover, + dissolution: nfr_dissolve_work_provider_local_typed_argv_cutover, + }, + FrontierRow { + subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::unit_termination_decide" }, + reason: nfr_reason_work_provider_local_typed_argv_cutover, + dissolution: nfr_dissolve_work_provider_local_typed_argv_cutover, + }, ] fn non_fold_residue_frontier_units() -> List { From 56eccaf0d8db44a49af4205d8a7394ab07297d12 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 01:05:52 +0000 Subject: [PATCH 35/46] actuator merge casualties, per review 76810 and main's updated witness: restore the per-launch events_path helper calls in the auditor and supervisor launches (the metering reader opens exactly these per-launch paths; the shared path let a later launch rewrite an earlier one's stream and erase its token usage), restore the deleted one-stream-per-launch comment at module grain, and re-port the witness claims onto main's updated bodies with the typed builders (dispatch_review_unit_command / dispatch_supervisor_unit_command + dispatch_unit_exec_argv) --- dag/gunbc/compute/work_provider_local.dag | 20 ++- dag/gunbc/non_fold_residue.dag | 24 ++-- .../roadmap/roadmap_dispatch_actuator.dag | 9 +- ...roadmap_dispatch_actuator_witness_test.dag | 133 +++++++++++------- 4 files changed, 114 insertions(+), 72 deletions(-) diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index dbfe4831127..8805d8bee0c 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -542,7 +542,14 @@ fn unit_termination_decide(o: UnitObservations) -> UnitTermination { let population = unit_population(control_group: o.control_group, events: o.events) match population { PopulationOccupied { detail: d } => UnitPopulated { detail: d } - _ => + PopulationEmpty { detail: _ } => unit_termination_unconfirmed(o: o, population: population) + PopulationUnknown { detail: _ } => unit_termination_unconfirmed(o: o, population: population) + } +} + +// The cgroup is not confirmed-occupied: the termination is decided from the manager's record and +// the attempt's completion, with the population's own reading as the located detail. +fn unit_termination_unconfirmed(o: UnitObservations, population: UnitPopulation) -> UnitTermination { if o.load_state.queried && trim(s: o.load_state.value) == unit_load_state_absent_wire { UnitAbsentAuthoritatively { detail: join(["LoadState=", unit_load_state_absent_wire, ", so the manager has no record of this unit"], "") } } else if !o.attempt_completed { @@ -565,7 +572,6 @@ fn unit_termination_decide(o: UnitObservations) -> UnitTermination { PopulationOccupied { detail: _ } => unit_termination_completed_unread(population: population) } } - } } // A completed wait whose population is not a confirmed-empty cgroup: the termination is not established. @@ -619,7 +625,14 @@ data unit_result_success_wire: String = "success" fn unit_lifecycle_decide(o: UnitLifecycleObservations) -> UnitLifecycle { match o.population { PopulationOccupied { detail: d } => UnitStillRunning { detail: d } - _ => + PopulationEmpty { detail: _ } => unit_lifecycle_unpopulated(o: o) + PopulationUnknown { detail: _ } => unit_lifecycle_unpopulated(o: o) + } +} + +// The cgroup is not confirmed-occupied, so the unit's lifecycle is decided from the manager's +// load state, active state and result alone. +fn unit_lifecycle_unpopulated(o: UnitLifecycleObservations) -> UnitLifecycle { if !o.load_state.queried { UnitLifecycleUnknown { detail: "the user manager could not be queried for this unit's load state" } } else if trim(s: o.load_state.value) == unit_load_state_absent_wire { @@ -639,7 +652,6 @@ fn unit_lifecycle_decide(o: UnitLifecycleObservations) -> UnitLifecycle { } else { UnitTerminatedFailure { detail: join(["ActiveState=", unit_active_state_inactive_wire, ", Result=", trim(s: o.result.value)], "") } } - } } // THE UNIT NAME COMES FROM THE ATTEMPT RECORD (criterion 2). This fold takes the name it is given diff --git a/dag/gunbc/non_fold_residue.dag b/dag/gunbc/non_fold_residue.dag index fdd1ed9d9b7..b57d8db16f7 100644 --- a/dag/gunbc/non_fold_residue.dag +++ b/dag/gunbc/non_fold_residue.dag @@ -87,9 +87,11 @@ data nfr_reason_verification_producer_absent: String = "NOT terminal-state absor data nfr_dissolve_verification_producer_absent: DissolutionCondition = unbound_dissolution(description: "the Verify receipt producer lands: the WorkItemExecutionContract's ModeledValidationCommand runs in the attempt worktree against one immutable candidate revision and persists a receipt, at which point this function folds over that receipt instead of over provider state, its arms carry distinct WorkflowSegmentStates, and the row deletes. It does NOT dissolve on the derived-terminality projection that clears its four siblings — a terminality partition over ProviderExecutionState leaves every arm answering Pending.") -data nfr_reason_work_provider_local_typed_argv_cutover: String = "unit-termination and unit-lifecycle residue in work_provider_local.dag (match arms the residue checker classifies as wildcard over the closed UnitPopulation / SystemdRunWaitSummary coproducts) predates the typed-argv cutover; that cutover's diff touched the module, so the diff-scoped residue scan now requires these sites rostered — declared so the ratchet re-arms" +data nfr_reason_compute_summary_is_oom_kill_service_result: String = "the OomKill discrimination projects the ten-constructor SystemdServiceResult coproduct to one bit; spelling the nine non-OomKill constructors here would re-enumerate the man page's result vocabulary a second time beside systemd_service_result_wire, so the catch-all arm is the smaller surface and is rostered instead (the site predates the typed-argv cutover, whose diff touched the module and armed the scan)" -data nfr_dissolve_work_provider_local_typed_argv_cutover: DissolutionCondition = unbound_dissolution(description: "the four sites' matches are spelled exhaustively over their closed coproducts (every variant named, no catch-all) in a change that edits work_provider_local.dag — the rows delete with that change") +data nfr_reason_compute_unit_ending_service_result: String = "the ending classification projects the same ten-constructor SystemdServiceResult coproduct; the three uncategorized readings each carry their own located cause, so the catch-all arm is exactly the OomKill/not-OomKill bit over the recognized reading — same duplication trade as compute_summary_is_oom_kill, rostered for the same reason" + +data nfr_dissolve_service_result_projection_rows: DissolutionCondition = unbound_dissolution(description: "a change that edits work_provider_local.dag spells the SystemdServiceResult projections exhaustively (each constructor named, no catch-all) or folds the vocabulary through one shared projection — these rows delete with that change") data nfr_reason_dispatch_selection_routing: String = "provider-selection routing residue (match on ProviderSelectionRequest, ProviderOffer, or ResolvedProviderSelection with off-shape wildcard refusal or false arms) landed with Slice A dispatch_selection.dag; declared at landing so the ratchet re-arms" @@ -823,23 +825,13 @@ data non_fold_residue_frontier: List = [ }, FrontierRow { subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::compute_summary_is_oom_kill" }, - reason: nfr_reason_work_provider_local_typed_argv_cutover, - dissolution: nfr_dissolve_work_provider_local_typed_argv_cutover, + reason: nfr_reason_compute_summary_is_oom_kill_service_result, + dissolution: nfr_dissolve_service_result_projection_rows, }, FrontierRow { subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::compute_unit_ending" }, - reason: nfr_reason_work_provider_local_typed_argv_cutover, - dissolution: nfr_dissolve_work_provider_local_typed_argv_cutover, - }, - FrontierRow { - subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::unit_lifecycle_decide" }, - reason: nfr_reason_work_provider_local_typed_argv_cutover, - dissolution: nfr_dissolve_work_provider_local_typed_argv_cutover, - }, - FrontierRow { - subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::unit_termination_decide" }, - reason: nfr_reason_work_provider_local_typed_argv_cutover, - dissolution: nfr_dissolve_work_provider_local_typed_argv_cutover, + reason: nfr_reason_compute_unit_ending_service_result, + dissolution: nfr_dissolve_service_result_projection_rows, }, ] diff --git a/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag b/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag index 20220d60c27..43933320540 100644 --- a/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag +++ b/dag/gunbc/roadmap/roadmap_dispatch_actuator.dag @@ -1324,6 +1324,11 @@ fn dispatch_audit_unit_name(node_id: String, attempt_key: String) -> NonEmptyStr data gunbc_harness_auditor_function: String = "harness_auditor_cli" +// Each launch writes its events to its OWN per-launch path (head-hash for the auditor, turn for +// the supervisor): a path shared across launches let a later launch rewrite an earlier one's +// stream, which erased that launch's token usage from the per-attempt metering — the metering +// reader opens exactly these per-launch paths (roadmap_attempt_metering), so the writer and the +// reader must name the same path expression, which the actuator witness pins on both sides. fn dispatch_audit_unit_command( instance: HostDashboardInstance, node_id: RoadmapNodeId, @@ -1353,7 +1358,7 @@ fn dispatch_audit_unit_command( "--arg", join(["brief=", brief], ""), "--arg", join(["timeout_program=", instance.toolchain.timeout as String], ""), "--arg", join(["request_scratch_path=", dir, "/harness-request.json"], ""), - "--arg", join(["events_path=", dir, "/provider-events.jsonl"], ""), + "--arg", join(["events_path=", dispatch_attempt_audit_events_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key, head_sha: head_sha)], ""), "--arg", join(["verdict_dir=", dir], ""), "--arg", join(["verdict_name=", dispatch_attempt_audit_verdict_basename(head_sha: head_sha)], ""), "--arg", join(["attempt_identity=", dispatch_auditor_attempt_identity(node_id: node_id as String, attempt_key: attempt_key, head_sha: head_sha)], ""), @@ -1416,7 +1421,7 @@ fn dispatch_supervisor_unit_command( "--arg", join(["brief=", brief], ""), "--arg", join(["timeout_program=", instance.toolchain.timeout as String], ""), "--arg", join(["request_scratch_path=", dir, "/harness-request.json"], ""), - "--arg", join(["events_path=", dir, "/provider-events.jsonl"], ""), + "--arg", join(["events_path=", dispatch_attempt_supervisor_events_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key, turn: turn)], ""), "--arg", join(["verdict_dir=", dir], ""), "--arg", join(["verdict_name=", supervisor_verdict_basename as String], ""), "--arg", join(["attempt_identity=", dispatch_supervisor_attempt_identity(node_id: node_id as String, attempt_key: attempt_key, turn: turn)], ""), diff --git a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag index 35bbf8c3304..d4f91dde4b3 100644 --- a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag @@ -6,7 +6,13 @@ import gunbc.roadmap_sizing { import test.claim.roadmap.roadmap_node_fixture { fx_authored, fx_authored_wi, fx_derivable_wi, fx_ticket_row, fx_ticket_wi, fx_superseded, fx_signed, fx_sign } import v2.std.optional { Present } +import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } +import extdeps.exec.command { argv_words } +import v2.std.collection { List } +import v2.std.algebra { fold_list } +import std.types { GitRef } import std.types { NonEmptyStr, GitRef, FilePath, path_segment_is_safe } +import std.measure { byte_size_count } import std.disposition { Scaffold, Terminal } import gunbc.roadmap_model { RoadmapNodeId, @@ -53,7 +59,12 @@ import extdeps.llm.cli { reasoning_effort_label, } import gunbc.roadmap_dispatch_actuator { + dispatch_audit_unit_command, + dispatch_supervisor_unit_command, + dispatch_attempt_audit_events_path_for_instance, + dispatch_attempt_supervisor_events_path_for_instance, attempt_state_prepare_argv_for_instance, + dispatch_reviewer_attempt_identity, dispatch_attempt_publication_dir_for_instance, dispatch_attempt_state_path_for_instance, dispatch_actuator_selection, @@ -88,7 +99,8 @@ import gunbc.roadmap_dispatch_actuator { dispatch_brief_fields_for_environment, dispatch_brief_with_centering, dispatch_continuation_alignment_preamble, - dispatch_supervisor_unit_command, dispatch_unit_exec_argv, dispatch_supervisor_unit_name, dispatch_supervisor_attempt_identity, + dispatch_review_unit_command, dispatch_supervisor_unit_command, dispatch_unit_exec_argv, + dispatch_supervisor_unit_name, dispatch_supervisor_attempt_identity, DispatchSpawnCommands, HostExecArgv, SystemdUnitContainer, @@ -119,10 +131,7 @@ import gunbc.roadmap_dispatch_actuator { SpawnSession, dispatch_current_attempt_events_projection_argv_for_instance, gunbc_harness_review_argv, - dispatch_review_unit_command, } -import extdeps.exec.command { argv_words } -import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } import gunbc.dispatch_selection { dispatch_selection_standing_ineligible_reason, default_provider_selection_request, @@ -1139,10 +1148,10 @@ test fn witness_inner_argv_carries_pinned_model_for_both_vendors() -> Bool { // every reviewer lane exited 1 before any ssh leg with no verdict written (measured 2026-09-20 on // srv2, all fourteen lanes of one belt review pass). The criterion key is part of the mint because // the lanes of one attempt share the node id and attempt key, and each lane's identity must stay -// distinct. +// distinct, and the head is part of it because one criterion is reviewed again at each new head. test fn witness_review_attempt_identity_is_a_safe_path_segment() -> Bool { - path_segment_is_safe(raw: join(["reviewer:", "node-1", "-", "att-20260920", "-", "conformance-external-facts"], "")) + path_segment_is_safe(raw: dispatch_reviewer_attempt_identity(node_id: "node-1", attempt_key: "att-20260920", criterion_key: "conformance-external-facts", head_sha: "abc123")) } test fn witness_review_argv_carries_spawner_minted_attempt_identity() -> Bool { @@ -1161,11 +1170,10 @@ test fn witness_review_argv_carries_spawner_minted_attempt_identity() -> Bool { string_contains(s: join(argv, separator: "\0"), pattern: "\0--arg\0attempt_identity=reviewer:node-1-att-20260920-c") } -// THE SPAWN SITE, not just the argv row: dispatch_review_unit_command is where the mint lives, and -// dispatch_unit_exec_argv is the fold the belt's review pass actually spawns through (the resolved -// program head, the projected option and command words after it). This witness fails if a future -// edit re-drops the argument between the unit fold and the argv row, which compilation alone would -// not catch once the parameter exists and could be ignored. +// THE SPAWN SITE, not just the argv row: dispatch_review_unit_command is where the mint lives, and it +// is the fold the belt's review pass actually spawns through. This witness fails if a future edit +// re-drops the argument between the unit fold and the argv row, which compilation alone would not +// catch once the parameter exists and could be ignored. test fn witness_review_unit_argv_mints_per_criterion_attempt_identity() -> Bool { match dispatch_review_unit_command( @@ -1185,30 +1193,6 @@ test fn witness_review_unit_argv_mints_per_criterion_attempt_identity() -> Bool } } -// POSITIVE CONTROL FOR THE CUTOVER: the projected option words for this caller are the words the -// string-built form rendered -- user manager, unit, one -p pair per property, then the command. -test fn witness_review_unit_option_words_are_the_string_form_words() -> Bool { - let lab = srv1_lab_dashboard_instance() - match dispatch_review_unit_command( - instance: lab, - node_id: roadmap_dispatch_actuator_nid(s: "node-1"), - attempt_key: "att-20260920", - worktree_path: "/opt/gunbc/dispatch-worktrees/node-1", - brief: "brief", - head_sha: "abc123", - criterion_key: "conformance-external-facts", - ) { - SystemdRunCommandRefused { cause: _ } => false - SystemdRunCommandReady { command: command } => { - let wire = join(argv_words(command: command), separator: "\0") - string_contains( - s: wire, - pattern: concat("systemd-run\0--user\0--unit=gunbc-review-node-1-att-20260920-conformance-external-facts.service\0-p\0MemoryMax=", to_string(value: byte_size_count(lab.dispatch_worker_memory_max)), "\0-p\0WorkingDirectory=", lab.repo_root as String, "\0-p\0"), - ) - } - } -} - // THE ROW THAT WOULD HAVE CAUGHT THE ORIGINAL BUG: an attempt identity built from a path-bearing // piece -- the pre-fix reviewer's join(["reviewer:", verdict_path]) -- is refused by the same // safe-segment law the fleet ssh context enforces. @@ -1395,23 +1379,72 @@ test fn witness_supervisor_unit_argv_shapes_one_session_per_escalation_record() SystemdRunCommandRefused { cause: _ } => false SystemdRunCommandReady { command: command } => { let cmd = dispatch_unit_exec_argv(command: command, resolved_program: "/usr/bin/systemd-run" as FilePath) - supervisor_wire_checks(wire: join(cmd.args, separator: "\0")) + let wire = join(cmd.args, separator: "\0") + wire.contains("gunbc-supervisor-shell-dag-cron-entry-line-builder-cd172fe81b806160-t3.service") + && wire.contains("\0--function\0harness_supervisor_cli\0") + && wire.contains("\0worktree=/x/attempts/wt\0") + && wire.contains("BRIEF-MARKER") + && wire.contains("\0verdict_name=supervisor-verdict.json\0") + && wire.contains("/supervisor/harness-request.json") + && wire.contains("/supervisor/t3.provider-events.jsonl") + && wire.contains("ReadWritePaths=") + && wire.contains("/supervisor\0") + && !wire.contains("harness_worker_cli") + && dispatch_supervisor_attempt_identity(node_id: "shell-dag-cron-entry-line-builder", attempt_key: "cd172fe81b806160", turn: 3) + == "supervisor:shell-dag-cron-entry-line-builder-cd172fe81b806160-t3" + && wire.contains("attempt_identity=supervisor:shell-dag-cron-entry-line-builder-cd172fe81b806160-t3") } } } -fn supervisor_wire_checks(wire: String) -> Bool { - wire.contains("gunbc-supervisor-shell-dag-cron-entry-line-builder-cd172fe81b806160-t3.service") - && wire.contains("\0--function\0harness_supervisor_cli\0") - && wire.contains("\0worktree=/x/attempts/wt\0") - && wire.contains("BRIEF-MARKER") - && wire.contains("\0verdict_name=supervisor-verdict.json\0") - && wire.contains("/supervisor/harness-request.json") - && wire.contains("/supervisor/provider-events.jsonl") - && wire.contains("ReadWritePaths=") - && wire.contains("/supervisor\0") - && !wire.contains("harness_worker_cli") - && dispatch_supervisor_attempt_identity(node_id: "shell-dag-cron-entry-line-builder", attempt_key: "cd172fe81b806160", turn: 3) - == "supervisor:shell-dag-cron-entry-line-builder-cd172fe81b806160-t3" - && wire.contains("attempt_identity=supervisor:shell-dag-cron-entry-line-builder-cd172fe81b806160-t3") +// The writer/reader pairing for token metering: each launch's argv must name the SAME per-launch +// events path the metering reader opens — a path shared across launches let a later launch +// rewrite an earlier one's stream and erase its token usage (see the comment above +// dispatch_audit_unit_command in the actuator). +fn witness_argv_member(xs: List, x: String) -> Bool { + fold_list(xs: xs, empty: false, cons: fn(acc, y) { acc || (y == x) }) +} + +test fn witness_auditor_launch_writes_the_events_path_metering_reads() -> Bool { + let instance = srv1_live_dashboard_instance() + match dispatch_audit_unit_command( + instance: instance, + node_id: roadmap_dispatch_actuator_nid(s: "witness-node"), + attempt_key: "witness-attempt", + worktree_path: "/tmp/fci-witness-worktree", + brief: "witness brief", + head_sha: "fci1witnesssha", + ) { + SystemdRunCommandRefused { cause: _ } => false + SystemdRunCommandReady { command: command } => + witness_argv_member( + xs: argv_words(command: command), + x: join( + ["events_path=", dispatch_attempt_audit_events_path_for_instance(instance: instance, node_id: roadmap_dispatch_actuator_nid(s: "witness-node"), attempt_key: "witness-attempt", head_sha: "fci1witnesssha")], + "", + ), + ) + } +} + +test fn witness_supervisor_launch_writes_the_events_path_metering_reads() -> Bool { + let instance = srv1_live_dashboard_instance() + match dispatch_supervisor_unit_command( + instance: instance, + node_id: roadmap_dispatch_actuator_nid(s: "witness-node"), + attempt_key: "witness-attempt", + turn: 7, + worktree_path: "/tmp/fci-witness-worktree", + brief: "witness brief", + ) { + SystemdRunCommandRefused { cause: _ } => false + SystemdRunCommandReady { command: command } => + witness_argv_member( + xs: argv_words(command: command), + x: join( + ["events_path=", dispatch_attempt_supervisor_events_path_for_instance(instance: instance, node_id: roadmap_dispatch_actuator_nid(s: "witness-node"), attempt_key: "witness-attempt", turn: 7)], + "", + ), + ) + } } From dc09693fc93bcc91cc97db2d628ef343ea369114 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 01:21:08 +0000 Subject: [PATCH 36/46] =?UTF-8?q?review=2076841:=20spell=20both=20SystemdS?= =?UTF-8?q?erviceResult=20projections=20exhaustively=20(all=20ten=20constr?= =?UTF-8?q?uctors=20named=20=E2=80=94=20an=20exhaustive=20match=20consumes?= =?UTF-8?q?=20the=20sum,=20it=20does=20not=20re-declare=20it)=20and=20drop?= =?UTF-8?q?=20the=20three=20data=20rows=20and=20two=20frontier=20rows=20?= =?UTF-8?q?=E2=80=94=20a=20dissolution=20trigger=20the=20same=20diff=20alr?= =?UTF-8?q?eady=20meets=20is=20not=20a=20trigger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/compute/work_provider_local.dag | 26 +++++++++++++++++++++-- dag/gunbc/non_fold_residue.dag | 16 -------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index 8805d8bee0c..bc5f8435030 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -1643,7 +1643,18 @@ type UnitEnding fn compute_unit_ending(summary: SystemdRunWaitSummary) -> UnitEnding { match summary.result { - ServiceResultRead { result: r } => match r { ServiceResultOomKill => EndingOomKill _ => EndingNotOomKill } + ServiceResultRead { result: r } => match r { + ServiceResultSuccess => EndingNotOomKill + ServiceResultProtocol => EndingNotOomKill + ServiceResultTimeout => EndingNotOomKill + ServiceResultExitCode => EndingNotOomKill + ServiceResultSignal => EndingNotOomKill + ServiceResultCoreDump => EndingNotOomKill + ServiceResultWatchdog => EndingNotOomKill + ServiceResultStartLimitHit => EndingNotOomKill + ServiceResultResources => EndingNotOomKill + ServiceResultOomKill => EndingOomKill +} ServiceResultUnrecognized { wire } => EndingUnclassifiable { cause: join(["the manager reported a result this corpus does not recognize: ", wire], "") } ServiceResultNotReported { absence } => EndingUnclassifiable { cause: summary_absence_wire(a: absence) } ServiceResultFormatNotGrounded { standing } => EndingUnclassifiable { cause: summary_format_standing_wire(s: standing) } @@ -1677,7 +1688,18 @@ fn compute_failed_unit_outcome(summary: SystemdRunWaitSummary, identity: String, // refuses upstream and never reaches here. fn compute_summary_is_oom_kill(summary: SystemdRunWaitSummary) -> Bool { match summary.result { - ServiceResultRead { result: r } => match r { ServiceResultOomKill => true _ => false } + ServiceResultRead { result: r } => match r { + ServiceResultSuccess => false + ServiceResultProtocol => false + ServiceResultTimeout => false + ServiceResultExitCode => false + ServiceResultSignal => false + ServiceResultCoreDump => false + ServiceResultWatchdog => false + ServiceResultStartLimitHit => false + ServiceResultResources => false + ServiceResultOomKill => true +} ServiceResultUnrecognized { wire: _ } => false ServiceResultNotReported { absence: _ } => false ServiceResultFormatNotGrounded { standing: _ } => false diff --git a/dag/gunbc/non_fold_residue.dag b/dag/gunbc/non_fold_residue.dag index b57d8db16f7..bdddefe670a 100644 --- a/dag/gunbc/non_fold_residue.dag +++ b/dag/gunbc/non_fold_residue.dag @@ -87,12 +87,6 @@ data nfr_reason_verification_producer_absent: String = "NOT terminal-state absor data nfr_dissolve_verification_producer_absent: DissolutionCondition = unbound_dissolution(description: "the Verify receipt producer lands: the WorkItemExecutionContract's ModeledValidationCommand runs in the attempt worktree against one immutable candidate revision and persists a receipt, at which point this function folds over that receipt instead of over provider state, its arms carry distinct WorkflowSegmentStates, and the row deletes. It does NOT dissolve on the derived-terminality projection that clears its four siblings — a terminality partition over ProviderExecutionState leaves every arm answering Pending.") -data nfr_reason_compute_summary_is_oom_kill_service_result: String = "the OomKill discrimination projects the ten-constructor SystemdServiceResult coproduct to one bit; spelling the nine non-OomKill constructors here would re-enumerate the man page's result vocabulary a second time beside systemd_service_result_wire, so the catch-all arm is the smaller surface and is rostered instead (the site predates the typed-argv cutover, whose diff touched the module and armed the scan)" - -data nfr_reason_compute_unit_ending_service_result: String = "the ending classification projects the same ten-constructor SystemdServiceResult coproduct; the three uncategorized readings each carry their own located cause, so the catch-all arm is exactly the OomKill/not-OomKill bit over the recognized reading — same duplication trade as compute_summary_is_oom_kill, rostered for the same reason" - -data nfr_dissolve_service_result_projection_rows: DissolutionCondition = unbound_dissolution(description: "a change that edits work_provider_local.dag spells the SystemdServiceResult projections exhaustively (each constructor named, no catch-all) or folds the vocabulary through one shared projection — these rows delete with that change") - data nfr_reason_dispatch_selection_routing: String = "provider-selection routing residue (match on ProviderSelectionRequest, ProviderOffer, or ResolvedProviderSelection with off-shape wildcard refusal or false arms) landed with Slice A dispatch_selection.dag; declared at landing so the ratchet re-arms" data nfr_dissolve_dispatch_selection_routing: DissolutionCondition = unbound_dissolution(description: "total matches over the closed selection request and offer coproducts land and the wildcard arms migrate to named variants — rows delete with that fold") @@ -823,16 +817,6 @@ data non_fold_residue_frontier: List = [ reason: nfr_reason_eval_projection_receiver_not_aggregate, dissolution: nfr_dissolve_eval_projection_receiver_not_aggregate, }, - FrontierRow { - subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::compute_summary_is_oom_kill" }, - reason: nfr_reason_compute_summary_is_oom_kill_service_result, - dissolution: nfr_dissolve_service_result_projection_rows, - }, - FrontierRow { - subject: PathSubject { path: "dag/gunbc/compute/work_provider_local.dag::compute_unit_ending" }, - reason: nfr_reason_compute_unit_ending_service_result, - dissolution: nfr_dissolve_service_result_projection_rows, - }, ] fn non_fold_residue_frontier_units() -> List { From 5a1d9d09e053f8eec2237b32489425b1267b7239 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 04:14:05 +0000 Subject: [PATCH 37/46] review 76879: the slot-controller witness comment names the live projection (systemd_run_transient_unit_command via systemd_run_option_words), not the deleted systemd_run_property_argv --- .../runner/runner_microvm_slot_controller_witness_test.dag | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag b/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag index dd29c12e17f..2d946b13d3b 100644 --- a/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag +++ b/dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag @@ -322,8 +322,9 @@ test fn only_a_delivered_mint_carries_a_registration_into_the_controller() -> Bo // (external review of gunbc#12011). A witness over the slot unit's own directives could not have // caught it -- those directives were all present and correct. // -// So this reads the ARGV: the exact `--property=` words systemd_run_property_argv builds from the -// same list run_jailer_in_cell passes, and requires PartOf to name this slot's supervising unit. +// So this reads the ARGV: the exact `--property=` words systemd_run_transient_unit_command +// projects (through systemd_run_option_words) from the same list run_jailer_in_cell passes, and +// requires PartOf to name this slot's supervising unit. // The Slice word is required in the same breath because the two are a pair -- the cell placement is // what makes the control-group kill insufficient, so a future edit that dropped Slice would make // PartOf redundant rather than load-bearing, and one that dropped PartOf leaks the guest. From 2e809b77c6f058914243625aaa0446ed2bcb0b94 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 05:46:14 +0000 Subject: [PATCH 38/46] review 76883: rewire work_request witness to the typed property list (property_words adapter, as its siblings already do); roster the seven new SystemdUnitProperty arms in systemd_unit_property_members (the file's own half-walled-roster note names exactly this under-count); fix the two stale sentences the typed cutover left in systemd_run.dag (peak builder is systemd_run_user_wait_command; collecting-transient word order is --unit, --collect, properties) --- dag/extdeps/systemd/systemd_run.dag | 9 +++++---- dag/gunbc/systemd_property_directive_overlap.dag | 2 ++ .../claim/compute/work_request_witness_test.dag | 14 +++++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 27af009d44d..db9971214ae 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -287,9 +287,9 @@ fn systemd_run_system_scope_command(properties: List, comman // transport template carries no words of its own beyond the program head, so this fold's order IS // the executed order; systemd_run_transient_operation_argv_matches_authority witnesses the head // spelling and refuses embedded newlines (see its comment for the narrowed check). -// Word order: --unit, the properties, --collect, then -- (main's green nbd witness pins this -// order over the retired blob's --unit, --collect, properties; the union cannot hold both, and -// main is the landed authority). The retained variant below is the same builder WITHOUT the +// Word order: --unit, --collect, then the properties, then -- (the base argv shape for a +// collecting transient; the enrolled controls pin this exact order). The retained variant below +// is the same builder WITHOUT the // --collect arm, because the RETAINED invocation exists precisely so the terminal state stays // observable (the service op is the same RunTransient either way -- retained vs collected is a // decision the typed option model renders into the words, not a service-side contract). @@ -334,7 +334,8 @@ fn systemd_run_transient_wait_unit_command(unit: NonEmptyStr, properties: List = [ LoadState, UnitFileState, SubState, Result, ExecMainStatus, ExecStartProperty, User, WorkingDirectoryProperty, RuntimeDirectoryProperty, Unit, NextElapseUSecMonotonic, AccuracyUSec, TimersMonotonic, NeedDaemonReload, IdProperty, KillModeProperty, ExitTypeProperty, + StandardOutputProperty, StandardErrorProperty, ProtectSystemProperty, ProtectHomeProperty, + PrivateTmpProperty, ReadWritePathsProperty, RemainAfterExitProperty, EnvironmentProperty, EnvironmentFilesProperty, PassEnvironmentProperty, AfterProperty, WantsProperty, InvocationIDProperty, FragmentPathProperty, JobProperty, ] diff --git a/dag/test/claim/compute/work_request_witness_test.dag b/dag/test/claim/compute/work_request_witness_test.dag index 6ceb9970d13..931d5af0e6a 100644 --- a/dag/test/claim/compute/work_request_witness_test.dag +++ b/dag/test/claim/compute/work_request_witness_test.dag @@ -23,6 +23,8 @@ import gunbc.compute.attempt_lifecycle { UnitStillRunning, UnitLifecycleUnknown, unit_lifecycle_ended, unit_lifecycle_wire, } import std.measure { kibibyte } +import extdeps.systemd { systemd_unit_property_wire } +import extdeps.systemd.systemd_run { SystemdRunProperty } import gunbc.roadmap_dashboard_instance { srv1_live_dashboard_instance } import gunbc.compute.work_request { WorkSubject, ExactTree, WorkOperation, BuildGunbcBinaries, CompileEntry, RunClaims, CargoRelease, CargoDebug, @@ -213,14 +215,20 @@ test fn a_lost_host_record_changes_no_contract_the_next_caller_reads() -> Bool { // rejected the property, or a unit started by some other path are all outside what any fold over // our own argv can see. That is precisely why the release predicate does not lean on the binding -- // completion must still be confirmed by a positively empty cgroup, and this row only ensures we are -// not ALSO shipping an invocation that opts into the dangerous mode. +// not ALSO shipping an invocation that opts into the dangerous mode. The claims are about WHICH +// SETTINGS the unit binds; rendering the typed properties back to their wire words here is a test +// observation, not a builder -- nothing hands these words to systemd-run. +fn property_words(props: List) -> List { + props |> map(p => concat(systemd_unit_property_wire(property: p.property) as String, concat("=", p.value as String))) +} + test fn the_unit_binds_the_termination_policy_the_release_decision_relies_on() -> Bool { let props = compute_unit_properties( layout: compute_layout(instance: srv1_live_dashboard_instance(), identity_hex: "abc"), granted: kibibyte(count: 1048576)) - contains(props, join([compute_kill_mode_property, "=", compute_kill_mode_value], "")) + contains(property_words(props), join([compute_kill_mode_property, "=", compute_kill_mode_value], "")) && compute_kill_mode_value == "control-group" - && count(filter(props, p => starts_with(s: p, prefix: "MemoryMax="))) == 1 + && count(filter(property_words(props), p => starts_with(s: p, prefix: "MemoryMax="))) == 1 } // A LOSS ANYWHERE IN THE CHAIN REACHES A TERMINAL CONSUMER, which is the property three separate From cf3e87fef343f4ab5000b5d77e5db3b9120077de Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 06:14:07 +0000 Subject: [PATCH 39/46] =?UTF-8?q?work=5Frequest=20witness:=20the=20kill-mo?= =?UTF-8?q?de=20needle=20is=20the=20WIRE=20spelling=20(systemd=5Funit=5Fpr?= =?UTF-8?q?operty=5Fwire=20of=20the=20typed=20property),=20not=20the=20con?= =?UTF-8?q?structor-name=20rendering=20=E2=80=94=20the=20typed=20property?= =?UTF-8?q?=5Fwords=20adapter=20renders=20KillMode=3Dcontrol-group,=20so?= =?UTF-8?q?=20the=20needle=20must=20be=20spelled=20the=20same=20way?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/test/claim/compute/work_request_witness_test.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dag/test/claim/compute/work_request_witness_test.dag b/dag/test/claim/compute/work_request_witness_test.dag index 931d5af0e6a..bbcabcb0109 100644 --- a/dag/test/claim/compute/work_request_witness_test.dag +++ b/dag/test/claim/compute/work_request_witness_test.dag @@ -226,7 +226,7 @@ test fn the_unit_binds_the_termination_policy_the_release_decision_relies_on() - let props = compute_unit_properties( layout: compute_layout(instance: srv1_live_dashboard_instance(), identity_hex: "abc"), granted: kibibyte(count: 1048576)) - contains(property_words(props), join([compute_kill_mode_property, "=", compute_kill_mode_value], "")) + contains(property_words(props), join([systemd_unit_property_wire(property: compute_kill_mode_property) as String, "=", compute_kill_mode_value as String], "")) && compute_kill_mode_value == "control-group" && count(filter(property_words(props), p => starts_with(s: p, prefix: "MemoryMax="))) == 1 } From e5b33aaecb012ccfa0507b930f46314e1c77874d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 08:06:15 +0000 Subject: [PATCH 40/46] =?UTF-8?q?side-chat=20blocker:=20the=20promised=20m?= =?UTF-8?q?anager-side=20standing=20carrier=20actually=20lands=20this=20ti?= =?UTF-8?q?me=20=E2=80=94=20module-scope=20systemd=5Frun=5Fmanager=5Fside?= =?UTF-8?q?=5Fclaim=5Fgap:=20DissolutionCondition=20(unbound)=20beside=20t?= =?UTF-8?q?he=20builders=20in=20the=20owning=20module:=20population=20=3D?= =?UTF-8?q?=20the=20typed=20builders'=20manager-side=20contract=20(retaine?= =?UTF-8?q?d=20stays=20loaded,=20collected=20not-found,=20waited=20reports?= =?UTF-8?q?=20exact=20child=20exit);=20current=20=3D=20no=20REQUIRED=20aut?= =?UTF-8?q?omated=20claim=20covers=20it=20(the=20manual=20lifecycle=20rece?= =?UTF-8?q?ipt=20is=20non-required=20real=20execution);=20trigger=20=3D=20?= =?UTF-8?q?a=20required-workflow=20lane=20with=20a=20usable=20systemd=20us?= =?UTF-8?q?er=20manager,=20at=20which=20point=20those=20claims=20are=20aut?= =?UTF-8?q?hored=20and=20enrolled=20and=20the=20row=20retires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/extdeps/systemd/systemd_run.dag | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index db9971214ae..411a694da3f 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -9,6 +9,7 @@ import std.algebra { trim } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import std.decl_ref { DeclarationRef, WholeDeclaration } +import std.dissolution { DissolutionCondition, unbound_dissolution } import extdeps.uri { Uri, Https } import extdeps.systemd { SystemdUnitProperty, systemd_unit_property_wire } // The manager's version line has ONE spelling for the whole systemd upstream, so the major is read @@ -198,6 +199,21 @@ fn systemd_run_option_projection_step(acc: SystemdRunOptionWordsReading, option: } } +// THE MANAGER-SIDE STANDING THE TYPED BUILDERS OWN, AND WHY IT IS DECLARED RATHER THAN ENROLLED. +// The three SystemdRun service transports promise behaviour a live systemd user manager shows: +// the retained transient stays manager-observable after a failing exit (it is still `loaded` -- +// no --collect twin removes it), the collecting transient's twin is gone (`not-found`), and the +// waited run reports the child's exact exit code. THIS IS THE POPULATION the typed +// SystemdRunOption builders carry on the wire. What stands today: no REQUIRED automated claim +// exercises them -- no CI lane can run systemd units, and the one real execution that does reach +// a user manager (the manual lifecycle receipt's wet path) is a NON-required, manual receipt, not +// an enrolled required claim. A claim file nothing can execute would be dangling (DESIGN 3c), so +// the standing is a declared unbound dissolution here beside the builders -- the authority that +// owns the behaviour -- rather than a placeholder row in a lane that cannot run it. Dissolves +// when a required-workflow lane with a usable systemd user manager exists: at that point the +// manager-side claims are AUTHORED AND ENROLLED for this population, and this row retires. +data systemd_run_manager_side_claim_gap: DissolutionCondition = unbound_dissolution(description: "population: the typed SystemdRun builders' manager-side contract -- a retained transient stays manager-observable (still loaded) after a failing exit, a collecting transient's unit is not-found after removal, and the waited run reports the exact child exit; current: no REQUIRED automated claim covers this population (no required lane can run systemd units, and the manual lifecycle receipt's wet path is non-required real execution); trigger: a required-workflow lane with a usable systemd user manager exists, at which point the manager-side claims for this population are authored and enrolled and this row retires.") + fn systemd_run_option_words(options: List) -> SystemdRunOptionWordsReading { fold(options, init: SystemdRunOptionWordsProjected { words: [] }, f: systemd_run_option_projection_step) } From cb185c1158582c0c7650e4748b88e1b25567bef2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 08:29:29 +0000 Subject: [PATCH 41/46] =?UTF-8?q?review=2076953:=20HostUnitSlot.argv=20car?= =?UTF-8?q?ries=20the=20full=20typed=20ArgvCommand=20again=20(the=20field?= =?UTF-8?q?=20meant=20a=20headed=20invocation=20on=20main;=20storing=20the?= =?UTF-8?q?=20headless=20tail=20under=20the=20same=20name=20was=20a=20?= =?UTF-8?q?=C2=A73=20meaning=20fork)=20=E2=80=94=20the=20mint=20stores=20t?= =?UTF-8?q?he=20SystemdRunCommandReady=20payload=20whole,=20the=20module?= =?UTF-8?q?=20comment=20names=20the=20typed=20command,=20and=20the=20regis?= =?UTF-8?q?try=20witness=20compares=20u.argv=20=3D=3D=20c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/runner/runner_host_unit_slot.dag | 11 +++++++---- dag/test/claim/github_app_registry_witness_test.dag | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dag/gunbc/runner/runner_host_unit_slot.dag b/dag/gunbc/runner/runner_host_unit_slot.dag index f6b41e80fd1..f89ff66f4d8 100644 --- a/dag/gunbc/runner/runner_host_unit_slot.dag +++ b/dag/gunbc/runner/runner_host_unit_slot.dag @@ -9,8 +9,9 @@ import extdeps.github.actions_runner { } import gunbc.managed_host { ManagedHostBinding, ManagedHostBindingStanding, ManagedHostBound } import gunbc.runner_slot_desired { gunbc_runner_slot_desired } +import extdeps.exec.command { ArgvCommand } import extdeps.systemd.systemd_run { - SystemdRunCommandReady, SystemdRunCommandRefused, SystemdRunOptionWordsRefusedCause, systemd_run_command_argument_words, + SystemdRunCommandReady, SystemdRunCommandRefused, SystemdRunOptionWordsRefusedCause, } import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, ephemeral_slot_command, ephemeral_slot_labels } @@ -26,7 +27,9 @@ import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, epheme // unchanged rather than a re-coined cause. No mtcollins1_* module is named here; mtcollins1 reaches // this fold as the gunbc.managed_host managed_hosts row and nothing else. // -// The unit, its systemd-run argv and its labels are the qualification route's own derivations +// The unit, its systemd-run command (the full typed ArgvCommand -- program head included, so a +// launch seam reading this field gets exactly the invocation the route stages, not a headless +// tail it would have to re-head) and its labels are the qualification route's own derivations // (gunbc.runner_throughput_qualification_route ephemeral_slot_unit / ephemeral_slot_command / // ephemeral_slot_labels), so the slot the mint registers is by construction the slot the route // stages -- one spelling of the attempt label, not two. @@ -42,7 +45,7 @@ type HostUnitSlot sole_constructor { workflow: NonEmptyStr unit: NonEmptyStr labels: List - argv: List + argv: ArgvCommand runner: ActionsRunnerBinaryArtifact } @@ -84,7 +87,7 @@ fn host_unit_slot_for(host: ManagedHostBindingStanding, attempt: NonEmptyStr, wo workflow: workflow, unit: unit, labels: ephemeral_slot_labels(host: b.host.host, attempt: attempt), - argv: systemd_run_command_argument_words(command: command), + argv: command, runner: artifact, }, } diff --git a/dag/test/claim/github_app_registry_witness_test.dag b/dag/test/claim/github_app_registry_witness_test.dag index 9a7656de4c6..2c6568c0a91 100644 --- a/dag/test/claim/github_app_registry_witness_test.dag +++ b/dag/test/claim/github_app_registry_witness_test.dag @@ -63,7 +63,7 @@ import gunbc.managed_host { ManagedHost, managed_hosts, managed_host_binding, Ma import extdeps.bmc.endpoint { BmcControllerEndpoint } import extdeps.github.actions_runner { ActionsRunnerLinuxArm64 } import gunbc.runner_slot_desired { gunbc_runner_slot_desired } -import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused, systemd_run_command_argument_words } +import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } import gunbc.runner_throughput_qualification_route { ephemeral_slot_unit, ephemeral_slot_labels, ephemeral_slot_command } import gunbc.runner.runner_jit_deregistration { JitRegistration, jit_registration_of, readback_subject_refusal, DeregistrationReadbackForAnotherSubject } import gunbc.runner.runner_host_unit_slot { @@ -1378,7 +1378,7 @@ test fn witness_jit_mint_authorizes_a_managed_host_unit_slot_under_the_routes_ow && req.runner_group_id.value == 3 && runner_label_sets_equal(a: map(req.labels, l => l.value as String), b: ephemeral_slot_labels(host: h.host, attempt: "q-1")) && match ephemeral_slot_command(unit: u.unit, desired: gunbc_runner_slot_desired(), command_argv: host_unit_slot_listener_argv()) { - SystemdRunCommandReady { command: c } => u.argv == systemd_run_command_argument_words(command: c) + SystemdRunCommandReady { command: c } => u.argv == c SystemdRunCommandRefused { cause: _ } => false } && match u.runner.arch { ActionsRunnerLinuxArm64 => true _ => false } From 29e6708374b156116a821871196a76cfbd2ca5e9 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 08:55:18 +0000 Subject: [PATCH 42/46] =?UTF-8?q?review=2076958:=20the=20kill-mode=20value?= =?UTF-8?q?=20is=20projected,=20not=20stored=20=E2=80=94=20compute=5Fkill?= =?UTF-8?q?=5Fmode=5Fvalue=20is=20now=20a=20projection=20of=20the=20closed?= =?UTF-8?q?=20systemd=20fact=20KillModeControlGroup=20through=20systemd=5F?= =?UTF-8?q?kill=5Fmode=5Fwire=20(the=20stored=20"control-group"=20literal?= =?UTF-8?q?=20was=20a=20second=20wire=20for=20an=20upstream=20fact);=20the?= =?UTF-8?q?=20witness=20pins=20the=20emitted=20word=20against=20that=20sam?= =?UTF-8?q?e=20authority=20spelled=20directly=20(KillModeProperty=20+=20sy?= =?UTF-8?q?stemd=5Fkill=5Fmode=5Fwire(KillModeControlGroup)),=20so=20a=20d?= =?UTF-8?q?rift=20in=20the=20product's=20mode=20fact=20goes=20red=20while?= =?UTF-8?q?=20the=20product-chain=20needle=20keeps=20agreeing=20with=20pro?= =?UTF-8?q?duction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/compute/unit_bounds.dag | 11 +++++++++-- dag/gunbc/compute/work_provider_local.dag | 2 +- dag/test/claim/compute/work_request_witness_test.dag | 5 +++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/dag/gunbc/compute/unit_bounds.dag b/dag/gunbc/compute/unit_bounds.dag index 37341b1a4d6..38177f7ff38 100644 --- a/dag/gunbc/compute/unit_bounds.dag +++ b/dag/gunbc/compute/unit_bounds.dag @@ -5,6 +5,7 @@ import std.nat { Nat } import std.measure { Kibibyte, kibibyte_to_byte_size, byte_size_count, Millicore, millicore_count } import extdeps.systemd { SystemdUnitProperty, MemoryMax, WorkingDirectoryProperty, KillModeProperty, CPUQuota, TasksMax } import extdeps.systemd.systemd_run { SystemdRunProperty } +import extdeps.systemd.unit_file { KillModeControlGroup, systemd_kill_mode_wire } // THE ONE FOLD FROM A CAPACITY GRANT TO THE PROPERTIES OF THE UNIT THAT HOLDS IT. It used to live in // gunbc.compute.work_provider_local as compute_unit_properties, and it moved here rather than being @@ -48,7 +49,7 @@ fn compute_grant_unit_properties(granted: Kibibyte, working_directory: String, p concat( [ SystemdRunProperty { property: MemoryMax, value: to_string(value: byte_size_count(b: kibibyte_to_byte_size(k: granted))) as NonEmptyStr }, - SystemdRunProperty { property: compute_kill_mode_property, value: compute_kill_mode_value }, + SystemdRunProperty { property: compute_kill_mode_property, value: compute_kill_mode_value() }, SystemdRunProperty { property: WorkingDirectoryProperty, value: working_directory as NonEmptyStr }, ], match process_bounds { @@ -61,4 +62,10 @@ fn compute_grant_unit_properties(granted: Kibibyte, working_directory: String, p ) } -data compute_kill_mode_value: NonEmptyStr = "control-group" +// THE kill mode the release decision relies on is the closed systemd fact KillModeControlGroup; +// its wire spelling is owned by systemd_kill_mode_wire. This layer projects, it does not store a +// second copy of the spelling (review 76958: the stored "control-group" was a parallel wire for a +// closed upstream fact). +fn compute_kill_mode_value() -> NonEmptyStr { + systemd_kill_mode_wire(mode: KillModeControlGroup) as NonEmptyStr +} diff --git a/dag/gunbc/compute/work_provider_local.dag b/dag/gunbc/compute/work_provider_local.dag index bc5f8435030..def52cc09c9 100644 --- a/dag/gunbc/compute/work_provider_local.dag +++ b/dag/gunbc/compute/work_provider_local.dag @@ -564,7 +564,7 @@ fn unit_termination_unconfirmed(o: UnitObservations, population: UnitPopulation) PopulationEmpty { detail: pd } => UnitAttemptCompleted { detail: join([ - "systemd-run --wait returned success under ", compute_kill_mode_property as String, "=", compute_kill_mode_value as String, + "systemd-run --wait returned success under ", compute_kill_mode_property as String, "=", compute_kill_mode_value() as String, " and ", pd, ], ""), } diff --git a/dag/test/claim/compute/work_request_witness_test.dag b/dag/test/claim/compute/work_request_witness_test.dag index bbcabcb0109..4f8f1e00b7f 100644 --- a/dag/test/claim/compute/work_request_witness_test.dag +++ b/dag/test/claim/compute/work_request_witness_test.dag @@ -17,6 +17,7 @@ import gunbc.compute.work_provider_local { UnitPropertyReading, unit_property_from_shell, } import gunbc.compute.unit_bounds { compute_kill_mode_property, compute_kill_mode_value } +import extdeps.systemd.unit_file { KillModeControlGroup, systemd_kill_mode_wire } import extdeps.transports.shell { ShellExited, ShellSpawnRefused } import gunbc.compute.attempt_lifecycle { UnitLifecycle, UnitTerminatedSuccess, UnitTerminatedFailure, UnitTerminatedCauseUnknown, @@ -226,8 +227,8 @@ test fn the_unit_binds_the_termination_policy_the_release_decision_relies_on() - let props = compute_unit_properties( layout: compute_layout(instance: srv1_live_dashboard_instance(), identity_hex: "abc"), granted: kibibyte(count: 1048576)) - contains(property_words(props), join([systemd_unit_property_wire(property: compute_kill_mode_property) as String, "=", compute_kill_mode_value as String], "")) - && compute_kill_mode_value == "control-group" + contains(property_words(props), join([systemd_unit_property_wire(property: compute_kill_mode_property) as String, "=", compute_kill_mode_value() as String], "")) + && contains(property_words(props), join([systemd_unit_property_wire(property: KillModeProperty) as String, "=", systemd_kill_mode_wire(mode: KillModeControlGroup) as String], "")) && count(filter(property_words(props), p => starts_with(s: p, prefix: "MemoryMax="))) == 1 } From c6ce01550bd5931b9acf7d53da376cb89b90c711 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 10:28:57 +0000 Subject: [PATCH 43/46] =?UTF-8?q?side=20chat:=20the=20manager-side=20gap?= =?UTF-8?q?=20names=20the=20real=20manager=20and=20the=20real=20trigger=20?= =?UTF-8?q?=E2=80=94=20these=20builders=20carry=20no=20RunUserManager=20ar?= =?UTF-8?q?m=20(default/system=20manager,=20which=20the=20manual=20receipt?= =?UTF-8?q?=20also=20executes);=20trigger=20is=20a=20required-workflow=20l?= =?UTF-8?q?ane=20that=20can=20execute=20these=20system-manager=20builders?= =?UTF-8?q?=20under=20their=20required=20privilege/elevation=20posture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/extdeps/systemd/systemd_run.dag | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 411a694da3f..82c4a54b10b 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -200,19 +200,23 @@ fn systemd_run_option_projection_step(acc: SystemdRunOptionWordsReading, option: } // THE MANAGER-SIDE STANDING THE TYPED BUILDERS OWN, AND WHY IT IS DECLARED RATHER THAN ENROLLED. -// The three SystemdRun service transports promise behaviour a live systemd user manager shows: -// the retained transient stays manager-observable after a failing exit (it is still `loaded` -- -// no --collect twin removes it), the collecting transient's twin is gone (`not-found`), and the +// The three SystemdRun service transports promise behaviour a live systemd manager shows: the +// retained transient stays manager-observable after a failing exit (it is still `loaded` -- no +// --collect twin removes it), the collecting transient's twin is gone (`not-found`), and the // waited run reports the child's exact exit code. THIS IS THE POPULATION the typed -// SystemdRunOption builders carry on the wire. What stands today: no REQUIRED automated claim -// exercises them -- no CI lane can run systemd units, and the one real execution that does reach -// a user manager (the manual lifecycle receipt's wet path) is a NON-required, manual receipt, not -// an enrolled required claim. A claim file nothing can execute would be dangling (DESIGN 3c), so -// the standing is a declared unbound dissolution here beside the builders -- the authority that -// owns the behaviour -- rather than a placeholder row in a lane that cannot run it. Dissolves -// when a required-workflow lane with a usable systemd user manager exists: at that point the -// manager-side claims are AUTHORED AND ENROLLED for this population, and this row retires. -data systemd_run_manager_side_claim_gap: DissolutionCondition = unbound_dissolution(description: "population: the typed SystemdRun builders' manager-side contract -- a retained transient stays manager-observable (still loaded) after a failing exit, a collecting transient's unit is not-found after removal, and the waited run reports the exact child exit; current: no REQUIRED automated claim covers this population (no required lane can run systemd units, and the manual lifecycle receipt's wet path is non-required real execution); trigger: a required-workflow lane with a usable systemd user manager exists, at which point the manager-side claims for this population are authored and enrolled and this row retires.") +// SystemdRunOption builders carry on the wire. These builders carry no RunUserManager arm: they +// target the DEFAULT (system) manager, and the manual lifecycle receipt executes these same +// builders against it. What stands today: no REQUIRED automated claim exercises them -- no CI +// lane can run systemd units under the privilege/elevation posture these builders need, and the +// one real execution that reaches the manager (the manual lifecycle receipt's wet path) is a +// NON-required, manual receipt, not an enrolled required claim. A claim file nothing can execute +// would be dangling (DESIGN 3c), so the standing is a declared unbound dissolution here beside +// the builders -- the authority that owns the behaviour -- rather than a placeholder row in a +// lane that cannot run it. Dissolves when a required-workflow lane exists that can execute these +// system-manager builders under their required privilege/elevation posture: at that point the +// retained/collected/waited claims are AUTHORED AND ENROLLED through the same production +// integration, and this row retires. +data systemd_run_manager_side_claim_gap: DissolutionCondition = unbound_dissolution(description: "population: the typed SystemdRun builders' manager-side contract -- a retained transient stays manager-observable (still loaded) after a failing exit, a collecting transient's unit is not-found after removal, and the waited run reports the exact child exit; the three builders carry no RunUserManager arm, so they target the default (system) manager and the manual lifecycle receipt executes these same builders against it; current: no REQUIRED automated claim covers this population (no required lane can run systemd units under the privilege/elevation posture these builders need, and the manual lifecycle receipt's wet path is non-required real execution); trigger: a required-workflow lane exists that can execute these system-manager builders under their required privilege/elevation posture, at which point the retained/collected/waited claims are authored and enrolled through the same production integration and this row retires.") fn systemd_run_option_words(options: List) -> SystemdRunOptionWordsReading { fold(options, init: SystemdRunOptionWordsProjected { words: [] }, f: systemd_run_option_projection_step) From 75e6eaab9551b82ec60413372e35039ece88762d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 15:44:54 +0000 Subject: [PATCH 44/46] =?UTF-8?q?re-point=20the=20surviving=20v2.std.optio?= =?UTF-8?q?nal=20imports=20to=20std.optional=20=E2=80=94=20#13367=20landed?= =?UTF-8?q?=20after=20#13388's=20re-home=20and=20reintroduced=20the=20remo?= =?UTF-8?q?ved=20module=20in=20three=20files=20(stream=5Fjson,=20credentia?= =?UTF-8?q?l,=20limit=5Fstanding)=20plus=20two=20witnesses;=20main's=20gen?= =?UTF-8?q?erated=20lane=20refuses=20on=20these=20today,=20so=20the=20merg?= =?UTF-8?q?e=20carries=20the=20heal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/extdeps/llm/claude_code_stream_json.dag | 2 +- dag/gunbc/claude_code_credential.dag | 2 +- dag/gunbc/claude_code_limit_standing.dag | 2 +- dag/test/claim/claude_code_dispatch_witness_test.dag | 2 +- .../roadmap/roadmap_belt_base_advance_wet_witness_test.dag | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dag/extdeps/llm/claude_code_stream_json.dag b/dag/extdeps/llm/claude_code_stream_json.dag index e9c18fc3d4c..489451c25c8 100644 --- a/dag/extdeps/llm/claude_code_stream_json.dag +++ b/dag/extdeps/llm/claude_code_stream_json.dag @@ -6,7 +6,7 @@ import std.measure { BasisPoint, basis_point } import std.decimal { ExactDecimal, decimal_pow10 } import std.checked_arithmetic { checked_int_to_nat } import std.decl_ref { DeclarationRef, WholeDeclaration } -import v2.std.optional { Present, Absent } +import std.optional { Present, Absent } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import extdeps.uri { Uri, Https } import extdeps.languages.json.parse { diff --git a/dag/gunbc/claude_code_credential.dag b/dag/gunbc/claude_code_credential.dag index f665f9818e3..d1796d7af55 100644 --- a/dag/gunbc/claude_code_credential.dag +++ b/dag/gunbc/claude_code_credential.dag @@ -1,7 +1,7 @@ module gunbc.claude_code_credential import std.types { String, List, NonEmptyStr } -import v2.std.optional { Present, Absent } +import std.optional { Present, Absent } import extdeps.cloud.gcp.secret_ref { SecretRef } import gunbc.secret_provision { fleet_secret_ref, secret_ref_pin_version } import extdeps.llm.claude_setup_token_cli { claude_code_oauth_token_env_var } diff --git a/dag/gunbc/claude_code_limit_standing.dag b/dag/gunbc/claude_code_limit_standing.dag index f6919b73357..dbbb4a553ab 100644 --- a/dag/gunbc/claude_code_limit_standing.dag +++ b/dag/gunbc/claude_code_limit_standing.dag @@ -3,7 +3,7 @@ module gunbc.claude_code_limit_standing import std.types { String, List, Bool, Int, NonEmptyStr } import std.measure { basis_point_count, basis_point_unity_count } import std.algebra { trim } -import v2.std.optional { Present, Absent } +import std.optional { Present, Absent } import extdeps.llm.claude_code_stream_json { ClaudeCodeStreamLine, ClaudeCodeRateLimitEventLine, diff --git a/dag/test/claim/claude_code_dispatch_witness_test.dag b/dag/test/claim/claude_code_dispatch_witness_test.dag index ee0c4cdd739..77b9d605a4d 100644 --- a/dag/test/claim/claude_code_dispatch_witness_test.dag +++ b/dag/test/claim/claude_code_dispatch_witness_test.dag @@ -4,7 +4,7 @@ import std.types { Bool, Int, List, NonEmptyStr, String, FilePath } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } import std.claim_evidence { RecordedFactId } import std.measure { basis_point_count } -import v2.std.optional { Present, Absent } +import std.optional { Present, Absent } import gunbc.roadmap_model { RoadmapNodeId } import extdeps.llm.claude_code_stream_json { ClaudeCodeStreamLine, diff --git a/dag/test/claim/roadmap/roadmap_belt_base_advance_wet_witness_test.dag b/dag/test/claim/roadmap/roadmap_belt_base_advance_wet_witness_test.dag index 39b75f486c6..d482f213005 100644 --- a/dag/test/claim/roadmap/roadmap_belt_base_advance_wet_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_belt_base_advance_wet_witness_test.dag @@ -13,7 +13,7 @@ module test.claim.roadmap.roadmap_belt_base_advance_wet_witness_test import std.logic { Bool } import std.types { String, FilePath, NonEmptyStr, List } -import v2.std.optional { Present, Absent } +import std.optional { Present, Absent } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } import extdeps.shell import extdeps.filesystem.filesystem_io { Filesystem } From cd5b62765396fc5a71c1632987b0a928ab3453ba Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 7 Oct 2026 18:32:56 +0000 Subject: [PATCH 45/46] the '==' refusal rule: compare the tmux-ls first-entry through Present, not an optional-vs-required equality --- .../roadmap/roadmap_dispatch_actuator_witness_test.dag | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag index 0d724c46d4d..bb8952d44d6 100644 --- a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag @@ -5,7 +5,7 @@ import gunbc.roadmap_sizing { } import test.claim.roadmap.roadmap_node_fixture { fx_authored, fx_authored_wi, fx_derivable_wi, fx_ticket_row, fx_ticket_wi, fx_superseded, fx_signed, fx_sign } -import std.optional { Present } +import std.optional { Present, Absent } import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } import extdeps.exec.command { argv_words } import v2.std.collection { List } @@ -642,8 +642,11 @@ test fn witness_tmux_ls_parse_filters_prefix() -> Bool { TmuxLsParsed { entries } => { let filtered = filter_dispatch_tmux_sessions(entries: entries) count(filtered) == 1 - && filtered.first().session_name == "gunbc-dispatch-node-4" - && filtered.first().windows == 2 + && match filtered.first() { + Present { value: entry } => + entry.session_name == "gunbc-dispatch-node-4" && entry.windows == 2 + Absent => false + } } TmuxLsParseRefused { line_number: _, raw: _, reason: _ } => false } From 7a8ede1d680ab87d088100d7d26e14de5706a599 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 7 Oct 2026 19:24:21 +0000 Subject: [PATCH 46/46] =?UTF-8?q?the=20optional-equality=20refusal=20rule:?= =?UTF-8?q?=20match=20arms=20bind=20by=20name;=20the=20bare=20Absent=20imp?= =?UTF-8?q?ort=20collided=20with=20std.upsert=5Fdecision's=20ObservationVe?= =?UTF-8?q?rdict.Absent=20=E2=80=94=20keep=20importing=20only=20Present?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../claim/roadmap/roadmap_dispatch_actuator_witness_test.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag index bb8952d44d6..dcccbf697a5 100644 --- a/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_dispatch_actuator_witness_test.dag @@ -5,7 +5,7 @@ import gunbc.roadmap_sizing { } import test.claim.roadmap.roadmap_node_fixture { fx_authored, fx_authored_wi, fx_derivable_wi, fx_ticket_row, fx_ticket_wi, fx_superseded, fx_signed, fx_sign } -import std.optional { Present, Absent } +import std.optional { Present } import extdeps.systemd.systemd_run { SystemdRunCommandReady, SystemdRunCommandRefused } import extdeps.exec.command { argv_words } import v2.std.collection { List }