Repository navigation
Gate 5: a modeled microvm_slot_start that starts the shakedown slot's controller once and reads that invocation's receipt - #12178
Conversation
…nPID, the 28 GiB shakedown slot row, the reservation-authorized planner arm, and the controller's release locus Deliverable 1 of the microVM slot end-to-end item: gunbc-microvm-slot@.service (Type=exec, root, ExitType=main, KillMode=control-group) execs gunbc.runner_microvm_slot_controller microvm_slot_controller_main, which composes the readiness store, the host CAS reservation re-read, the cell binding, the JIT mint, the per-slot guest shape, the reservation-authorized launch plan and the durable network receipt into one call of run_controller. The controller_main_pid_consumer_frontier row is dissolved per its trigger. The unit and the controller's release locus (gunbc + sources + Firecracker + jailer, root-owned) are installed through the live_deploy release locus, parameterized by owner rather than mirrored, as a new fleet-converge mode microvm_controller_install (a new HostEffect arm, an apply wet entry with readback). Deliverable 2: the srv1 shakedown slot (srv1-13) carries a 28 GiB MemoryMax, MemoryHigh at the ceiling and MemorySwapMax=0 through gunbc_runner_slot_desired_for, consumed per slot by the cell digest, the slice directives, the sudoers grants and the guest shape; the fleet row does not move and the expecting-red production-shape witness stays red. The width cost is derived in memory_admitted_width_of_envelope and the change is declared as gunbc.rung_drop microvm_shakedown_slot_row_above_fleet_row. Parent decision 2026-09-21: the planner gains a sealed, trust-domain-selected reservation-authorized arm (no production route constructs an ExecutionGrant), with the grant producer declared as its frontier. Two further frontiers are declared where owned: the App control-plane observation the mint needs, and the admin-edge ConvergedSlotNetwork receipt at runner_microvm_converged_receipt_path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-install job Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nted mover (run 35655377521 refused rsync as the job user) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…stall names its source relative to the job cwd Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ROOT), and both release-locus units set it from one row Parent ruling 2026-09-21: cli_run's workspace_root_from scaffold named its own dissolution -- release bins receive checkout-root at spawn -- and the first start of gunbc-microvm-slot@srv1-13 on srv1 (exit 101, 'not inside a git checkout') is the population that needs it; the approval broker's serve unit is dead on srv1 for the same reason. One env name, read at one site (workspace_root_resolve, consumed by workspace_root and process_workspace_root); when set it is the authority and the walk is not consulted, refusing typed unless the path holds dag/ and the locus's tree receipt; when unset the walk is exactly as before. Two executed controls in workspace_root_discovery_tests. v1 admission (gunbc.v1_maintenance_standing): this serves the emitted-binary door the seed already scaffolded, not v1 growth. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…controller; one Filesystem authority; dissolve markers on the two piped-install strings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… cell (the seed refuses an unlimited cgroup: HostBudgetUnreadable on srv1) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s installed and the write is checked, the stop slack is a declared row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ed at 8 GiB on srv1 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ced slice (the seed filled each bound and was killed at it on srv1) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ric_execution_slot_identities, not a count Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…w plus cache headroom (24 GiB); the planning-budget request is dropped as inert (RSS filled 8 and 12 GiB bounds alike on srv1) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…es a body annotation) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lice (parent ruling 2026-09-22); the controller figure is homed in the width authority and cites the attribution lane's frontier rather than being tuned to the symptom Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t discharged Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…an answer for UnmeasuredRoot Review 69931 (DESIGN §5, fabricated default): whole_corpus_compile_public_root_demand_peak matched the demand row and answered byte_size(0) on the UnmeasuredRoot arm. That arm is unreachable only while the roster row stays MeasuredForRoot, and this change made the figure load-bearing twice over -- it sizes the controller slice's MemoryMax and it is a term in gunbc_microvm_shakedown_slot_charge_bytes, which host_memory_admitted_width derives srv1's admitted slot count from. A zero there is not a stop: 0 + 8 GiB is a plausible cgroup bound (measured fatal -- OOM-killed at 7.9G under an 8 GiB bound) and a 16 GiB drop in the charge hands srv1 two extra admitted slots. There is no non-fabricated answer for that arm, so the fix is to have no arm. The measured figure is hoisted to whole_corpus_compile_public_root_peak, the demand row is BUILT from it, and the one process-sizing consumer reads the row directly. If this root ever becomes UnmeasuredRoot the row is deleted with the arm and every consumer refuses to compile rather than reading a default. Receipt: gunbc run --source-root dag --source-root src/v2 --entry dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag --function the_controller_unit_joins_its_own_bounded_no_swap_slice_outside_the_cell -- the closure typechecks and the claim evaluates true (the host's trailing refusal is the ProcessExit wrapper convention, not the claim). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ion in the roster The floor refused this PR's changed-witness set with an inhabitance error at two call sites in test.claim.typed_argv_exec_realization_witness_test: `privileged_commands.skip(n: 4).first()` produces Optional<PrivilegedCommand> and was handed to a `cmd: PrivilegedCommand` parameter. The defect is latent on main, not written here; what this change did was reach it. The roster is derived (grounded_principal_privileged_commands maps the principal's sudo_grants), so altering fleet membership and the sudoers projection pulls this witness into the floor's changed-witness set. Checked before attributing it to the wall: gunbc#12046's floor is SUCCESS on the same wall and gunbc#12045 refuses it in that PR's own changed file, so the wall is sound and this site is simply now within reach. Index 4 was the deeper fault and the type error was its symptom. A positional citation into a derived list re-points silently whenever the roster moves, and it never said WHICH grant was meant -- both claims assert the answer is the tailscale one. The selector now asks for it by command_path against fleet_tailscale_binary_path, the same authority the roster derives the path from, and each claim matches Present/Absent with Absent returning false: a roster that no longer carries the grant fails the claim rather than skipping it. Receipt: gunbc run --source-root dag --source-root src/v2 --entry dag/test/claim/typed_argv_exec_realization_witness_test.dag --function witness_tailscale_roster_selects_grant_list_not_execute_probe -- the closure resolves with no inhabitance error and the claim evaluates true, so the selector finds the grant rather than passing through the Absent arm. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he seed, so the seed stops transcribing them Review 69990 (DESIGN §3/§5): the spawn-root env name and the tree-receipt name each had two authors -- the .dag rows the emitter renders into the release-locus unit, and a `const` of the same spelling hand-written in cli_run.rs -- and the only thing standing over them was `gunbc_workspace_root_env_name == "GUNBC_WORKSPACE_ROOT"`, a row compared to a copy of its own value. That check stays green when the row is edited, which is precisely §5's tell. If either side moved alone every release-locus unit would exit 101 in workspace_root at start, the failure the spawn arm exists to repair. NOT the prescribed remedy, and the substitution is deliberate. The review asked for a witness reading cli_run.rs, on the precedent of the hand-Rust equivalence witnesses. Two reasons against: a check over two independently-authored literals is a drift DETECTOR that fires after the fork already exists, and a new tree-reading witness is a new WET witness, which v2.workflow.floor_changed_witness blocks for any identity the PR touches (RouteGapBeforeVerdict and DeclinedDiscoveryExcluded both map to PlannedWithoutTerminalVerdict) -- so the prescribed form likely cannot reach a verdict on this PR at all. So the fork is removed instead of watched. gunbc.release_locus_seed_constants_emit projects both rows into release_locus_seed_constants_generated.rs, registered in the generated-artifact roster and its drift wall, and cli_run.rs consumes them rather than declaring its own. The join is now the Rust compiler plus the drift gate: a .dag spelling moves the seed at the next regen, and a hand edit of the generated file is refused. Construction over validation, and the same argument gunbc.evaluation_budget_consequence_emit already records for refusing a runtime read of the .dag. The surviving claim asserts the projection CARRIES each row's value. The emitter never spells either literal, so it goes red if a row moves and the projection does not. Receipts, both executed: - ctrl-build --remote -- bash -lc 'ls -la src/v1/stage0/src/release_locus_seed_constants_generated.rs && cargo check -p v1-compiler --lib' -- the file is present (500 bytes) and the seed compiles clean against the generated consts. The ls is a control: an earlier run reported Finished over an UNTRACKED generated file that the mirror could not have carried, and that green proved nothing. - gunbc run --entry dag/test/claim/cli_run_workspace_root_hand_rust_witness_test.dag --function workspace_root_spawn_arm_names_its_env_receipt_and_controls -- true. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…arms the new artifact variant owed TWO THINGS, the second a repair of the first commit in this pair. 1. workflow_dispatch_choice_input_projects_options_list discriminates at ONE interface -- workflow_trigger_entry turning a WorkflowDispatch with a choice input into a yaml mapping whose `mode` carries an `options` sequence. It reached that interface by forcing fleet_converge_workflow and taking its first trigger, which constructs every DispatchInput's description (one is a multi-thousand-character string) for a fact about none of them: 528572 eval steps against a 72300 budget, rising with every input any lane adds. It now supplies the trigger, which is the construction two sibling claims in the same file already use. The predicate was ALSO fusing two subjects, which is why supplying alone did not work: it hard-wired fleet_converge_mode_options, so it asserted the PROJECTION and the CONTENT at once. Only the first is a fact about workflow_trigger_entry, and the second is true by construction -- the workflow literal writes `options: fleet_converge_mode_options`, so comparing against that row restates what the source already carries (DESIGN §5), and the roster's own coherence is established by every_dispatch_option_is_a_wire_value_of_the_vocabulary. The expected list is now a parameter, so the discrimination is intact: all options reach the yaml as strings, none dropped and none added. The pairing obligation is named on the carrier: the real producer is still run end to end by fleet_converge_workflow_has_build_job_needs_release_bins and the rendered-yaml claims, so deleting them would leave this supplied input unpaired. 2. ReleaseLocusSeedConstantsGeneratedRsArtifact left two GeneratedArtifact matches non-exhaustive -- gunbc.ledger_row_coherence and gunbc.instruments.docs_projection_gate -- so the previous commit does not resolve. The exhaustive-match wall is what caught it; nothing I ran before pushing reached either module, which is the defect in the receipt rather than in the wall. Both arms are added. Receipt: gunbc run --source-root dag --source-root src/v2 --entry dag/test/claim/workflow_dispatch_input_witness_test.dag --function workflow_dispatch_choice_input_projects_options_list -- the closure resolves and the claim evaluates true. The discriminating red is recorded in the same pair of runs: with the predicate still hard-wired to the fleet roster the supplied two-option trigger returned FALSE, so the parameter is load-bearing rather than decorative. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…authorable red, and the third non-exhaustive match Review 70028, both findings, plus the resolution break that was the actual CI blocker. 1. THE CENSUS ROW WAS FALSE. cli_run_workspace_root_discovery_loc_delta_net binds `v1_compiler.cli_run workspace_root_from` at WholeDeclaration and stood at 12 -- the .git-ancestor kernel alone -- while this change grows exactly that declaration. A row left at 12 is the parallel ledger DESIGN §6 refuses: it states a figure the tree contradicts, and its only consumer (`... > 0`) cannot see it move. Re-derived to 70, and the INSTRUMENT is named on the carrier rather than the count alone (§6): added non-blank lines of this scaffold's hand-Rust in cli_run.rs outside its #[test] module, 35 + 20 + 3 = 58, with the 42 test lines excluded on the same basis the original 12 excluded its own. 12 + 58 = 70. 2. TWO CONJUNCTS WERE DECORATION INSIDE AN OTHERWISE REAL CLAIM, and the review is right that this is the same objection the claim's own note raises one conjunct earlier. `string_contains(s: <row>, pattern: "walk_is_not_consulted")` compares a row to a slice of its own value and stays green after the seed test it names is renamed or deleted. Deleting them outright would have left both discriminator rows with no consumer at all (§3c), so the claim now asserts the scaffold names FOUR DISTINCT, non-empty controls. That has an authorable red and it was RUN: setting the refusal row to the authority row's value -- the copy-paste that makes an unwritten control look covered -- returns false; restored, it returns true. What the replacement does NOT establish is stated on the carrier: that the seed declares those names. No claim here can, because a witness reading cli_run.rs is a new WET witness and v2.workflow.floor_changed_witness blocks any such identity its own PR touches. Also corrected: this change's own de-fork had made the scaffold's prose false -- it still said "the seed transcribes both spellings" after the seed had stopped transcribing them. 3. THE FLOOR BLOCKER WAS A THIRD NON-EXHAUSTIVE MATCH, generated_artifact_emit artifact_extra_valid, a SECOND match over GeneratedArtifact in a file I had already edited once. My earlier search grepped a sibling variant while excluding the files I had touched, which is why it found two sites and not three. The five sites are now enumerated by arm count; generated_workflow_provenance carries `_ => none` and needs nothing. The cost rows this run was meant to re-measure were never reached, so those figures are still owed. Receipt: gunbc run --source-root dag --source-root src/v2 --entry dag/test/claim/cli_run_workspace_root_hand_rust_witness_test.dag --function workspace_root_spawn_arm_names_its_env_receipt_and_controls -- true, and false under the planted duplicate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…fold that cannot says why Review 70050. The sharpest form of the finding is the one worth answering: this change MINTS a ByteSize authority (whole_corpus_compile_public_root_peak) and then unwraps it on the next line to add a bare Int, which gunbc.runner_microvm_slot_unit re-wraps one module later. std.measure measure_add is the existing surface for adding two measures of one quantity and scale, so reaching past it is DESIGN §2's re-invention of a concept that already has a home. That the sibling gunbc_runner_slot_memory_max_bytes sits on main in the same file is prevalence, not precedent. SUMMED IN THE CARRIER, where the carrier is this change's to move: - microvm_controller_slice_memory_max() returns ByteSize via measure_add over the peak row and microvm_controller_slice_cache_headroom (now a ByteSize row). The pass-through in runner_microvm_slot_unit that re-wrapped the Int is DELETED rather than retyped -- two names for one fact is the §3 fork, and the module imports the authority directly now. - gunbc_microvm_shakedown_slot_memory_max is a ByteSize row; the two slice directives that wrapped it with byte_size(...) read it directly. - gunbc_microvm_shakedown_slot_charge() returns ByteSize via measure_add. - microvm_slot_unit_timeout_stop() returns Second via measure_add over the five bound rows. Second is Measure<Time, One, Nat>, so the same surface applies verbatim; the projection to a scalar moved to microvm_slot_unit_timeout_stop_sec, which is the one place the rendered directive needs it. TAGGED RATHER THAN CONVERTED, once, with the reason on the carrier: memory_admitted_width_of_envelope divides by gunbc_runner_slot_memory_max_bytes, an Int row standing on main and not this change's to move. Converting the fold alone would take a ByteSize, unwrap it to divide by that row, and re-wrap -- the same carrier drop relocated rather than removed. It carries `🟡 dissolve-on: feature:fleet-slot-row-on-a-measure-carrier`, naming measure_fit_count_floor as the surface that owns the quotient and its zero-divisor guard once the fleet row carries ByteSize. This is the second arm the review itself offers. Two annotations that cited the renamed symbols are corrected in the same change; a citation that no longer resolves is a §3 defect, not a cosmetic. Receipt: all four claims over the changed arithmetic execute true -- the_controller_unit_joins_its_own_bounded_no_swap_slice_outside_the_cell, the_unit_stop_timeout_covers_the_controllers_declared_bounds, the_shakedown_slot_carries_28_gib_no_swap_and_no_throttle_line_while_the_fleet_row_stands, the_shakedown_slot_is_charged_its_cell_plus_its_controller_before_the_fleet_row_divides. Running all four rather than only the one naming a renamed symbol is deliberate: twice in this lane a receipt whose closure did not reach the consumers missed a break that the floor then found. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ledger-Repair-Judged: docs/design-rung-drops.md Heal-Candidate-Run: 35699456339
…with the new generated artifact THE LAST COST BLOCKER. fleet_converge_workflow_has_build_job_needs_release_bins is about the job partition and its needs edges, and it read them off fleet_converge_workflow -- which forces the WHOLE row, every job's steps AND every DispatchInput description under `on`, one of which runs to thousands of characters, to establish seven ids: 521845 eval steps against a 72300 budget, identical across two separate runs (so the claim's own deterministic work, not contention), and rising with every job or input any lane adds. The seven jobs were written inline in the Workflow literal, which made the workflow the only route to the fact. They are now `fleet_converge_jobs()` in the owning module and the workflow reads it. That is a single authority with the workflow as one consumer, not a test affordance bolted on: the roster is where the jobs are declared, and there is no second list to drift. THE COMPANION FIX IS SECOND-ORDER AND IS WHY THE GATE WAS RUN AT ALL. Registering ReleaseLocusSeedConstantsGeneratedRsArtifact and emitting its .rs was not sufficient: .gitattributes is ITSELF derived from the artifact roster, so a new artifact moves it too, and the committed file was stale by exactly the one line that binds the new path to the refusing merge driver. Regenerated through main_wet on the gate rather than hand-edited, which is what the finding itself instructs. Receipts: - gunbc run --entry dag/test/claim/workflow_dispatch_input_witness_test.dag --function fleet_converge_workflow_has_build_job_needs_release_bins -- true. - gunbc run --entry dag/gunbc/instruments/generated_artifact_gate.dag --function main -- the only finding was .gitattributes. fleet-converge.yml is ABSENT from it, which is the receipt that matters for the roster move: lifting the jobs into a function is emission-neutral, so the committed workflow yaml is unchanged. - The eval-step figure for the roster-reading form comes from CI; this change does not claim it clears 72300 until that run reports it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ion/quiet-koi-746
…he one its shape suggests Review 70080. Three annotations -- gunbc.live_deploy.emit release_locus_tree_copy_steps, gunbc.runner_microvm_slot_unit, gunbc.runner_microvm_slot_controller -- justified the root-owned locus as buying "a tree root execs that the job user cannot write" / "root must not exec a binary the job user can write". On srv1 that protection does not hold, and the tree already says so: ghrunner holds NOPASSWD over a PATH-UNRESTRICTED /usr/bin/install on every host carrying /etc/sudoers.d/gunbc-deploy, install(1) is the very mover the install step elevates with, and the class is rostered at gunbc.rung_drop the_job_user_holds_a_path_unrestricted_root_grant and gunbc.recurring_failure_mode the_grant_a_frontier_refuses_is_already_held_from_another_authority. That is DESIGN §4b(1) inflation in this change's own prose: a class's rung is the MINIMUM across its in-scope paths, and these sentences cited the strongest while the granted-mover path stayed silent. It was also inconsistent with this same diff twelve lines away, where the guest-image note states the shakedown makes no isolation claim rather than implying one. Each carrier now separates the two facts. WHAT THE LOCUS BUYS: the unprivileged-by-default write path is removed -- nothing lands bytes there without a granted privileged mover, so an ordinary job step, a stray tool or a mistaken relative path cannot -- and the unit execs this tree's VMM rather than the job user's HOME copy. WHAT IT DOES NOT BUY: a locus the job user cannot write, because the same principal reaches it through the same grant. The standing drop is named at each site, with the statement that this change adds no grant and removes none, so the stronger claim becomes true when that drop retires rather than when this locus lands. Comments only; no declaration, value or emitted byte moves. Receipt: gunbc run --source-root dag --source-root src/v2 --entry dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag --function the_slot_unit_execs_the_controller_entry_as_main_pid_with_the_declared_teardown -- true, and the annotation-grain sweep over the three files reports no body-grain block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…racted against the measurement MEASURED, AND THE HYPOTHESIS IS FALSIFIED. The job roster was extracted to bring test.claim.workflow_dispatch_input_witness fleet_converge_workflow_has_build_job_needs_release_bins under its floor budget, on the reading that `on` -- whose DispatchInput descriptions run to thousands of characters each -- dominated the row. Two required-floor runs: whole Workflow row : 521845 eval steps roster : 521510 eval steps budget : 72300 335 steps, 0.06%. The cost is constructing the seven Job values themselves, step bodies included; a List<Job> projection forces every one, so reading the roster cannot avoid it. The suspect was wrong. The extraction is KEPT, on the ground that survives: the jobs were inline in the Workflow literal, which made the workflow the only route to the job partition, and the roster is now that authority with the workflow as one consumer. What is retracted is the cost claim -- both annotations now carry the two figures and state the refutation, because an annotation asserting a saving this lane measured away is the same overclaim review 70080 flagged in this change's prose one commit ago. What would actually be cheap is named rather than implied: a roster of ids and needs edges ALONE, consumed by the seven job functions for those two fields. That restructures all seven and is not attempted here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…, consumed by the seven builders Parent ruling, item 2. Each of the seven job builders hardcoded its own `id` and `needs`, so the graph -- which jobs exist, and which must wait for the release binaries -- was authored in seven places and owned nowhere. FleetConvergeJobEdge is that graph, and all seven builders now take their needs from fleet_converge_job_needs(id:); no literal needs list survives in a builder. THE LOOKUP REFUSES RATHER THAN INVENTS. An absent id would otherwise fall to an empty needs list, which is the one wrong answer that still emits: a host-touching job with no edge to `build` installs whatever binaries happened to be on the host. FleetConvergeJobEdgeAbsent carries the id and the needs projection renders a REFUSED line instead of a dependency. WHY THIS IS NOT THE PREVIOUS ATTEMPT REPEATED. Lifting the JOBS into their own list bought 335 eval steps of 521845, because forcing a List<Job> forces every Job including every step body -- the projection WAS the construction, now filed as gunbc.recurring_failure_mode extracting_a_collection_whose_elements_are_the_cost. These edges are ids and lists of strings built independently of any Job, so a consumer reading the graph forces no job body. That is a difference in mechanism, stated before the measurement rather than hoped for after it. The claim reads the edges; its inhabitance obligation stays with the rendered-yaml claims in the same file, which run the real workflow end to end -- so what is established is the graph the workflow EMITS, not the graph a builder was told. Receipts: - gunbc run --entry dag/test/claim/workflow_dispatch_input_witness_test.dag --function fleet_converge_workflow_has_build_job_needs_release_bins -- true. - RED, run: dropping the microvm-controller-install edge row fails the claim. Reported honestly -- it fails as an evaluation error on the now-absent index rather than a clean false, so the count conjunct is carrying part of that red. - gunbc run --entry dag/gunbc/instruments/generated_artifact_gate.dag --function main -- ZERO findings, so fleet-converge.yml is byte-identical with every needs list now computed through the lookup. That is the receipt that matters: the refactor is emission-neutral. - The eval-step figure comes from CI. This change does not claim it clears 72300 until that run reports it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review 70098. outcome_is_success was an exact re-mint of gunbc.command_runner process_outcome_admitted -- same three arms, same verdicts -- in a module that already imports from that very module, so the canonical name was one import member away. DESIGN §3: a second name for one concept, and §2's test that net concepts must not grow by re-invention. The canonical function even documents this exact caller shape (a `test -f` whose product is the exit status and whose stdout nobody wants), so the re-mint did not even carry a different rationale. Deleted, with both call sites -- path_is_executable and path_exists -- now asking process_outcome_admitted. The extdeps.shell.exec import went with it: ProcessOutcome and its three variants were reachable from this module ONLY through the deleted function, so keeping the line would trade a §3 fork for an unused import. (The neighbouring bare `import extdeps.shell` is pre-existing and untouched.) Receipt: gunbc run --source-root dag --source-root src/v2 --entry dag/test/claim/runner/runner_microvm_slot_controller_witness_test.dag --function the_controller_selects_only_a_fabric_member_out_of_its_instance_name -- the closure resolves with the import removed and the claim evaluates true. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… dying Parent ruling: a control that DIES is weaker evidence than one that ANSWERS false, because an evaluation error is also what a typo, a renamed field or a broken fixture produces -- so the red stops discriminating the thing it was built for. THE PRESCRIBED REMEDY DOES NOT APPLY HERE, and the evidence was already in hand: the count conjunct was ALREADY first (`edges.length() == 7 && edges[0].id == ...`) when the control died on the absent index. `&&` in this evaluator does not spare its later operands, so no reordering turns that into a false. So the claim is TOTAL instead -- it reads no index at all. The projected id list is compared against the expected spelling (count, membership and order in one structural comparison) and the edge property is folded with `all`: build needs [], every other row needs [build]. Same content as the indexed form, nothing weakened. Receipts, both run: - green: the claim returns true. - red: dropping the microvm-controller-install edge row returns FALSE -- a clean answer, where the indexed form produced an evaluation error under the same edit. An eval error from this claim now means something else genuinely broke, which is the separation the ruling asked for. Roster restored to seven rows after the control. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tionID, and property reads are typed (review 70683) systemctl start on an active unit is a no-op that exits zero, so the step could have read an EARLIER start's receipt as this dispatch's. The unit must now be inactive before the start (SlotStartAlreadyActive names the running invocation) and the InvocationID after the start must differ from the one read before it (SlotStartInvocationUnchanged). ShowProperty failures are UnitPropertyUnread with their detail instead of an empty string, carried into exec_main_status and into SlotStartInvocationUnread. Witness reds gain both new arms. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review 70683: both findings held and are fixed at d1d6b2a.
The witness red set now includes both new arms. 2/2 PASS, typechecks. — sent from quiet-koi-746 |
… projected only when rendered (review 70696) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review 70696: fixed at 32a178a. On the previous head's floor red ( — sent from quiet-koi-746 |
…operation_command instead of a duplicate of it The previous heads re-authored executor_privileged_operation_command, same name and same body, in gunbc.executor_privileged_operation and admitted it on argv_command. Two declarations of one name is a second authority for one fact (DESIGN 3), and the floor refused on it (AmbiguousBareNameRead, surfaced as PureProducerShareRowModuleUnframeable in namespace_xl2_rehearsal_census). Both files are restored to main's bytes and the slot start imports the existing builder. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Root cause of the floor red on the last two heads, fixed at 1919c8a: my own duplicate. I had re-authored — sent from quiet-koi-746 |
…sed unit-name witness is deleted and the step timeout rounds up (review 70720) fleet_converge_workflow imported gunbc.runner_microvm_slot_start for one string, which pulled 17 modules (the controller, lifecycle, guest image, launch planner and more) into the workflow emitter's closure; the floor refused on that head. The receipt path now lives with the unit's on-host layout in gunbc.runner_microvm_slot_unit, which main's workflow closure already contains; the workflow and ci_spec read it there (ci_spec no longer spells it a second time). The fleet workflow's closure now adds nothing over main's. Review 70720: the_started_unit_is_the_shakedown_slots_own_instance compared a function to its own body and is deleted; the step timeout uses ceiling division as its annotation says. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review 70720 is fixed at 4a9d139. The same commit makes a second attempt at the floor red.
Floor. The duplicate builder in 1919c8a was a real defect, but it was not the only one: the refusal persisted. The workflow emitter imported — sent from quiet-koi-746 |
…iguousBareNameRead; the boot-probe comment returns to its own function (review 70740) The floor's refusal named its sites on the lines after the refusal: gunbc.runner_guest_image and gunbc.runner_microvm_host_ready read Filesystem bare, and the slot-start module brings both declaring modules (extdeps.filesystem.filesystem_io's service and std.resources) into one scope, so the bare read would be resolved by scope precedence. Both call Filesystem.Write, the extdeps service, and now import it from there, as the refusal's remedy directs. Review 70740: the new step had been inserted between the boot-probe upload step and its leading comment; the comment is back above its function, and the new upload step states its own always() rationale. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Fixed at b4ec93b: review 70740, plus the floor red's actual cause.
— sent from quiet-koi-746 |
…bels' into session/quiet-koi-746-slot-start
…s YamlEmitRefused arm (main's #11730 replaced serialize_yaml) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…bels' into session/quiet-koi-746-slot-start # Conflicts: # .github/workflows/fleet-converge.yml
…-slot-start # Conflicts: # .github/workflows/fleet-converge.yml # dag/gunbc/fleet/fleet_converge_workflow.dag # docs/design-rung-drops.md
… per-file claim (no refusals, zero uses) main's census requires every executed workflow file to carry a roster row and a per-file claim; the new shakedown workflow had neither, so the floor's roster join failed. Its claim asserts zero uses rather than census_holds's uses > 0: the job is action-free by design, an unreadable file refuses rather than reading as empty, and a uses: added later reds it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…bels' into session/quiet-koi-746-slot-start
…diff, applied three-way Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Regenerate fleet-converge.yml and design-rung-drops.md from the merged authorities. The shakedown job from #12178 carries Job.environment (none), which this branch adds to extdeps.github.actions Job. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Gate 5 of the microVM real-run plan: nothing issued
systemctl startfor the slot unit.microvm_controller_installwrites the locus and thegunbc-microvm-slot@template and starts no instance, so every real-run receipt (5a job, 5b second attempt, 5c cancel, 5d 28 GiB floor) had no first step.What changes
gunbc.runner_microvm_slot_start(microvm_slot_start_ci_wet):gunbc.runner_microvm_host_readymicrovm_host_binding_wet, the one the boot probe reads) against the shakedown slot's host. A dispatch pinned elsewhere refuses by name.gunbc.runner_slot_allocationsrv1_microvm_shakedown_slot. The instance is never a dispatch input. The start goes through the modeled privileged operationSystemdStartUnit, whose argv the sudoers match is derived from. It starts a root-owned unit thatmicrovm_controller_installwrote, so the job chooses no byte the root process evaluates.InvocationIDand waits for inactive. The bound is the slot unit's ownmicrovm_slot_unit_timeout_stop(): the VMM deadline plus every teardown bound, the same sum systemd enforces.gunbc.runner_microvm_slot_controllerslot_controller_receipt_path), so an earlier start's receipt cannot stand for this one.microvm_slot_start. It is a step in the existing fleet-converge job, alongsidemicrovm_boot_probe, so no new job is added. Its timeout is derived from the same stop bound. A receipt-upload step is added.fleet-converge.ymlis regenerated.gunbc.executor_privileged_applyexecutor_privileged_operation_command, the existing typed builder over the sameexecutor_privileged_operation_elevatedderivation the sudoers match reads. Nothing new is admitted onargv_command.What running it will show today
The controller refuses at the JIT mint: #12119 makes it pass
RunnerGroupRestrictionUnobserveduntil bright-dove-353's #12155, #12172 and PR-C land. It may refuse earlier on the slot-network receipt (conntrack-tools is missing on srv1). Either way the controller receipt names the stage, which is the "exercised to the refusal arm" evidence the brief asks for before the network lands.Evidence (fresh claim_batch)
runner_microvm_slot_start_witness_test: 2/2 PASS.workflow_dispatch_input_witness_test(enumerates the modes): 30/30 PASS.🤖 Generated with Claude Code