Skip to content

devguard, runner, server: launch governed executions once under DevGuard (CSRG-U4) - #90

Merged
novelKR merged 7 commits into
mainfrom
codex/csrg-u4-launch
Oct 9, 2026
Merged

novelKR merged 7 commits into
mainfrom
codex/csrg-u4-launch

Conversation

@novelKR

@novelKR novelKR commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Requested by Seongjae

CSRG-U4: managed launch. A prepared execution in a workspace that requires resource participation is now launched exactly once under DevGuard: BeginLaunch once, a one-time permit, DevGuard's devguard-launch helper started by CodeSpace's own pipe or PTY spawner, DevGuard binding and policing the helper's process group as the attempt's scope, then the payload. CodeSpace keeps spawn, PTY, process handles, output, timeout, termination and reaping. No Codex or DevGuard pin change.

Issue

Scope: the CSRG-U4 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 (sections 1, 11–14) and the CS-RG handoff of 2026-10-03 (section 5). It does not close an issue. Tracking: #76 and novelKR/DevGuard#14.

Base, head and pins

Item Value
Base main, started from e0610c4 (#89's merge, the DevGuard pin move to 6e7e065)
Head c1c785962fc830cb41519c202c24814c56edd6c4
Commits 0de8f6e (adapter BeginLaunch/AbandonLaunch/helper invocation, PTY adapter spawn_inheriting, runner launch flow and descriptor exclusion), 6710f8b (gateway and worker wiring, launch wire op, CI helper build, docs), d1dc658 (tests: the owner keeps no carrier once the helper started, timeouts of governed processes; the domain doc line), 8894955 (the adapter's helper lookup moved into its macOS-only tests, for Linux clippy), 8bc8af0 (the runner tests' stand-in helper under bash, for Linux; resolve_program on the platform's default path; the documentation's helper rule), d5ef68a (the D6 test lets DevGuard release each round before the next, for small hosts), c1c7859 (the governed timeout test leaves the helper room to report READY; the documentation says where a governed timeout starts)
DevGuard source pin 6e7e065a0dd5b3876dc7f4546ab76069489937d1 (devguard-client, devguard-contract; dev-only devguard-daemon fixture), unchanged
DevGuard launch helper in tests devguard-launch built from that same commit with DevGuard's own lockfile (cargo install --locked --git https://github.com/novelKR/DevGuard --rev 6e7e065…); never linked
Codex gitlink 6b9826e3aa83b1a5947db50f4332cb9c65f1b340 (rust-v0.154.0), unchanged
DevGuard main 6e7e065 at start; df3c537 now (novelKR/DevGuard#28, ledger only: CSRG-U4 active). Nothing under crates/ changed since the pin
Bookkeeping novelKR/DevGuard#28 records CSRG-U4 as active; this PR does not edit DevGuard's ledger

Summary

Launch order (PreparedExecution::launch_managed, after U3's preparation):

  1. Local checks before anything is committed: the prepared command, process_id, PTY choice and deadline (U3); a usable helper; the program resolved to an absolute path on the execution's own PATH; no Linux command sandbox and no Enabled network. A failure cancels the unstarted attempt (precondition: never launched).
  2. BeginLaunch, exactly once, in a bounded owner session (Register, then BeginLaunch). Only a permit that arrives with this owner's committed record of this attempt and fingerprint is a grant (LaunchAnswer::Granted); any other answer is Refused (an error answer committed nothing, so the attempt is cancelled), NotSent (cancelled) or Unknown.
  3. Helper: DevGuard's helper_command builds the invocation (arguments carry no secret; two private descriptors, the permit carrier and the transcript writer, close-on-exec in the owner). CodeSpace spawns it through its own pipe or PTY spawner as its direct child, into the prepared slot. The owner's copies of both descriptors close as soon as the spawn returns, on every path.
  4. Transcript, read to its end on a blocking thread, bounded by 10 s for the answer:
Transcript end Phases reached exec_command
READY, then end (Started) prepared, launch_committed, helper_created, scope_bound, policies_applied, helper_ready, payload_exec_attempted, payload_running dispatch_status: confirmed; a lookup records what DevGuard applied per resource and the scope root
READY, then exec_failed … helper_ready, payload_exec_attempted, payload_failed PROCESS_SPAWN_FAILED; DevGuard settles the claimed grant through its scope
refused, failed or end before READY prepared … helper_created refusal; after CodeSpace reaps the helper, AbandonLaunch(helper_exited)
not ended in 10 s, or malformed prepared … helper_created dispatch_status: unknown for the process_id; the transcript is still read, and the owner reports holding no helper once the process has ended

draining and released are represented and mapped from DevGuard's record when observed; U4 never cancels a committed launch, so it does not cause draining.

No replay. After Unknown, one Lookup: still Prepared → Cancel (not committed); LaunchCommitted without a scope → AbandonLaunch(grant_not_received), which DevGuard releases as no_helper_created (known not started); anything else reported as found; no answer → unknown. BeginLaunch is never sent again, and the prepared execution is consumed by launch_managed, so no second launch exists. AbandonLaunch(spawn_failed) covers a helper that could not be created. A report never releases a claimed grant.

Execution owner per mode (the same owner as U2/U3; DevGuard requires the helper's parent to be the registered owner):

Runner mode Launches How
in-process the gateway Preparer::with_launcher beside its own InProcessRunner
uds + --runtime-bin the worker the gateway started one new launch request, stated as launch in the worker's Hello only when it registers and has a helper; never cached or sent again. A lost answer is dispatch_status: unknown; a request that never left is owner_unreachable
uds + --runner-socket nobody refused before any approval or lease (RESOURCE_POLICY_UNSUPPORTED), as in U2/U3

Registration and identity mapping are U2's, unchanged. The helper's ticket names the owner's socket, consumer, generation, attempt ID and the owner's registered instance ID.

Participation policy. required with --devguard register and --devguard-launch-helper launches under DevGuard; without a helper it is admitted and refused (MANAGED_LAUNCH_UNAVAILABLE, U3's behavior); without registration it is refused before anything exists. required never runs outside DevGuard. off is unchanged.

Carrier choice and D6 (U4 sections 4 and 9)

Choice: DevGuard's own carriers (a socket pair holding the permit, its writer closed, and a pipe for the transcript, both created under DevGuard's spawn_guard above the standard three, close-on-exec), delivered by CodeSpace's spawners:

  • Pipe: the helper's tokio::process::Command gets exclude_unrelated_except: in the child, every descriptor above 2 except the two is marked close-on-exec (the Investigate descriptor inheritance between concurrent spawns on macOS #79 exclusion), and close-on-exec is cleared on the two only there. If either is not open, the spawn fails.
  • PTY: the pinned Codex PTY (0.154) passes only descriptors that are already inheritable (inherited_fds, its descriptor-keeping path). The two are made inheritable in the owner's process just before that spawn and closed when it returns. During that window every other spawner in the process excludes unrelated descriptors from its children (pipe, PTY, patch helper, sandbox helper, worker spawn; audited by grep of every Command::new in the gateway, runner and worker), and the Codex PTY child closes every other inheritable descriptor itself.
  • The helper consumes and closes the permit, marks the transcript close-on-exec and closes its own session before exec, so the payload holds none of them.

Why this is the smallest choice satisfying N1 and N2: it uses DevGuard's existing carriers and helper unchanged and CodeSpace's existing spawners and #79 exclusion; no new backend, no CodeSpace PTY implementation, no change to process ownership. The alternatives recorded in DevGuard's W3 packet (a regular-file permit, results through the authority, FIFO readers) need DevGuard changes; Codex ChildFds::Attached needs a pin past 0.157. With #79 merged, the condition W3 named for this choice (every same-process spawner excludes unrelated descriptors) holds, and the tests below check it rather than assume it.

D6 controls, against CodeSpace's real spawners:

Control Test Result
unrelated descriptor inheritance descriptors::only_the_kept_descriptors_reach_the_child (an inheritable fd and two close-on-exec "carriers": a plain child holds only the inheritable one; the guarded child holds exactly the two); gateway concurrent_launches_pass_their_descriptors_to_no_other_child (an inheritable socket held by every unguarded control spawn and by no CodeSpace child) pass
permit/credential leakage adapter the_executable_keeps_none_of_the_helpers_descriptors (real helper; payload probes 3..1023); gateway D6: no child holds a socket, and no child's environment or output contains the consumer secret; the permit never appears in Debug, arguments or errors (a_permit_never_shows_itself, an_invocation_names_its_two_descriptors_and_carries_no_secret) pass
transcript writer/reader leakage gateway D6: no child holds a pipe above 2; runner tests check the owner's copies close when the spawn returns pass
helper-only inheritance runner only_the_helper_receives_its_two_descriptors_and_the_command_none (stand-in helper lists exactly what it received: exactly its --permit-fd and --report-fd, pipe and PTY); during_a_pty_spawn_only_guarded_children_are_safe (inside the PTY window an unguarded child holds both, a guarded child neither) pass
payload exclusion the same tests: the payload holds nothing above 2 pass
concurrency gateway D6: 6 rounds of 2 governed launches and 4 ordinary pipe/PTY children at once, plus unguarded controls, against the fixture authority and the real helper; each round waits until DevGuard has released its attempts (on c1c7859: "12 governed launches and 24 ordinary children held no descriptor beyond stdio; the unguarded control held the inheritable descriptor 24/24") pass

Pipe and PTY semantics (U4 section 8)

Tested on the governed path: PTY initial size 24×80, process_resize to 40×120, session leader with its controlling terminal (pgid == pid), stdin and output, EOF; pipe stdin, stdout and stderr through CodeSpace's pumps, process status, termination; timeout and termination use CodeSpace's own tasks unchanged. Differences from an ungoverned execution, documented:

  • the payload's argv[0] is its resolved absolute path (DevGuard's helper execs by path), found on the execution's PATH, or on the C library's default as execvp would when it has none;
  • on a pipe the payload leads its own process group (the helper's scope); before, a pipe child stayed in the gateway's group;
  • on a PTY a signal death is reported as 128 + signal instead of 1 (the Codex PTY's descriptor-keeping path; ungoverned PTYs still report 1, process_status reports a signal death that CodeSpace did not cause as a normal exit #85's note);
  • the timeout runs from the helper's spawn, so it also covers the helper's exchange with DevGuard before READY (tens of milliseconds); a timeout that runs out first ends the launch before the command starts, reported as not started.

Contract changes

  • CLI: --devguard-launch-helper PATH / CODESPACE_DEVGUARD_LAUNCH_HELPER (absolute path; with register only). The worker takes the same flag from the gateway. Feature-off CLI unchanged.
  • MCP: no schema change. exec_command on a governed workspace now returns a process_id with dispatch_status: confirmed or unknown, or a refusal. Messages end with "nothing was started" when no helper process existed and "the command was not started" when one did. No new error code: ADMISSION_UNKNOWN now also covers a launch commit whose answer was lost and whose attempt stays unknown.
  • workspace_info: resource_authority.governs_execution is true when the owner is registered and has a helper.
  • Runner wire: launch op, launched result, Hello.launch (omitted when false). WIRE_PROTOCOL stays 7; the op is sent only to a worker that states it.
  • Helper check: the helper must be a regular executable file owned by this user or root, writable only by its owner or root (a group write only from root's group, gid 0), under directories with the same property (a root-owned sticky /tmp aside). Checked before every launch.

Error mapping:

Outcome Attempt state Tool error
Launched, transcript confirmed (charged; settles through its scope) none: dispatch_status: confirmed
Launched, transcript not settled or malformed unknown none: dispatch_status: unknown
No helper configured (U3 path) cancelled MANAGED_LAUNCH_UNAVAILABLE
Helper unusable cancelled MANAGED_LAUNCH_UNAVAILABLE
Program not on PATH; Enabled network cancelled PROCESS_SPAWN_FAILED
Linux command sandbox would wrap the command cancelled RESOURCE_POLICY_UNSUPPORTED
BeginLaunch refused cancelled by DevGuard code: RESOURCE_UNAVAILABLE, RESOURCE_POLICY_UNSUPPORTED, else RESOURCE_AUTHORITY_UNAVAILABLE
BeginLaunch session ended before sending cancelled RESOURCE_AUTHORITY_UNAVAILABLE
BeginLaunch no answer from one lookup: cancelled, released, found, or unknown ADMISSION_UNKNOWN when unknown, else RESOURCE_AUTHORITY_UNAVAILABLE
Helper could not be spawned released (after AbandonLaunch(spawn_failed)) PROCESS_SPAWN_FAILED
Helper refused / failed before presenting released (unclaimed) or launched (claimed) as BeginLaunch refused / RESOURCE_AUTHORITY_UNAVAILABLE
Helper ended before READY released or launched PROCESS_SPAWN_FAILED
READY, then exec failed launched PROCESS_SPAWN_FAILED
UDS launch lost after sending unknown none: dispatch_status: unknown
UDS launch never sent not_asked (owner_unreachable) RESOURCE_AUTHORITY_UNAVAILABLE

Supported and unsupported modes

Platform Runner Pipe PTY
macOS, DevGuard with native host evidence in-process supported supported
macOS, DevGuard with native host evidence uds + --runtime-bin supported supported
any uds + --runner-socket unsupported (no registration) unsupported
Linux before DG-LINUX any unsupported (DevGuard registers nobody; refused, nothing started) unsupported
any Linux command sandbox or Enabled network unsupported (refused before commit) unsupported

Supported means the workload class whose processes end with the root. A descendant that outlives the root keeps the attempt charged; one CodeSpace reaps past before DevGuard observes it leaves the attempt suspect (CSRG-U5). On a pipe, terminate_process and a timeout kill the root only, as before.

Tests

Local validation ran on the owner's Mac (macOS 27.0, Apple M1, 8 CPUs; Rust 1.98.0) with scripts/validate-upstream.py, against DevGuard's fixture authorities (the in-process TestAuthority and the fixture_authority process) and a devguard-launch built from the pin, never the installed DevGuard service or its helper. Failed attempts are kept in the evidence set, each with the hypothesis stated before its re-run.

On the final head c1c7859:

Run Stages Result
u4-local-14 all 14 passed (pin, policy, python, format, clippy-root, clippy-adapters, clippy-codex, dependencies, unit-patch, unit-pty, unit-file-system, unit-linux-sandbox, unit-devguard, unit-linux-sandbox-protocol); linux-isolation not run (Linux only, CI runs it); integration and unit-codex-runtime failed on the worker's first launch, outside this PR (below)
u4-local-15 macos-core, macos-fork-race passed: adapter 55 (4 ignored), worker 1, runner 44, gateway 31 (5 ignored); fork race 400 trials, 0 failed, 57,158 children, 0 without an exit code, 0 new crash reports
u4-local-16 integration and unit-codex-runtime by hand, the worker launched once before the tests passed, inputs unchanged: cargo test --workspace (runner 105, gateway 30, store 47, domain 29, policy 21, protocol 4 and the integration tests), gateway --features devguard 60 (5 ignored), runner 140 and domain 32 with --features devguard; runtime_binary 6

Ignored tests are the child halves of parent tests, which their parents run in separate processes.

CI:

Run Head Result
37909439770 d1dc658 failed: Rust / Clippy (adapters) on Linux, helper_bin unused (a test helper only the macOS tests call; fixed by 8894955); Rust / Integration on Linux, 10 of the runner's 140 --features devguard tests (fixed by 8bc8af0); macOS cancelled by the next push
37910322495 8894955 Rust / Integration: the same 10; macOS cancelled by the next push
37911831642 8bc8af0 every Linux job passed; Rust / macOS contracts failed the D6 test, a DevGuard capacity refusal (fixed by d5ef68a, below)
37914545763 d5ef68a every Linux job passed; macOS cancelled by the next push
37916045407 c1c7859 passed: every job, Rust / macOS contracts and Rust / Integration included, and the docs build

What the failures were (all found by tests; the only product change among the fixes is resolve_program's default path, a defect in this PR's own new code):

  • Linux, the runner's launch tests (8bc8af0). Nine used a stand-in helper written for /bin/sh that redirects the carriers by number; dash, Linux's sh, rejects descriptor numbers above 9 ("Bad fd number"). The same nine fail on macOS with the stand-in under /bin/dash and pass under bash, which it now uses. The tenth found a defect in this PR's new resolve_program: without PATH it searched macOS's default path on every platform, while glibc's execvp searches /bin:/usr/bin; it now uses the platform's default. Linux registers nobody yet, so no managed launch reached it.
  • macOS CI, the D6 test (d5ef68a). DevGuard refused a governed launch of a later round (resource_unavailable), and CodeSpace reported a refusal that started nothing, as it should. DevGuard's CPU work capacity on the 3-CPU runner is 480 m (3000 m less 2000 m of host headroom, 500 m for the system and 20 m of control reservations): nine of the test's 50 m attempts. The six rounds ran back to back while DevGuard had not yet released earlier rounds (it releases a launched attempt once its reconciler has seen the scope end). With the request raised so that this Mac also holds nine, the unchanged test was refused the same way in three runs of three, with 0, 2, 4, 6 and 8 attempts charged at the round starts; waiting for each round's release, every round started with none charged, its attempts released 0.73–0.95 s after its processes ended, and all three runs passed.
  • macOS local, the governed timeout test (c1c7859). It set a 300 ms request timeout, which runs from the helper's spawn; under load the stand-in had not reported READY in time, so the timeout ended it and the launch was reported as not started (HelperEnded), as it should be. A 5 ms timeout reproduces it in three runs of three. It now allows 2 s, and the macos-core runner command passed in five runs of five.

Two failures outside this PR, macOS local only:

  • integration and unit-codex-runtime fail or pass by chance with "worker socket not ready": tests that launch the worker binary wait about 2 s for its socket (the gateway's wait_for_socket, crates/codex-runtime/tests/runtime_binary.rs), and the first launch of each newly placed worker file takes macOS 2.2–2.8 s here (later launches 11–23 ms; a byte-identical copy 1.3 s and a new hard link 1.0 s also pay it). Every cargo command that includes the worker places it again. With the worker launched once first, both stages pass. The gateway's DevGuard tests already launch their worker once for this reason (CSRG-U2); U3's validation met the same failure.
  • codespace-runner linux_sandbox::tests::prepare_stdout_flood_is_too_large (unchanged since Investigate descriptor inheritance between concurrent spawns on macOS #79) timed out once under load in cargo test --workspace: its stand-in writes one byte per printf and must fill 64 KiB within the 2 s prepare bound. It runs on Linux in CI's Integration, not in macos-core.

Earlier heads: 6710f8b: u4-local-1; d1dc658: u4-local-2 to u4-local-4; 8894955: u4-local-5 to u4-local-7; 8bc8af0: u4-local-8 to u4-local-10; d5ef68a: u4-local-11 to u4-local-13. u4-local-4 is void: a source file was edited while it ran (the fix for CI's clippy failure).

Verified: the launch flow, refusals, lost answers, the carriers and D6, pipe and PTY semantics, timeouts and termination on macOS against the fixture authority and the real helper; the same flow on Linux with a scripted authority and the stand-in helper (CI); feature-off builds and tests.
Not verified: managed launch on Linux (unsupported until DG-LINUX); the installed DevGuard service (never used); descendants that outlive the root, observation before reap and release paths (CSRG-U5); qualification (CSRG-U6).

Security scenarios

  • Permit: carried only by DevGuard's socket pair to the helper; never in arguments, environment, logs, statuses, errors or Debug; the adapter's Permit cannot be cloned or printed and is consumed by the invocation.
  • Consumer credential: unchanged U2 path; the D6 test checks no child's environment or output holds it.
  • Descriptors: see the D6 table; the helper's descriptors reach only the helper, and the payload holds nothing above 2.
  • Helper substitution: the helper path is checked before every launch (owner and write permissions on the file and its directories), so a file another user could replace is never handed a permit.
  • No fallback: required never runs a command outside DevGuard; every failure before the helper's READY is a refusal, and uncertainty is reported as dispatch_status: unknown, never as success or as "not started".
  • Free text: no DevGuard message reaches MCP; outcomes are enumerations, numbers and CodeSpace IDs.

Dependency delta

  • crates/pty: dev-dependency libc = "0.2" (already in that lockfile; one line added to crates/pty/Cargo.lock). No product dependency change.
  • devguard-launch is built from the pinned DevGuard source only by the validation stages; it is not a CodeSpace dependency, and no product graph contains it.
  • Product graphs (scripts/upstream_dependencies.py, aarch64-apple-darwin, development edges excluded): all nine (root, root+devguard, codex-runtime, codex-runtime+devguard, devguard, pty, patch, file-system, linux-sandbox) have the same packages and edges as main e0610c4, and no violations (u4-local-14, on c1c7859).

Feature-off result

Without the devguard features the managed-launch code is not compiled (launch module, wire op, CLI flag); the pipe and PTY spawners take the same paths as before; cargo test --workspace and the feature-off runner tests pass (u4-local-14: clippy without features passed; u4-local-16: cargo test --workspace passed, runner 105 and gateway 30 without features).

Rollback

Revert this PR. Operationally, without reverting: drop --devguard-launch-helper to return governed workspaces to U3's admit-then-refuse, or set resources.participation to off to run them ungoverned as before. Processes already launched remain CodeSpace processes; DevGuard settles their attempts through their scopes. No stored data or wire version changes (a U3 worker without launch is driven as in U3).

Out of scope

  • Observation before reap, reaper unification, release on termination/timeout/shutdown paths, approval dispatch states and no-replay lifecycle beyond launch (CSRG-U5).
  • Qualification (CSRG-U6), P1-RECOVERY, DG-LINUX.
  • Any Codex or DevGuard pin change, a DevGuard release or a change to the installed DevGuard service.
  • The macOS first launch of a newly placed worker binary against the 2 s waits of wait_for_socket and runtime_binary.rs, and the timing of prepare_stdout_flood_is_too_large (see Tests); candidates for a separate change.
  • Release latency: DevGuard holds a launched attempt until its reconciler sees the scope end, so a burst of short governed commands on a small host can be refused with RESOURCE_UNAVAILABLE although earlier ones have ended (seen in the D6 analysis above: nothing released while rounds ran back to back for about 2 s). Faster release belongs to CSRG-U5's release paths.

novelKR and others added 3 commits October 9, 2026 15:05
…d (CSRG-U4)

A prepared execution in a workspace that requires resource participation can
now be launched instead of cancelled, when its owner has a launch helper:

- codespace-devguard: BeginLaunch in a bounded owner session, sent once. Only a
  permit that comes with this attempt's committed record is a grant.
  AbandonLaunch reports a grant the owner holds no helper for. The helper's
  invocation comes from DevGuard's helper_command but is spawned by CodeSpace:
  it exposes the helper's arguments and its two private descriptors (permit
  carrier, transcript writer) and reads the transcript to its end.
- codespace-pty: spawn_inheriting passes named descriptors through the pinned
  Codex PTY's inherited_fds path (Codex 0.154, unchanged).
- runner: exclude_unrelated_except keeps exactly those two descriptors in the
  helper's pipe child; the PTY spawn makes them inheritable only for the
  spawn. PreparedExecution::launch_managed checks locally, sends BeginLaunch
  once, settles a lost answer with one lookup (cancel when still prepared,
  AbandonLaunch when committed and unclaimed), spawns the helper into the
  prepared slot and reads the transcript: READY then close is a running
  command; exec failure, refusal, failure or an end before READY is
  reported, the last three after the helper is reaped.

The gateway and the worker do not use it yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (CSRG-U4)

With --devguard register and the new --devguard-launch-helper (the absolute
path of devguard-launch from the DevGuard release that serves the socket),
the execution owner launches each execution of a workspace that requires
resource participation once, instead of cancelling it:

- InProcess: the gateway's preparer launches through its launcher.
- UDS: the gateway passes the helper's path to the worker it starts. The
  worker states `launch` in its Hello only with a registration and a helper,
  and answers one `launch` request per execution, which is never cached or
  sent again. A lost answer is an unknown dispatch for the process_id; a
  request that never left is owner_unreachable.
- exec_command arms the workspace lease for dispatch and returns the
  process_id with dispatch_status confirmed or unknown, or the refusal of a
  command that did not start. Without a helper, U3's admit-then-refuse stays.
- workspace_info reports governs_execution true when the owner is registered
  and has a helper.
- check_helper accepts a group write only from root's group (wheel), which
  only root can do.
- An uncertain launch reports that the owner holds no helper once its process
  has ended, so an unclaimed grant does not stay charged.

Validation builds devguard-launch from the DevGuard client pin with
DevGuard's own lockfile (cargo install --locked --git ... --rev) and names it
in CODESPACE_DEVGUARD_LAUNCH_BIN; macos-core also runs the runner's launch
tests. Gateway tests launch through the fixture authority and the real
helper in both modes, on pipes and PTYs, with stdin, resize, termination and
status, and check D6 with concurrent launches beside ordinary children of the
same gateway. Documentation: the managed launch section and its Korean
counterpart, and the helper's provenance in the upstream records.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…es (CSRG-U4)

- A launch test in a process of its own checks that the owner holds neither
  of the helper's descriptors once the helper has started, on a pipe and on
  a PTY.
- A governed process times out as any other, through CodeSpace's own
  timeout, on a pipe and on a PTY.
- The registration participation's description no longer says that nothing
  is admitted or launched through the authority.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@novelKR novelKR self-assigned this Oct 9, 2026
novelKR and others added 4 commits October 9, 2026 18:17
… it (CSRG-U4)

`helper_bin` is used only by the adapter's native launch tests, which run
on macOS. On Linux the test build left it unused, and CI's
`clippy-adapters` failed with dead code under `-D warnings`. It now lives
in the `native` module, beside its only callers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…form's default path (CSRG-U4)

CI's Linux integration job failed ten runner launch tests. In nine, the
stand-in helper ended before READY: it was a `/bin/sh` script that
redirects the carriers by number, and dash, Linux's `sh`, takes only
one-digit descriptor numbers in redirections, while the carriers sit
above 9 in a busy test process. The stand-in now runs under bash. The
real helper is DevGuard's Rust program.

In the tenth, `resolve_program` searched macOS's default path
(`/usr/bin:/bin`) on every platform when the environment has no `PATH`,
while glibc's `execvp` searches `/bin:/usr/bin`, so on Linux it could
name a different file than the spawn would. It now uses the platform's
default. Managed launch is not supported on Linux yet, so no execution
reached this.

The documentation's helper rule now states the group write by gid 0
that `check_helper` accepts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CI's macOS job failed the D6 test: DevGuard refused a governed launch
of a later round with resource_unavailable. Each round was admitted
while earlier rounds' attempts were still charged: DevGuard releases a
launched attempt only once its reconciler has seen the scope end, and
the test waited for that only after its last round. On the 3-CPU runner
DevGuard's CPU work capacity is 480 m (3000 less 2000 of host headroom,
500 for the system and 20 reserved), nine 50 m attempts, which the
rounds outgrew. With the request raised so that this Mac also holds
nine, the unchanged test was refused the same way, with 0, 2, 4, 6 and
8 attempts charged at the round starts; waiting at the end of each
round, every round started with none charged.

The test now waits for, and asserts, each round's release. The product
is unchanged: DevGuard refused, and CodeSpace reported a refusal that
started nothing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ADY (CSRG-U4)

macos-core failed once here on a_governed_process_times_out_like_any_other:
the launch was reported as not started (HelperEnded). The test set the
request's timeout to 300 ms, and CodeSpace's timeout runs from the spawn,
which is the helper's. Under the load of the parallel tests the stand-in
helper had not reported READY by then, so the timeout ended it first and
the launch was rightly reported as not started. With a 5 ms timeout the
test failed this way in three runs of three. The test means a timeout of
the running command, so it now allows 2 s (the command sleeps 30 s); the
macos-core runner command then passed in five runs of five.

The documentation now says that a governed execution's timeout runs from
the helper's spawn, and so also covers the helper's exchange with
DevGuard before READY.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@novelKR
novelKR marked this pull request as ready for review October 9, 2026 14:25
@novelKR
novelKR merged commit 3e5e3e8 into main Oct 9, 2026
19 checks passed
novelKR pushed a commit to novelKR/DevGuard that referenced this pull request Oct 9, 2026
CSRG-U4 merged in CodeSpace on 2026-10-09 as novelKR/CodeSpace#90
(3e5e3e8, at head c1c7859, whose CI passed in full; the merge commit has
that head's tree). Record U4 as merged-verified with the units dated
2026-10-09, 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>
novelKR pushed a commit to novelKR/DevGuard that referenced this pull request Oct 11, 2026
The owner directed CSRG-U5 to start on 2026-10-09, together with merging
CodeSpace's test-timing cleanup, after CSRG-U4 merged in CodeSpace as
novelKR/CodeSpace#90 (3e5e3e8), recorded here by #29. The cleanup merged
as novelKR/CodeSpace#91 (470069c) on 2026-10-11 and U5 started that day.
Record U5 as active with the units dated 2026-10-11, keep 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>
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