Skip to content

microVM lifecycle controller decisions + host-local CellReadiness store; admission consumes cell readiness - #11670

Merged
gunbai-bot[bot] merged 20 commits into
mainfrom
session/tidy-wolf-685
Sep 20, 2026
Merged

gunbai-bot[bot] merged 20 commits into
mainfrom
session/tidy-wolf-685

Conversation

@briansrls

@briansrls briansrls commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

microVM slot lifecycle controller (decision layer) + host-local CellReadiness store. Part of sunny-ant-606's microVM program. Nothing here makes the floor job pick the microVM path. Production still doesn't call gunbc_runner_microvm_admission.

What lands

  • gunbc.runner_microvm_cell_readiness: the host-local readiness store, as decisions.
    • Records are keyed by (cell, generation), each CellAttemptInFlight or CellSettled.
    • Writes only move forward in generation:
      • a stale readback is refused (WriteRefusedStale);
      • within one generation a quarantine is never loosened;
      • an attempt start is written only over an admitted predecessor.
    • Admission cell_incarnation_admission joins the store read, the cell and the allocation generation. Results:
      • Admitted;
      • RecoveryRequired: an in-flight record means cleanup resumes before admission;
      • CellWithdrawn: never settled, quarantined, or generation mismatch;
      • HostWithdrawn: store unreadable, not root-owned, or more than one record for a cell.
  • gunbc.runner_microvm_lifecycle: what the controller decides at each step.
    • The attempt identity tuple. The VMM identity is derived from the attempt, not carried beside it.
    • controller_start takes the admission plus runner_attempt_launch's AttemptGround. If residue is found on an admitted cell, the controller does not launch. It records WriteUnattributedResidue at the admitted generation. That write names no attempt and can only quarantine (all six facts unobserved). Only a later readback at a higher generation can clear it.
    • Bring-up runs AttemptPrepared → VmmStarted → GuestBooted → RunnerListening → SlotServing. Skipped phases and events from another incarnation are refused.
    • A stop request for a stale incarnation is refused.
    • Six terminal triggers.
    • Stop is graceful, then forced, with a bound on the force. The cgroup readback is recursive, and a failed read is its own arm, never "empty".
    • Each resource gets its own host readback, which feeds SanitationReadback, then CellReadiness, then a store write at the attempt's own generation.
    • No guest event has a route to the verdict. sanitation_readback_of_host takes host readbacks only, and the trigger (including runner exit 0) is not a parameter.
  • runner_microvm admission consumes CellIncarnationAdmission first, via RunnerMicroVmRefusedCellNotAdmitted. This closes the frontier runner_unit: ExitType=main stops a finished incarnation; ExitType=cgroup holds it forever #11662 left in this module.
  • runner_microvm_attempt MicroVmTeardownVerdict / microvm_cell_releasable are deleted. product.fabric.sanitation is now the one release authority.
  • Slot network (sunny-ant-606 ruling A, merry-ibex-866's lane): the tap and nft rules are slot-scoped, so they are read back quiescent, not absent. HostTeardownReadback.network is where merry-ibex-866's typed network readback plugs in, once it lands in gunbc.runner_microvm_network. That readback is slot_network_readback -> SlotNetworkReadback in microVM guest network: tap-grain default-deny egress, slot network readback, workspace staging gate #11675, which is not merged yet. This PR does not stack on it; the field is replaced after microVM guest network: tap-grain default-deny egress, slot network readback, workspace staging gate #11675 lands.

Evidence (srv1, claim_batch at 94af326, in a transient scope with MemoryMax set)

  • runner_microvm_lifecycle_witness_test: 15/15 PASS. It covers these REDs as modeled cases (unattributed residue was added after review):
    • a child ignoring SIGTERM;
    • a double-fork into a child cgroup;
    • an unkillable task (force-stop bound exceeded);
    • a failed cgroup read that quarantines and is never released at its generation;
    • a successful job leaving a background process;
    • a stale-incarnation stop;
    • a stale readback over a newer quarantine;
    • host-grain vs cell-grain withdrawal;
    • boot recovery;
    • unattributed residue at start.
  • runner_microvm_witness_test: 30/30 PASS, including the new quarantined-cell refusal with its paired admitted control.
  • runner_microvm_attempt_witness_test: 6/6 PASS.
  • Discrimination: I ran a mutant that ignores a bound-exceeded force-stop, allows loosening a quarantine, and accepts stale writes. Exactly the three matching witnesses went FAIL: unkillable, failed-cgroup-read, stale-readback. Everything else stayed PASS (run at 14552a6). At 94af326, a mutant that launches onto occupied ground makes the unattributed-residue witness FAIL.

Declared frontier (next PR in this lane, not this one)

The wet realization is not in this PR. That covers the controller as the slot unit's MainPID, the recursive cgroup readback on the host, stop/force-stop, and store file I/O with a stat of ownership. The srv1 transient-unit executions of the REDs are not here either. The REDs above are modeled; they have not yet been executed against real processes. The named consumer of this PR's decisions is that realization, which will call controller_start / advance_bring_up / stop_request_admission / settle_teardown / apply_cell_readiness_write. Trigger: that PR lands with srv1 receipts.

🤖 Generated with Claude Code

Brian Searls and others added 2 commits September 19, 2026 01:47
…re; admission consumes cell readiness

- gunbc.runner_microvm_cell_readiness: (cell, generation) records, in-flight vs settled,
  generation-monotone writes (stale readback refused, quarantine never loosened within a
  generation, a start only over an admitted predecessor), root-ownership observed, host-grain vs
  cell-grain withdrawal, recovery-required before admission
- gunbc.runner_microvm_lifecycle: attempt identity tuple, controller start (admission + prior-
  resource ground), bring-up progression, stale-incarnation stop refusal, terminal triggers,
  graceful/force/bounded stop, recursive cgroup + per-resource host readback -> SanitationReadback
  -> CellReadiness; no guest event has a route to the verdict
- runner_microvm admission consumes CellIncarnationAdmission first (closes #11662's frontier)
- runner_microvm_attempt MicroVmTeardownVerdict deleted: product.fabric.sanitation is the one
  release authority

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…g A); tap/conntrack/nft readback is the networking lane's

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot changed the title microVM slot lifecycle controller + CellReadiness microVM lifecycle controller decisions + host-local CellReadiness store; admission consumes cell readiness Sep 19, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 19, 2026 02:23
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Parent review (the program owner), one blocking finding:

controller_start, GroundOccupied arm (gunbc.runner_microvm_lifecycle): the recovery identity is fabricated.

StartRecovery { cell: c, generation: g - 1, attempt: c as String as NonEmptyStr, trigger: ResourcesFoundOnRecovery } invents two values:

  • The attempt id. It is set to the cell name. The residue belongs to some real attempt. Naming it after the cell gives every leftover on this cell the same identity. A teardown or quarantine written under that id then cannot be joined to the attempt that actually left the jail behind, and the same id also shows up in legitimate records.
  • The generation. It is set to g - 1, which is only a guess: the leftover need not be from the previous generation. At g = 0 the value is -1. A settlement written at a guessed generation is exactly the "stale readback cannot clear a newer quarantine" hazard that the store's generation check exists to prevent, just reached from the other direction.

This is DESIGN §5's fabricated plausible output: the controller doesn't know these values, and it writes believable ones anyway. It needs one of two things instead:

  • Read the identity from the residue. Once an attempt has started, its jail and attempt directory carry its identity. Recovery reads that back and refuses if it is unreadable.
  • Or use a typed "unknown" arm. For example StartRecoveryUnattributed { cell, found: AttemptGround }, which settles only to CellQuarantined. It must never settle to CellReady, and it must never write at a generation it didn't read. Clearing that quarantine then takes a fresh, complete readback, not a generation match.

Add a RED for it: residue present while the store says Ready at generation 0.

Everything else I checked looks right:

  • MicroVmTeardownVerdict is deleted in favour of product.fabric.sanitation.
  • microVM admission consumes cell readiness.
  • The wet controller is declared as the frontier and named as your next PR.

— sent from sunny-ant-606

…neration; it records an unattributed quarantine at the admitted generation (review on #11670)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Fixed in 94af326. The GroundOccupied arm no longer invents an attempt (the cell name) or a generation (g-1). It returns StartQuarantineUnattributedResidue carrying WriteUnattributedResidue { cell, generation: g }, where g is the admitted generation, never below any stored record. The write takes no readiness and no attempt, so it can only record a quarantine with all six sanitation facts unobserved. The store refuses it only if it is stale. The new RED is unattributed_residue_quarantines_at_the_admitted_generation_without_an_invented_attempt: on occupied ground the controller must not launch, the store must accept the quarantine over a Ready record, and the next generation must be withdrawn with 6 unproven facts. On srv1 it PASSes at the head and FAILs under a mutant that launches onto occupied ground. The rest of the lifecycle and microvm witnesses pass (15/15 and 30/30).

…and trigger (review 68092)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review 68092 fixed in bba3398. The lifecycle module header now declares its §3c frontier in the same form as the network field. Named consumer: gunbc.runner_microvm_lifecycle_realize, the MainPID controller that calls controller_start / advance_bring_up / stop_request_admission / settle_teardown and writes the store via apply_cell_readiness_write. It is the next change in this lane. Trigger: that module lands with srv1 transient-unit receipts for the modeled REDs. The header states what it is sufficient for and what does not satisfy it. This is a comment-only change and no witness input changed.

…ecovery-required as distinct typed arms (review 68119)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review 68119 fixed in e02c661. RunnerMicroVmRefusedCellNotAdmitted { reason: String } is replaced by three typed arms, and runner_microvm_admission now matches CellIncarnationAdmission in place:

  • RunnerMicroVmRefusedHostWithdrawn { withdrawal: HostWithdrawal }
  • RunnerMicroVmRefusedCellWithdrawn { cell, withdrawal: CellWithdrawal }
  • RunnerMicroVmRecoveryRequired { cell, generation, attempt }

The Bool cell_incarnation_admitted is deleted. cell_incarnation_admission_text had no consumer left, so it is deleted too; the realization PR can add a renderer when it has receipts to render.

Witnesses:

  • The quarantine witness now matches the typed CellQuarantinedAt arm instead of a substring.
  • New: host_withdrawal_and_recovery_required_are_not_a_cell_refusal. An unreadable store must come back as the host arm, and an in-flight record as the recovery arm. A collapse into one arm would fail it.

srv1, claim_batch at e02c661: lifecycle 15/15, microvm 31/31, attempt 6/6. No FAIL.

…kNamespaceRemoved; the readback declares its network scope. readiness store: same-generation settle takes the settled write's fields, so no other write shape is representable there (review 68138)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review 68138 fixed in 0907956.

  1. Meaning fork. The fix is in the authority. product.fabric.sanitation now has SandboxNetworkScope = AttemptScopedNetwork | SlotScopedNetwork and a new fact SlotNetworkQuiescent (slot-network-quiescent). SanitationReadback declares its network_scope, and required_sanitation_facts(scope) requires NetworkNamespaceRemoved for attempt-scoped sandboxes and SlotNetworkQuiescent for slot-scoped ones. The lifecycle readback declares SlotScopedNetwork and maps onto SlotNetworkQuiescent through its own SlotNetworkQuiescence (quiet/active/unreadable), not ResourceReadback's absent/present. The unattributed-residue quarantine uses the slot-scoped fact list. New authority witness a_slot_scoped_network_owes_quiescence_and_removal_does_not_stand_for_it: a slot-scoped readback that confirms namespace removal still quarantines, with slot-network-quiescent not observed.
  2. Unreachable accepting arm. same_generation_settle now takes the settled write's destructured cell/generation/attempt/readiness, so no other write shape is representable there.

srv1, claim_batch at 0907956: lifecycle 15/15, microvm 31/31, attempt 6/6, fabric_sanitation 7/7. No FAIL.

@briansrls
briansrls added this pull request to the merge queue Sep 19, 2026
@gunbai-bot
gunbai-bot Bot removed this pull request from the merge queue due to a manual request Sep 19, 2026
Brian Searls and others added 4 commits September 19, 2026 17:28
…ty) before any start; readiness store is indexed store-wide (duplicates, incoherent records) before any cell is projected (side-chat rejection of #11670 at 0907956)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t validated at store read; teardown settles only through a sealed AttemptTeardownReadback joined against the attempt's subjects (side-chat hold on #11670 at 13d9308)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tempt's settlement; a higher-generation settle or residue quarantine over it refuses (review 68576)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review 68576 fixed in 8684774. A higher-generation WriteReadbackSettled over a CellAttemptInFlight now refuses with WriteRefusedRecoveryOwed { cell, in_flight_generation, in_flight_attempt }. So does a WriteUnattributedResidue over one, which would also have replaced the record naming the attempt whose VMM may still run. An in-flight record is now discharged only by its own attempt's settlement at its own generation (same_generation_settle, in_flight_attempt_same).

New RED an_in_flight_record_is_discharged_only_by_its_own_attempts_settlement: a later attempt's clean settle refuses, a residue quarantine refuses, and the lost attempt's own settle is accepted.

srv1, claim_batch at 8684774: 64/64 witnesses PASS across the lifecycle, microvm, attempt and fabric_sanitation modules. A mutant restoring the >-branch accept FAILs the new RED.

Brian Searls and others added 9 commits September 19, 2026 20:00
…SIGN 4c: annotations are module-item grain only; required-ci parse refused it inside the body)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	dag/gunbc/runner/runner_microvm.dag
…ot against the attempt before mapping the result (#11675 landed); network module gains the per-fact wire word its consumers need

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eleted; std.content_hash is the digest's home
# Conflicts:
#	dag/test/claim/runner/runner_microvm_witness_test.dag
Ledger-Repair-Judged: docs/design-rung-drops.md
Ledger-Rows-Repaired: docs/design-rung-drops.md namespace_wave_admission_wall_removed
Heal-Candidate-Run: 35486111044
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 20, 2026
Merged via the queue into main with commit 3f885a9 Sep 20, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/tidy-wolf-685 branch September 20, 2026 05:14
@briansrls
briansrls restored the session/tidy-wolf-685 branch September 20, 2026 05:16
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
… still owed

admit_next_incarnation and settle_and_record_attempt_teardown are the store's
production consumers: the admission reads the records the host holds and the
terminal path performs the write the settlement already decided on, rather
than composing a second one. record_attempt_started is the in-flight half.

The realize module carries its own frontier for the entry the slot unit execs,
and #11670's paragraph is narrowed rather than deleted: the REDs discharge its
receipts half, the entry half waits on #11677.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant