Repository navigation
CSRG-U3: admission, attempt identity and the pre-spawn slot lifecycle - #88
Merged
Merged
Conversation
…d executions (CSRG-U3) - runner: a SlotReservation is taken before any process is created, so a request over the live process limit is refused before a child exists (previously the child was spawned and then killed). Slots held for processes not yet created count against the limit. - devguard adapter: Admit, Lookup and Cancel of one attempt, each a bounded session of the registered owner. Answers are NotSent, Refused (a definite DevGuard error), Attempt (this owner's record for this meaning) or Unknown (timeout, lost reply, EOF, a foreign record or the client's transport code); no DevGuard free text is kept. - runner (devguard feature): AttemptIdentity, AdmissionAccount (requested, required, supported, reserved, applied), a Preparer that takes the slot, computes the digest and asks for admission, and a one-shot, non-Clone PreparedExecution. Its launch cancels the unstarted attempt and reports LaunchUnavailable until managed launch exists (U4). Unknown admissions are looked up once, cancelled only when found prepared, and never replayed. - policy: resources.request, the operator's per-execution request with positive quantities and minimum control levels. - domain: MANAGED_LAUNCH_UNAVAILABLE, RESOURCE_UNAVAILABLE, RESOURCE_AUTHORITY_UNAVAILABLE and ADMISSION_UNKNOWN. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ner (CSRG-U3) - gateway: a workspace with `resources.participation: required`, under `--devguard register` with an owner that can prepare, goes through policy, the approval hold and the workspace lease, then the owner's preparation. Every preparation ends in a refusal that starts nothing and releases the lease: MANAGED_LAUNCH_UNAVAILABLE after the unstarted attempt is cancelled, or WORKSPACE_BUSY, RESOURCE_UNAVAILABLE, RESOURCE_POLICY_UNSUPPORTED, RESOURCE_AUTHORITY_UNAVAILABLE or ADMISSION_UNKNOWN, each naming the attempt and what is known of it. Other modes keep U2's refusal before any approval or lease. - worker: a `prepare` request, stated in its Hello as `admission` and answered beside the others, never cached for replay. A request the worker may have received without answering is reported unknown; one that never left, owner_unreachable. - fixture authority: waits until DevGuard admits before printing its settings, and reports the attempts and budget still charged. - CI: the macOS stage also runs the runner's `admission` tests. - docs: the CSRG-U3 section in English and Korean; the process limit refuses before spawn. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…CSRG-U3) The macOS CI runner's DevGuard work capacity is under 500 m CPU, so the default request of 1000 m was denied there, as were the adapter tests' 500 m. The default request is now 250 m CPU, 256 MiB and 32 tasks; the adapter tests ask for 100 m, 64 MiB and 8 tasks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
novelKR
marked this pull request as ready for review
October 7, 2026 09:45
novelKR
pushed a commit
to novelKR/DevGuard
that referenced
this pull request
Oct 7, 2026
CSRG-U3 merged in CodeSpace on 2026-10-07 as novelKR/CodeSpace#88 (d543ea5, at head 4af3feb), and its post-merge CI is green. Record U3 as merged-verified, keep U4-U6 not started, and say so in AGENTS.md, the milestone table and note, the planning index and the CS-RG plan, English and Korean. CS-RG stays in-progress with qualification not-run, and the suspension of the CSRG-C00-C09 plan is unchanged. Korean counterparts reviewed; planning-readme and planning-milestones-cs-rg hashes recorded. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 7, 2026
novelKR
pushed a commit
to novelKR/DevGuard
that referenced
this pull request
Oct 8, 2026
The owner directed CSRG-U4 to start on 2026-10-08, after CSRG-U3 merged in CodeSpace as novelKR/CodeSpace#88 (d543ea5) and CodeSpace moved its DevGuard pin to 6e7e065 as novelKR/CodeSpace#89 (e0610c4), both with their post-merge CI green. Record U4 as active with the units dated 2026-10-08, keep U5 and U6 not started, and say so in AGENTS.md, the milestone table and note, the planning index and the CS-RG plan, English and Korean. CS-RG stays in-progress with qualification not-run, and the suspension of the CSRG-C00-C09 plan is unchanged. Korean counterparts reviewed; planning-readme and planning-milestones-cs-rg hashes recorded. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Requested by Seongjae
CSRG-U3: admission, attempt identity and the pre-spawn slot lifecycle. A governed execution now goes through authorization and workspace readiness, then a CodeSpace process slot, then DevGuard admission, then a one-shot prepared execution. Managed launch is U4. Until it lands, a successful preparation cancels the unstarted attempt and returns
MANAGED_LAUNCH_UNAVAILABLE, the default the owner chose for section 7. No governed payload is spawned, and nothing runs through a non-governed path.Issue
Scope: the CSRG-U3 section of the owner's U1–U6 order (DevGuard
docs/handoff/2026-10-02-cs-rg-u1-u6-instruction.md), with the standing rules of the U2 order. It does not close an issue. Tracking: #76 and novelKR/DevGuard#14.Base, head and pins
main; started fromebed574(#84's merge, U2), withmainat579c075(#87's merge) merged in as87c00554af3febee402ed6050d6ca90f95394f3b83dc5e55dc5332(slot, adapter, owner-side preparation, policy, error codes),5fc4d3c(gateway, worker, fixture, CI filter, docs),87c0055(merge ofmainat579c075),4af3feb(default request sized for small hosts)f1f908429abea962d62d6b53c56a26c17250179b(devguard-client,devguard-contract; dev-onlydevguard-daemonfixture), unchanged6b9826e3aa83b1a5947db50f4332cb9c65f1b340(rust-v0.154.0), unchangedmainat start334472a(no change undercrates/orCargo.locksince the pin)Summary
Order. For a workspace with
resources.participation: required, under--devguard register:allow,require_exec, argv, theapprovals: confirmhold, then the workspace mutation lease.InProcessRunner::reserve_slot, before anything is created.Register, thenAdmit.PreparedExecution: owns the command, the slot and the attempt.Admission state is never treated as a process handle. Spawn, PTY, output, timeout, termination and reaping stay with CodeSpace; DevGuard keeps admission, the reservation and the attempt's durable identity and accounting.
Execution owner per mode (the same as U2's registration):
in-processPreparerbeside its ownInProcessRunner, under itsOwnerRegistrationuds+--runtime-binprepare, stated in the worker'sHelloasadmission; answered beside other requests and never cached for replayuds+--runner-socketRESOURCE_POLICY_UNSUPPORTED, before any approval or leaseAttempt identity (
AttemptIdentity):attempt_id(cs-attempt-plus 32 hex digits; never a transport request ID), the CodeSpaceprocess_id, the workspace, PTY or not, and the resource request.ExecutionMeaningv1 encoding of the argv as given; the workspace ID, root, profile and network policy; the exact environment the child would get (the samespawn_envthe spawner uses); the PTY flag; the timeout; and the request.attempt_conflict). The adapter accepts a record only if it has this attempt's key, this request's fingerprint and the owner's registered identity; anything else is treated asunknown. Launching with another argv,process_idor PTY choice isDigestMismatchand cancels the attempt.Slot before spawn. This is a product change for every execution, governed or not. Before U3,
spawn_pipeandspawn_ptycreated the child and only then checked the live-process limit, killing the over-limit child. Nowspawn_hosttakes aSlotReservationfirst. Slots held for processes not yet created count against the limit, so a ninth request is refused with the sameWORKSPACE_BUSY"live process limit reached" before any child exists. Slot, attempt, workspace and retained-output lifetimes are separate:Admission account (
AdmissionAccount). Kept as distinct fields: requested (quantities), required (the minimum level per resource), supported (DevGuard's plan: scope, plus level and method per resource), reserved (quantities and the prepared TTL), and applied (not_launched; no runtime enforcement is claimed before U4).PreparedExecution.Clone;launchandcanceltakeself, so it is used once.Admitwas sent. An optional lower bound is used only by tests.launch(request)with the prepared command before the deadline is where U4 will callBeginLaunch. In U3 it cancels and reportsLaunchUnavailable. After the deadline it cancels and reportsExpired. With another command it cancels and reportsDigestMismatch.Preparer::prepare_and_disposeruns as its own task, so an MCP request that stops waiting still lets the preparation settle.Contract changes
MANAGED_LAUNCH_UNAVAILABLE,RESOURCE_UNAVAILABLE,RESOURCE_AUTHORITY_UNAVAILABLE,ADMISSION_UNKNOWN.WORKSPACE_BUSYandRESOURCE_POLICY_UNSUPPORTEDare reused.process_idand the attempt state (not_asked,not_recorded,denied,cancelled,expired,other,unknown), and ends with "nothing was started". Only enumerations and identifiers appear; no DevGuard free text.resources.request(cpu_milli,memory_bytes,tasks,minimum.{cpu,memory,pids}∈accounted|cooperative|kernel). The default is 250 m, 256 MiB, 32 tasks, allaccounted, small enough for a three-core host whose DevGuard work capacity is under 500 m. Quantities must be positive, and unknown keys fail the load.prepareop and itspreparedresult, behind the feature, plus aHello.admissionflag that is omitted when false.WIRE_PROTOCOLis unchanged by this PR (7 since runner: report a signal death that CodeSpace did not cause as signaled #87). The op is sent only to a worker that statesadmission, as U2 did forregistration.Error mapping:
cancelled(or what the cancel found)MANAGED_LAUNCH_UNAVAILABLEnot_askedWORKSPACE_BUSYresource_unavailable(shortage)deniedRESOURCE_UNAVAILABLEresource_policy_unsupporteddeniedRESOURCE_POLICY_UNSUPPORTEDdeniedRESOURCE_AUTHORITY_UNAVAILABLEAdmitwas sent (registration state and code given)not_askedRESOURCE_AUTHORITY_UNAVAILABLEAdmitwith an errornot_recordedRESOURCE_AUTHORITY_UNAVAILABLEexpired/cancelledRESOURCE_UNAVAILABLEcancelledINTERNALLookupADMISSION_UNKNOWNpreparesent and not answeredunknownADMISSION_UNKNOWNpreparenever leftnot_asked(owner_unreachable)RESOURCE_AUTHORITY_UNAVAILABLEUncertainty rules (§8).
resource_control_unavailableis treated as unknown, because DevGuard's client uses it for every transport failure.Lookup. A record found Prepared is cancelled. A settled record is reported as found. NotFound or no answer staysunknown: NotFound is not proof, since the admission may still be in flight.Cancelis the only settlement used. Its precondition holds because the owner never sentBeginLaunch;AbandonLaunchis not used.Tests
Against DevGuard's real authority (fixture with native host evidence, macOS), in
crates/devguard:committed == 0)attempt_conflictand is never matchedkernelmemory) are eachDenied, with nothing reservedAdmitisUnknown, and DevGuard then shows itPrepared: a timeout is not a refusal (hold_authorityafterRegister)Scripted authority (adapter): each wire error keeps only its code; a timeout or lost reply on
Admit,LookupandCancelisUnknown; a record for another attempt, digest or owner isUnknown; an invalid admission opens no session; a session that closes before the request isNotSent.Runner (
admission, deterministic fake authority;process, real spawns), against each item of §9:a_prepared_execution_holds_its_identity_slot_and_account_and_launches_nothingdenials_and_an_unreachable_authority_reserve_nothing_and_cancel_nothingan_occupied_slot_refuses_before_the_authority_is_askedconcurrent_requests_over_the_limit_are_refused_before_admission;concurrent_requests_over_the_limit_create_only_the_children_allowed(feature-off, real children: exactly 3 of 12 markers)a_request_over_the_limit_creates_no_child(feature-off, real children)an_expired_preparation_is_not_launched_and_is_cancelled; adapteran_unlaunched_attempt_expires_on_its_owna_preparation_cancelled_before_launch_frees_its_slotan_unknown_admission_is_looked_up_never_replayed_and_settled_conservatively; adapter timeout and lost-reply tests; gatewaya_preparation_lost_with_the_worker_is_unknownanother_command_is_not_launched_under_the_prepared_attempt; adapterthe_same_attempt_for_another_command_is_refused;the_meaning_follows_the_command_workspace_and_requesta_caller_that_stops_waiting_leaves_the_preparation_to_settle(request future dropped; preparation dropped unsettled)the_slot_and_the_attempt_are_released_separately(cancel unreachable: slot and lease freed, attemptunknown, not claimed released)touchmarker and zero occupied slotsGateway (
codespace-server --features devguard, out-of-process fixture authority, plus the devguard worker for UDS):MANAGED_LAUNCH_UNAVAILABLEwithcancelled, nothing charged afterwards, lease released, reads unaffected, re-resume returns the stored refusalresources.requestproducesRESOURCE_UNAVAILABLE(shortage) andRESOURCE_POLICY_UNSUPPORTED(kernel minimum)RESOURCE_AUTHORITY_UNAVAILABLEwithnot_askedADMISSION_UNKNOWN, thenowner_unreachableWithout native evidence (Linux before DG-LINUX), the owner cannot register, so governed executions are
RESOURCE_AUTHORITY_UNAVAILABLE(incompatible); the gateway tests assert that branch there.Local validation (macOS 26, Rust 1.98.0 Homebrew,
scripts/validate-upstream.py,CARGO_INCREMENTAL=0):87c0055: all 18 stages passed across three runs. One run (u3-local-2) passed 16 of them: policy, python, pin, format, the three clippy stages, unit-devguard, unit-patch, unit-pty, unit-file-system, unit-linux-sandbox-protocol, macos-fork-race (400 trials), integration and dependencies.macos-corepassed in full on its rerun (u3-local-3), andunit-codex-runtimeon its rerun (u3-local-4).4af3feb: policy, python, pin, format, the three clippy stages, unit-devguard, macos-core and dependencies passed (u3-local-5). The integration stage's test commands passed when run directly (u3-local-7): the workspace, plus server, runner and domain withdevguard.cargo test --test runtime_binarypassed 6/6 on its third run (failure 8 below).4af3feb, because4af3febdoes not touch them: macos-fork-race and the patch, pty, file-system and linux-sandbox-protocol unit stages. All passed on87c0055.CI on the exact head
4af3feb: run 37599585285 passed every job: Plan, Format + upstream pin, the three Clippy jobs, every Unit job, Integration, Linux isolation, macOS contracts, policy-scan, docs-build and therustgate. The previous head's run 37595340009 failed in macOS contracts; see failure 7.Security scenarios
requirednever runs without the authority; in every mode that cannot prepare it is refused before an approval or lease exists.Dependency delta
None: no
Cargo.toml,Cargo.lockor workflow changed. The new code uses onlydevguard-contractanddevguard-client, already at the pin. Thedependenciesstage passed locally on4af3feb, as did CI's dependency and pin checks; the feature-off graphs are unchanged.Feature-off result
Without the
devguardfeature, the only behavior change is the pre-spawn slot: an over-limit request is refused before any child is created, rather than created and then killed. The error code and message are unchanged. The registry gainsresources.request; a workspace withoutresourcesencodes exactly as before. Therequiredrefusal message tail changed (above).Supported and unsupported modes
--runtime-bin): admitted, then cancelled.--runner-socket, and any build or run without--devguard register: refused before any approval or lease.RESOURCE_AUTHORITY_UNAVAILABLE.Local validation failures (kept)
Kept in the local evidence set
<DEVGUARD_CHECKOUT>/evidence/csrg-u3-2026-10-07/(local/dev/failures.md, with logs and a SHA-256MANIFEST.json):CARGO_TARGET_DIRand started a fresh 4.7 GiBtarget/debug. I stopped it and removed that output.Register, and the early return was removed.spawn_hello_then_drop_kills_workerfailed on the first launch of a just-relinked worker; runs 2 and 3 passed 56/56.5fc4d3cto mergemain. Its passed stages are kept, but it is not a result for any head.87c0055.unit-codex-runtimefailed (relinked worker), and the pty testcreated_terminal_has_advertised_sizeread"", which stoppedmacos-coreearly.crates/ptyis identical tomain.87c0055.macos-corepassed in full.unit-codex-runtimefailed again, because the binary at the shared output path was the devguard build; a feature-off rebuild then passed.87c0055. Five native adapter tests were deniedresource_unavailable: the 3-core macOS runner's DevGuard work capacity is 480–500 m CPU. The default request (1000 m) would have been denied on such a host too. Fixed by4af3feb, which sets the default to 250 m / 256 MiB / 32 tasks and the test request to 100 m.4af3feb. Worker-launch failures came right after relinks. A fresh copy of the worker takes 1.41 s to bind on its first launch and 0.08 s afterwards, and the test launches six workers within a 2 s window.main(579c075) shows the same behavior in this worktree: it fails right after the relink and passes on the next run. This is the Mac's behavior on new binaries, not U3.Out of scope
BeginLaunch, permits and carriers, helper launch, observation, reaping or release integration, and any Codex or DevGuard pin change.past_the_credentialretries.Rollback
Revert this PR's merge commit. Nothing persistent was added: no store schema, no DevGuard ledger change, and attempts are DevGuard's and expire on their own. A workspace that sets
resources.requestmust drop that key after a revert, because U2's registry rejects unknownresourceskeys.