Repository navigation
Conversation
…erve An exactly clean stopped Development pool reuses its retained disks on restart, so admission charges only the 16 GiB host reserve instead of the 58 GiB fresh allocation budget. Clean requires a quiesced stop of the last boot, a creation receipt, rootfs pin, both disk identities and declared sizes, the recorded executable and user, a dead provider, a free VM lock, no open disk handle or owned listener, and no pending owner update. Foreign, active, dirty and stale owners refuse with closed codes and keep the fresh-allocation budget. The selection is rechecked around every sample, under the startup lease, around the pre-boot sample and immediately before machine start, and never reaches machine create. Running and Research admission are unchanged. Refs: HACK-1195 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stopped Development pool qualifies for the 16 GiB retained-disk reserve only with a clean-stop marker that `down` records in the same receipt write as its `stopped` phase. Every other phase write and every new boot clear it, so a pre-boot refusal that rewrites the phase to `stopped` after an uncertain or unclean stop no longer relabels it as clean. The stopped-pool checks now have two layers. The receipt checks are read-only and lock-free, and run around every sample and in `runtime probe`. The release checks (VM lock, lsof, sockets and the provider config audit) run only under the startup lease. `machine create` and `machine start` now need typed authorities (`FreshCreate`, `StartPermit`) that only the admission pool issues, after the pre-boot sample on the qualifying basis and a final leased recheck. Fresh samples next to a created stopped pool report the closed `retained_reserve_refusal` code. Refs: HACK-1195 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Choice accessors used only by the macOS-only admission pool tests were compiled for every test build, so Linux test builds failed -D dead-code. Use the same cfg as the tests module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A clean stopped Development pool selected the 16 GiB retained reserve on every restart, so its leased release checks (VM lock, `lsof` over the owned disks, listeners, provider configuration) ran even when free disk fit the unchanged 58 GiB fresh budget. On a loaded host the 5 s `lsof` bound timed out and the start was refused as `retained_reserve_active_owner`, where the base branch restarted the same pool on the fresh budget without any `lsof` scan. Once the startup lease is held, `settle` now drops a stopped selection whose every qualifying sample, including the lease recheck, also fits the fresh budget. Those samples are recorded on the fresh basis and the start continues exactly as one without a selection, with no release checks. Only a start whose free disk lies between the host reserve and the fresh budget keeps the selection; its release checks bound `lsof` at 30 s instead of `down`'s 5 s. `down`, `recover` and other stop paths keep the 5 s bound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…alifies for `runtime probe` takes no startup lease and does not settle a basis, so for a qualifying clean stopped pool it reports the host-reserve basis even when a start with the same headroom would drop the selection and use the fresh budget. State that the start's `admission.json` records the basis the start actually used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Linear: HACK-1195. This implements the bounded October 2 follow-up: tell a verified retained-disk restart apart from a fresh disk allocation.
Development admission now treats an exactly clean stopped pool like a verified running pool for the disk budget. A restart reuses the pool's retained 32 GiB storage disk and 10 GiB overlay. So admission charges only the 16 GiB host reserve (
disk_budget_basis: verified-stopped-owned-pool-host-reserve), not the 58 GiB fresh-allocation floor. This is the October 2 M3 Event Agent case, where a start with 58,010,112,000 free bytes was refused even though it would allocate nothing new.This revision fixes the defects the verifier found at
ea0d434b.Final behavior
"Clean" is a marker that only
downcan completeclean_stop. It holds{guest_boot_id, daemon_pid, daemon_start}.downwrites it, throughfinish_quiesced_stop. That write happens in the same receipt write as itsstoppedphase, after three things are proven: the guest acknowledged flush and shutdown of a quiesced engine, the provider is gone, and the provider released the VM lock, disk handles and sockets.phase()clears the marker on every phase other thanstopped, andOwner::begin_bootclears it on every new boot.stopped(lifecycle.rspre-boot refusal). That rewrite now:down;downwritesquiescence.json, then hitsstop_uncertain;recoverwritesrecovered-unclean; a later start is refused before boot and writesstopped. Thestopped-before-enginerewrite is closed the same way.stopped()requires a marker that matches the receipt's boot and engine identity, as well as the existing matching quiescence receipt.down.Two layers of checks, and no provider lock outside the lease
runtime probe. They are read-only, take no lock and run no host tool. They require:stopped;owner.pending;created, a rootfs pin, both disk identities and the declared 32/10 GiB sizes;released) run only while the startup lease is held. They need a&state::Lock, whose path is verified. They require a free VM lock (flock), no open disk handle (lsof), no owned listener, and an unchanged audited provider configuration (config_audit::verify, which is new at selection). They run at three points: once after the lease is acquired, again before any restart effect (restart_only), and in the final recheck beforemachine start.flockandlsofno longer run unlocked inselect(), in samples taken before the lease, or inruntime development probe. They cannot contend with another operation that is handing the VM lock to a provider.Owner::load. The rootfs content is checked against its pin byprepare_rootfsbefore boot. Both are unchanged.The boot gate is typed
machine createnow goes throughcreate_machine(.., FreshCreate). Onlyadmission_pool::fresh_createissues aFreshCreate, and it refuses whenever a reserve selection exists.machine startnow goes throughstart_machine(.., StartPermit). AStartPermitcomes only from:admission_pool::admit_boot, the pre-boot sample on the same basis as qualification, with the selection rechecked around the sample and swapouts compared with the baseline;BootAdmission::release, the final leased recheck.admission_pool, where tests cover it.stoppedand returnsadmission_rejected, as before.Diagnostics
Admissionhas a new optional field,retained_reserve_refusal. It appears on a fresh-budget sample when a created stopped pool failed the receipt checks, and holds the closed code:retained_reserve_{foreign,active,dirty,stale}_owner.Unchanged
Docs
packages/runtime-core/README.mdnow describes the marker, the two check layers, the typed gates and the diagnostic field.docs/guides/native-candidate.mdno longer says Development always reserves 58 GiB before startup. It now describes the running and stopped reserve bases.Validation (local, macOS arm64,
RUSTUP_TOOLCHAIN=1.97.1)bun run check:local(rustfmt check plus clippy--all-targets -D warnings): exit 0.bun run test:local: exit 0. The lib suite ran 1003 passed, 0 failed and 58 ignored, and every integration target passed.cargo test --lib admission_pool: 20 passed, three runs in a row. The wideradmissionfilter: 70 passed, 2 ignored. On a heavily loaded shared host, a run of that filter can time out insidelsof(see Remaining gaps).bun test tests/integration-ownership.test.ts tests/cli-spawn-env-isolation.test.ts: 16 pass.bun run --cwd packages/cli typecheck: exit 0.bun run --cwd packages/cli check: 0 errors and 34 existing warnings. No TS files changed.bun run privacy:check: ok.native-stream-relayrequires Zig 0.15.2. No changed code depends on a feature./private/tmpalias and a fixturesmolvm.db. They make no Docker, OrbStack, real HOME, keychain or sudo calls. Read-onlylsofis the only host tool they run. Tests that runlsofare serialized within the module.Negative controls (
admission_pool/tests.rs)downquiesces,recoverwritesrecovered-unclean, then a pre-boot refusal rewrites the phase tostopped: refused asDirty.recovered-uncleanorstopped-before-engineon the clean boot itself: refused asDirty.Dirty.stoppedrecorded byfinish_stoppedinstead ofdown'sfinish_quiesced_stop: refused asDirty.finish_quiesced_stopwhile the VM lock is held writes nothing.downkeeps the pool eligible.finish_quiesced_stoprecords the marker, and the pool then qualifies.agent.sockis listening (leased checks).owner.pendingis present, and missing fields, replaced, missing or mis-sized disks, or a missing VM lock. These are unchanged from the previous revision.OwnedPool::Stopped, no selection withFresh, and a live selection withRunning.BootGate::Refused(admission_rejected).retained_reserve_dirty_owner.releaserefuse.restart_onlyrefuses a running selection, a held owner that differs from the selection, and a held VM lock.fresh_createrefuses any selection.retained_reserve_dirty_ownerin the sample and its JSON. A never-created pool reports nothing.Mutation proof
I applied each of 18 mutations alone and ran
cargo test --lib admission_pool. 17 were killed:stopped()ignores the marker;phase()keeps the marker on an unclean write;begin_bootkeeps the marker;downnever records the marker;releaseskips the final recheck (the verifier's mutation B);admit_boot);restart_onlyignores the held owner;restart_onlyuses lock-free checks instead of leased ones;fresh_createallows a selection;reverify_leasedskips the release checks;releasedskips the config audit;The survivor is removing
!owner.createdfromrestart_only. That is an equivalent mutant, becauserestart_onlyalso requires the held owner to equal the selected owner, andstopped()requirescreated. I kept the check as defense in depth.Under host load, some kills also failed unrelated tests because of the
lsofbound. The listed tests fail deterministically for their mutation.Remaining gaps
start_poolis still not driven end to end in a test, because it needs artifacts and a provider. The typedFreshCreateandStartPermitmean that skipping the admission-pool gates would now require deliberately bypassingcreate_machineorstart_machine, not a one-line mutation. Removing therestart_onlycall instart_poolis not detected by a test;fresh_createand the leasedreleasestill guard create and boot.Ownerusesdeny_unknown_fields. An earlier candidate build cannot load a receipt that carriesclean_stopuntil a new boot clears it. This matches the earlierprevious_guest_boot_idandreclamationadditions.lsofbound. The release probe keeps its 5 slsofbound. On a heavily loaded host the probe can time out. That correctly refuses asretained_reserve_active_owner, and it makes a selected start fail instead of falling back. It can also make lsof-based unit tests flaky under heavy parallel load.downorrecoverlsofcould briefly observe that read handle and reportstop_uncertain. I have not observed this.Rework 2
Head before this round:
c3ccabdd. New commits:da798965(fix) and82d3ffaa(docs).Verifier defects
6698f47b. Already fixed byc3ccabdd, which gatesinto_selectionandrefusaltocfg(all(test, target_os = "macos")). I checked the hosted run onc3ccabddbefore this push: all 16 checks passed, includingCandidate core (ubuntu-latest),Candidate core (macos-latest)and all fourConfig compilerjobs. The readiness claims in this body now refer to the heads named here, not to6698f47b.lsofbound refused clean stopped restarts that the base branch admitted. Fixed inda798965.admission_pool::settlereplaces the old leased recheck. A stopped selection's receipt is rechecked first. Then, if every qualifying sample (including the lease recheck) also fits the unchanged 58 GiB fresh budget, the selection is dropped. Those samples are recorded on the fresh basis, and the start continues exactly as a start without a selection: no release checks, nolsof, a fresh pre-boot sample, and aStartPermitwithout the leased release.lsofat 30 s (RELEASE_HANDLE_WAIT) instead of 5 s.down,recover, the short-HOME and host-filesystem paths keep the 5 s bound (STOP_HANDLE_WAIT).lsofscan, as it does onnext. Anlsoftimeout can refuse only a start thatnextwould also refuse (admission_rejectedon the fresh budget). It now refuses withretained_reserve_active_ownerinstead.82d3ffaaadds a README note:runtime probetakes no lease and settles no basis, so it reports the reserve basis for any qualifying stopped pool. The start'sadmission.jsonrecords the basis that start actually used.New tests and mutation proof
admission::tests::only_an_admitted_development_sample_with_fresh_headroom_rebases_to_the_fresh_budget: the inclusive 58 GiB boundary, the October 2 value (58,010,112,000 bytes), unobserved disk, refused and explained samples, and Research.admission_pool::tests::a_start_with_fresh_headroom_drops_the_reserve_and_runs_no_release_check:retained_reserve_active_owner. Once the lock is released, the same window settles on the reserve basis.owner.pending) is refused before any basis is settled, even when there is enough room for the fresh budget.settlealways runs release checks;allbecomesany; the receipt recheck is skipped; the selection is kept; samples are not rebased; the live branch is removed;fits_fresh_budgetignoresadmitted,reasonsorprofile; it compares against the reserve floor;rebase_freshkeeps the reserve basis.lsofis a fixed host path, and no test injects a slow scan.Gates on
82d3ffaa(macOS arm64)cargo +1.97.1 fmt --checkpassed, andcargo +1.97.1 clippy --all-targets -- -D warningsreported no warnings. A local Linux cross-clippy could not buildring(no cross C compiler), so hostedCandidate core (ubuntu-latest)is the Linux lint proof.cargo test --lib admissionat load average 10 gave 72 passed and 0 failed.gitleaksoverorigin/next..HEADfound no leaks. CLItypecheckreported 0 errors, and CLIcheckreported 0 errors (34 existing warnings).bun run testdid not pass on this host: 6,958 passed, 240 failed and 149 skipped, at load average 15–58. This PR changes no TS, JS or JSON (git diff --stat 3b530068 HEADlists only.rsand.mdfiles). The only test that reads the changed docs istests/prerelease-plan.test.ts, and it passes (57/57);tests/env-command-matrix.test.tsalso passes.tests/native-authored-project-start.test.ts. A timing failure there leaves "Startup fixture settlement unconfirmed; roots retained", and in the shared process its fixture home then leaks into later files.--timeout 60000. 41 of 48 passed.native-compose-down-command(13; the CLI was killed, exit 137),native-compose-file-command(3),native-compose-proxy-probe(2),native-authored-project-recovery(2),native-authored-process-incarnation(1),test-config-compiler(1), andnative-authored-project-start(20; it cascades after its first timing failure).native-authored-project-startpasses when run alone.testjob, which runs this same TS code, passed onc3ccabdd. I am reporting these as load-only flakes rather than masking them.lsofalone took 4.1–6.0 s on this host, which has about 2,200 processes. The fullcargo testrun had 6 failures, and a serial rerun ofprovider::lifecycle::had 2. Every failure wasprovider_timeout_uncertain, and every failing test exercises the unchanged 5 s stop-path bound (short_home,host_filesystem,interrupted, and the twoadmission_pooltests that callfinish_stoppedorfinish_quiesced_stop). None of them runs the code this round changed. Run alone,only_a_completed_quiesced_down_records_a_clean_stoppassed; the others still failed wheneverlsoftook more than 5 s. In these runs, noadmissiontest failed withretained_reserve_active_owner, the failure the verifier saw from the release-check bound.Still open
start_poolis not run end to end in any test. Removing itssettleorrestart_onlycall would not fail a test.clean_stopuntil a new boot clears it. This is the samedeny_unknown_fieldspolicy that applied to the earlier receipt fields.lsofbound.downandrecoverkeep the 5 s bound, unchanged fromnext. On a loaded host,downcan returnprovider_timeout_uncertain. It then never records the clean-stop marker, so the next restart uses the fresh budget.🤖 Generated with Claude Code