From 9cba782ab2bca491973ad14a5172710f47f28ab8 Mon Sep 17 00:00:00 2001 From: Seongjae Date: Sun, 27 Sep 2026 22:13:31 +0900 Subject: [PATCH 1/5] docs(handoff): record the CS-RG integration-boundary revalidation of 2026-09-27 The owner-directed review of the CodeSpace/DevGuard integration boundary stopped at a decision packet. Record its results for the next session: the owner's fixed inputs, the state of the open PRs, findings with links to fixed revisions, the gap resolution with excluded options and surviving candidates, the recommendation, the decisions still needed, how to continue and where the evidence is kept. This is a handoff, not a design decision: no architecture is approved, no unit is started and no ledger status or count changes. --- .../2026-09-27-cs-rg-boundary-revalidation.md | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md diff --git a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md new file mode 100644 index 0000000..fd8934a --- /dev/null +++ b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md @@ -0,0 +1,146 @@ +# Session handoff: CS-RG integration-boundary revalidation (2026-09-27) + +> **Status: submitted for the owner's decision; not a design decision.** This handoff records a review and its +> recommendation. It approves no architecture, starts no work unit and changes no ledger status. CS-RG +> implementation stays suspended: CSRG-C00, CSRG-C03 and CSRG-C09 and the CodeSpace-owned managed execution +> transport they assume are not implementation directives. Nothing below authorizes implementation. + +## 1. Why this review happened +The owner's directive CS-DG-REASSESS-2026-09-27 and the owner's clarifications of the same day placed the +managed-PTY C00 path on hold and asked for a boundary review before any CS-RG implementation. The review label +"CS-RG integration-boundary revalidation" names an owner-directed review; it is not a work unit. + +The owner fixed these inputs: +- CodeSpace's product semantics, responsibility boundaries and selective use of Codex upstream for execution + mechanisms are constraints of the integration, not options to trade away. DevGuard, a later opt-in, adapts. +- CodeSpace constraints (N1) and DevGuard safety invariants (N2) are joint hard constraints with no priority + between them. A combination that cannot meet both is unsupported; neither is weakened. +- Current code is evidence, not a constraint. The CS-RG planning documents (design revision 1, CSRG-C00/C03/C09 + and the matching CodeSpace text) are audit targets, not requirement sources. +- CS-RG alone never justifies moving a Codex-delegated mechanism (notably PTY) into CodeSpace or refactoring + unaffected CodeSpace paths for DevGuard or for symmetry. CodeSpace changes stay a thin adapter. +- The review stops at a decision packet. A normative design PR, new units and any code come only after the + owner's decision. + +## 2. State at a glance (2026-09-27 13:05 UTC) +| Item | Value | +| --- | --- | +| DevGuard `main` | `7e3cbda91308f527d6cc34fba908375e6332bc58` | +| CodeSpace `main` | `794867ef52f530be6bc0d91aa10416d5195367b7` | +| Codex pin in CodeSpace | `6b9826e3aa83b1a5947db50f4332cb9c65f1b340` (rust-v0.154.0), unchanged | +| Hold notices | [DevGuard #12](https://github.com/novelKR/DevGuard/pull/12) (head `ae85ebb95b68821361ae59d3c60b5344a1d8ab03`) and [CodeSpace #75](https://github.com/novelKR/CodeSpace/pull/75) (head `1bee230595698b0974df43561bccfce67d7e8cb9`): open, all required checks green, waiting for exact-head merge approval | +| Independent test fix | [DevGuard #11](https://github.com/novelKR/DevGuard/pull/11) (head `2bbe7c5ed88ad3170bc76985bdecb1fd7434d501`): reviewed, green, waiting for exact-head merge approval | +| Product code, pins, services, credentials | unchanged | + +Until #12 and #75 merge, the CS-RG documents on `main` still read as directives. No agent implements from them. + +## 3. Findings +Evidence levels: confirmed (read at the fixed revision), inferred, requires experiment. + +| ID | Finding | Source | Level | +| --- | --- | --- | --- | +| F-1 | The priority inversion is documented, not executed. Revision 1 listed CodeSpace's Codex PTY use and the Codex pin as changeable means and did not list DevGuard's helper, direct-child check, descriptor layout or observe-before-reap; the plan then fixed DG-1's consumer interface for every unit | [design-revision-1.md L96-L107](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/design-revision-1.md#L96-L107), [CS-RG.md L22](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/planning/milestones/CS-RG.md#L22) | confirmed | +| F-1a | That premise became the D1 default and the C00, C03 and C09 definitions, and entered CodeSpace's own reuse policy | [design-revision-1.md L479](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/design-revision-1.md#L479), [CS-RG.md L47](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/planning/milestones/CS-RG.md#L47), [L88](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/planning/milestones/CS-RG.md#L88), [L164](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/planning/milestones/CS-RG.md#L164), [CodeSpace codex-reuse.md L72](https://github.com/novelKR/CodeSpace/blob/794867ef52f530be6bc0d91aa10416d5195367b7/docs/codex-reuse.md#L72) | confirmed | +| F-1b | Before CS-RG, CodeSpace documented selective Codex reuse with PTY through `codex-utils-pty` | [codex-reuse.md L7 and L18 at e94d214](https://github.com/novelKR/CodeSpace/blob/e94d21475643608ad2a466256fb57266b86faa47/docs/codex-reuse.md#L7-L18) | confirmed | +| F-2 | No unauthorized execution was found. One lapse is self-reported: a queued read-only subagent delegated under the old instruction was not stopped and wrote a superseded C00 plan after the hold, without repository or product effect | session records | confirmed | +| F-3 | The core mismatch is DevGuard's launch API: `HelperCommand` makes DevGuard's client perform the spawn, so a governed PTY launch would take PTY and spawn ownership from Codex | [launch.rs L150-L154](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/crates/client/src/launch.rs#L150-L154) | confirmed | +| F-4 | Observation before reap is not a safety invariant: if the owner reaps first, unknown survivors become tracking loss and the attempt stays Suspect; it is never released | [contracts.md, Reconciliation](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/contracts.md#reconciliation) | confirmed; frequency requires experiment | +| F-5 | Codex reaps PTY children internally and reports only an exit code at the pin, at rust-v0.157.1 and on main | [pty.rs L244-L253 at the pin](https://github.com/openai/codex/blob/6b9826e3aa83b1a5947db50f4332cb9c65f1b340/codex-rs/utils/pty/src/pty.rs#L244-L253) | confirmed | +| F-6 | Delivering close-on-exec descriptors only to the intended child (`ChildFds::Attached`) exists on Codex main and in prereleases, first in #47797 (2026-09-24); no stable release has it | [pty.rs L405-L408 at main 41f9084](https://github.com/openai/codex/blob/41f9084b30812db321a0b592def4f500d1e79cf4/codex-rs/utils/pty/src/pty.rs#L405-L408) | confirmed from source; behaviour requires experiment | +| F-7 | macOS cannot create pipes or socket pairs close-on-exec atomically; DevGuard's `spawn_guard` covers the window, but CodeSpace's Tokio pipe, patch-helper and probe spawns neither hold it nor close descriptors in the child | [launch.rs L175-L190](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/crates/client/src/launch.rs#L175-L190) | confirmed | +| F-7a | DevGuard's client session socket has the same window outside `spawn_guard` | [connect.rs L39-L51](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/crates/client/src/connect.rs#L39-L51) | window confirmed; exposure suspected | +| F-8 | Codex exposes no PID for PTY children at the pin or on main | `SpawnedProcess` in [process.rs L356-L361](https://github.com/openai/codex/blob/6b9826e3aa83b1a5947db50f4332cb9c65f1b340/codex-rs/utils/pty/src/process.rs#L356-L361) | confirmed | +| F-9 | DevGuard declares Rust 1.95 and CodeSpace 1.88, so a DevGuard client dependency in CodeSpace must stay optional for the `off` build | workspace `Cargo.toml` of both repositories | confirmed | +| F-10 | CodeSpace issues outside CS-RG: the tool text promises subtree termination that the code does not perform; a PTY handle dropped 15 minutes after exit signals a stored numeric pgid (Codex behaviour); worker setup failure leaves the worker and its directory | [mcp.rs L124-L125](https://github.com/novelKR/CodeSpace/blob/794867ef52f530be6bc0d91aa10416d5195367b7/crates/server/src/mcp.rs#L124-L125), [process.rs L33](https://github.com/novelKR/CodeSpace/blob/794867ef52f530be6bc0d91aa10416d5195367b7/crates/runner/src/process.rs#L33), [Codex process.rs L273-L277](https://github.com/openai/codex/blob/6b9826e3aa83b1a5947db50f4332cb9c65f1b340/codex-rs/utils/pty/src/process.rs#L273-L277), [runtime.rs L42-L50](https://github.com/novelKR/CodeSpace/blob/794867ef52f530be6bc0d91aa10416d5195367b7/crates/server/src/runtime.rs#L42-L50) | confirmed from code; not run | + +## 4. Gap resolution +| Gap | Conclusion | Change locus | +| --- | --- | --- | +| Helper spawn ownership | DevGuard exposes the helper launch as data (program, argv, attachments or tickets); CodeSpace's existing backends spawn it | DevGuard client; adapter | +| Descriptor delivery, PTY | either Codex `ChildFds::Attached` after a pin update (route Y) or no descriptors at all (route X), which needs a small generic Codex PID accessor | Codex pin or upstream; DevGuard; adapter | +| Descriptor delivery, pipe | no descriptors: the owner confirms the helper's PID, which it already knows for Tokio children | DevGuard wire; adapter | +| Descriptor creation window | removed by the no-descriptor route, or by creating grant descriptors atomically close-on-exec; CodeSpace taking the guard is excluded | DevGuard | +| Observation before reap | accept Suspect for unobserved survivors after measuring it, or let the helper supervise the payload (PTY) | DevGuard helper | +| UDS worker credential | send it over the existing Runner channel, have the worker read an operator-provisioned credential, or leave UDS-mode `required` unsupported | owner decision | +| Lost replies, output contract, macOS parentage, termination | existing paths fit | adapter only | + +Excluded before comparison because they break N1 or N2: CodeSpace allocating the PTY for a DevGuard spawn; +`off` on Codex with `required` on a CodeSpace-owned transport; converging the `off` backends; CodeSpace holding +DevGuard's guard; inheritable descriptors without protection; a permit through the terminal; a CodeSpace-built +`ProcessDriver`; removing the permit without an equal proof; releasing on reap; a supervising helper for pipe +while terminate stays unchanged. + +Surviving candidates (all designed to meet N1 and N2; none verified by experiment): + +| Candidate | What | Upstream dependency | +| --- | --- | --- | +| R | governed pipe first on macOS InProcess: launch as data, owner-confirmed helper identity, transcript through the authority, reap-first accepted with measurement | none | +| X | R's mechanism for PTY too | a generic PTY child PID accessor in Codex, then a release and a pin update | +| Y | PTY with an attached permit file through `ChildFds::Attached` | a stable Codex release containing #47797 and a pin update | + +Current verdicts: every macOS `required` combination is unsupported until the DevGuard changes land; pipe needs +nothing else; PTY also needs X or Y; UDS mode also needs the credential decision; Linux waits for DG-LINUX; +`off` is unchanged. + +## 5. Recommendation (not a decision) +| Topic | Recommendation | Confidence | +| --- | --- | --- | +| Boundary | DevGuard adapts to CodeSpace's existing execution; CodeSpace adds a thin adapter and generic events; Codex is consumed through normal pin updates and, where missing, a small generic upstream API | high that it meets the constraints as designed | +| First step | R | medium; requires experiment | +| PTY | X, keeping one DevGuard mechanism for both transports; Y as fallback; PTY `required` stays unsupported until one lands | medium-low | +| Observation | accept reap-first at first and measure it | medium | +| UDS mode | credential over the existing Runner channel | low-medium | + +## 6. Decisions needed from the owner +1. Exact-head merge approvals for DevGuard #12, CodeSpace #75, DevGuard #11 and this handoff PR. #12, #11 and this + PR share DevGuard's base; merging one moves the others' base, which needs a branch update and a new head + approval unless the approval allows that move. +2. The boundary statement in section 5. +3. The route for governed PTY (R then X, R then Y, or another order). +4. Whether governed pipe terminate may also call DevGuard `Terminate` (only needed for a supervising helper on + pipe). +5. Whether a generic "attach descriptors" input on CodeSpace's Tokio pipe spawn is acceptable (default: no). +6. The UDS worker credential path, or UDS-mode `required` left unsupported. +7. Accept reap-first with measurement, or require the supervising helper. +8. If X: approval to submit the upstream Codex PR (a local draft is allowed without it). +9. Unit identifiers: a proposal to record the hold PRs and the later normative correction as the next + documentation units (DGP-D08, CSP-D05) and to replace the CS-RG unit sequence with new units instead of + redefining CSRG-C00. +10. Whether to fix the client session-socket window (F-7a) as a DevGuard-only change. + +## 7. How to continue +- Start by confirming state: both `main` heads, the Codex pin and the heads of the open PRs above. +- Do not implement, prototype or plan a CodeSpace-owned PTY or process backend for CS-RG. Do not start any CS-RG + unit. Do not open a normative design PR before the owner's decisions. +- Merge only with an explicit approval tied to the exact head: recheck head and base, merge with + `--match-head-commit`, read the post-merge `main` CI once, preserve it, then fast-forward local `main` and clean + up without deleting remote branches. +- After the decisions: the next task is a normative design correction in DevGuard and CodeSpace that records the + approved boundary, the thin adapter contract, the chosen route and the new unit sequence, each merged only with + its own approval. Product implementation needs a further explicit approval. +- Public records carry no local absolute paths. + +## 8. Evidence +Raw evidence and the full review records stay outside commits by repository rule; this handoff carries their +conclusions. Location and manifests (SHA-256 of `MANIFEST.json`): + +| Record | Location | Manifest | +| --- | --- | --- | +| Review records (requirements, findings, ownership map, upstream matrix, DevGuard decomposition, gap resolution, decision packet, handoff, directive copy, upstream source snapshots) | `/evidence/cs-rg/boundary-revalidation-2026-09-27/` | `85813d5967148ff75384c900ce79a306d528d587b44b0bc796966720ce2a65a7` | +| Codex source snapshots (pin, rust-v0.157.1, main `41f9084`) | same, `stage4/` | `b58a308fbbb2243680847c2c350f8d24323513a5128206801cdeb76ad1842cc4` | +| Hold PR records | same, `stage1/` | `309b210c1cf87b795797236355dde8ad97ee776cfb2bbaa339e3d618ccd1a2aa` | +| DevGuard #11 local experiments, review and exact-head CI | `/evidence/dg1-delivery/stuck-probe-sample-time/` | `138dc8b513096b80802b6fc54d790dac92eec9475429e2a34b2e9096b68e51a0` (experiments) | +| Superseded C00 materials (pre-investigation, late harness plan) | `/evidence/codespace-delivery/csrg-c00/` | `c1690398c85ff02b1178c51dc6d9613a155171835055f54f49c37e546828ea2b` | + +Not run: behaviour of Codex main's `Attached` path (needs a Codex workspace build the host's memory did not +allow); frequency of reap-first Suspect cases (needs real workloads); a supervising helper prototype (would +precede the decision). The DevGuard #11 reproduction kept its run helper but not each case's invocation; that +limit is stated in the PR. + +## 9. Pitfalls met +- A subagent whose status reads "not found" is queued, not gone. A hold must reach it when it starts. +- Korean counterparts are maintained documents with their own structure; match sections by content, not by + position. +- CodeSpace documentation avoids unexplained work-package numbers; explain a CS-RG item where it is mentioned. +- On macOS every per-grant descriptor created with `pipe()`, `socketpair()` or `socket()` is inheritable until + it is duplicated or flagged; any descriptor design has to account for concurrent forks. From 06028bc21b16fcad069c0d81fe2b158fd2ade00e Mon Sep 17 00:00:00 2001 From: Seongjae Date: Sun, 27 Sep 2026 22:15:30 +0900 Subject: [PATCH 2/5] docs(handoff): do not pin the regenerated review manifest hash --- docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md index fd8934a..b9a63ce 100644 --- a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md +++ b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md @@ -126,7 +126,7 @@ conclusions. Location and manifests (SHA-256 of `MANIFEST.json`): | Record | Location | Manifest | | --- | --- | --- | -| Review records (requirements, findings, ownership map, upstream matrix, DevGuard decomposition, gap resolution, decision packet, handoff, directive copy, upstream source snapshots) | `/evidence/cs-rg/boundary-revalidation-2026-09-27/` | `85813d5967148ff75384c900ce79a306d528d587b44b0bc796966720ce2a65a7` | +| Review records (requirements, findings, ownership map, upstream matrix, DevGuard decomposition, gap resolution, decision packet, handoff, directive copy, upstream source snapshots) | `/evidence/cs-rg/boundary-revalidation-2026-09-27/` | `MANIFEST.json` in that directory lists every file with its SHA-256; it is regenerated when records are added, so its own hash is not pinned here | | Codex source snapshots (pin, rust-v0.157.1, main `41f9084`) | same, `stage4/` | `b58a308fbbb2243680847c2c350f8d24323513a5128206801cdeb76ad1842cc4` | | Hold PR records | same, `stage1/` | `309b210c1cf87b795797236355dde8ad97ee776cfb2bbaa339e3d618ccd1a2aa` | | DevGuard #11 local experiments, review and exact-head CI | `/evidence/dg1-delivery/stuck-probe-sample-time/` | `138dc8b513096b80802b6fc54d790dac92eec9475429e2a34b2e9096b68e51a0` (experiments) | From 1af1921fb72bb969b4f19ff859bd59fdb361ca55 Mon Sep 17 00:00:00 2001 From: Seongjae Date: Sun, 27 Sep 2026 22:53:05 +0900 Subject: [PATCH 3/5] docs(handoff): harden the CS-RG revalidation record against overstatement Apply the owner's review of the decision packet, checked against source: - add a companion candidate analysis with the review disposition, corrected findings with evidence levels, the reap-first safety-versus-operability state analysis (backed by existing native tests), the candidate R message sequence and its gaps, the meaning of authority-mediated results, the client session-socket window, what X and Y do and do not solve, the supervising-helper and group-anchor variants, candidate and support matrices, and the decisions that remain - rewrite the handoff so no route is presented as supportable, drop the request to re-approve fixed principles, and correct the "not found" and base-versus-head statements Still a record for the owner's decision: no architecture is approved, no unit is started and no ledger status or count changes. --- ...27-cs-rg-boundary-revalidation-analysis.md | 291 ++++++++++++++++++ .../2026-09-27-cs-rg-boundary-revalidation.md | 215 ++++++------- 2 files changed, 378 insertions(+), 128 deletions(-) create mode 100644 docs/handoff/2026-09-27-cs-rg-boundary-revalidation-analysis.md diff --git a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation-analysis.md b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation-analysis.md new file mode 100644 index 0000000..6303d2e --- /dev/null +++ b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation-analysis.md @@ -0,0 +1,291 @@ +# CS-RG boundary revalidation: hardened candidate analysis (2026-09-27) + +> **Status: candidate analysis for the owner's decision; not a design decision.** It approves no architecture, +> starts no work unit, changes no ledger status and authorizes no implementation. The CS-RG implementation hold +> stays in force. Companion to the [session handoff](2026-09-27-cs-rg-boundary-revalidation.md). + +Revisions: DevGuard `7e3cbda91308f527d6cc34fba908375e6332bc58`, CodeSpace `794867ef52f530be6bc0d91aa10416d5195367b7`, +Codex pin `6b9826e3aa83b1a5947db50f4332cb9c65f1b340` (rust-v0.154.0), Codex stable rust-v0.157.1 +`36650394c5b38c2990ccf2a3457165ca3e9d9726`, Codex main `41f9084b30812db321a0b592def4f500d1e79cf4` (looked up 2026-09-27 +12:27Z). Line references are to those revisions. + +Evidence levels: **source** (confirmed source fact), **test** (confirmed diagnostic result), **inference**, +**candidate** (unverified candidate), **experiment** (requires experiment), **owner** (owner decision), +**unsupported** (unsupported under current constraints). + +## 1. Review disposition +The owner's reference review of 2026-09-27 was used as a checklist and checked against source; where they differ, +source decides. + +| ID | Review point | Checked | Verdict | Correction | +| --- | --- | --- | --- | --- | +| RR-1 | Conclusions ("pipe needs nothing else", "PTY needs only a small precondition") are stronger than the evidence | the earlier handoff text; sections 3-12 below | confirmed | every route is an incomplete candidate; no combination is shown supportable (section 12) | +| RR-2 | Reap-first safety and operability were conflated | contracts.md Observation, Limits, Reconciliation; crates/launch/tests/reconcile.rs L470-L560; crates/macos/src/scope.rs L628; macOS CI run 36311129096 | confirmed and strengthened: the leak is deterministic and has no recovery before reboot on macOS | section 3; RE reclassified | +| RR-3 | RB/RC were called required without a protocol | earlier packet; contracts.md Fenced launch helper | confirmed | RB/RC are incomplete candidates; protocol and gaps in sections 4-5 | +| RR-4 | Authority-mediated results need defined meaning | contracts.md Transcript, Command-line owner | confirmed | section 6 | +| RR-5 | The client socket window gates R; a DevGuard-local mutex is not enough | crates/client/src/connect.rs L39-L51; crates/client/src/launch.rs L29-L36, L150-L154 | confirmed; the window also exists in the current DevGuard design, not only in R | section 7; D6 becomes a prerequisite of every CodeSpace route | +| RR-6 | X and Y each solve a narrow slice | upstream sources (pty.rs, process.rs) | confirmed | section 8 | +| RR-7 | A supervising helper changes CodeSpace-visible semantics | CodeSpace process.rs L360-L384, L706-L740; Codex pty.rs L55-L92 | confirmed for pipe; open for PTY | section 9 (also adds a group-anchor variant) | +| RR-8 | "Existing termination paths fit" is not established | Codex process.rs L273-L277; CodeSpace process.rs L33, L208-L211; runtime.rs L42-L50 | partially confirmed: DevGuard release never relies on CodeSpace signals, but UDS worker kill and eviction-time signalling affect operability and unrelated processes | section 10 | +| RR-9 | The public handoff was too summarized to review | earlier handoff | confirmed | this document | +| RR-10 | "G3 complete" could be read as technical verification complete | earlier report wording | confirmed | status is: review record submitted; candidate contracts not verified; product support pending | +| RR-11 | "`not found` means queued" overgeneralizes one incident | spawn records of 2026-09-27 | confirmed | section 14 | +| RR-12 | Base movement is not head movement | Git and GitHub behaviour; the owner's 08:35Z merge rule | confirmed | section 14 | +| RR-13 | The owner should not be asked to re-approve fixed principles or to pick X or Y prematurely | owner instructions of 2026-09-27 | confirmed | section 13 lists only open choices | +| RR-14 | Merge approvals and architecture decisions must be separate tracks | same | confirmed | section 13 | +| RR-15 | The hold PRs and the handoff PR are appropriate records | DevGuard #12, CodeSpace #75, DevGuard #13 | confirmed | none | + +No review point was contradicted by source. One point was strengthened: RR-2 (section 3). + +## 2. Corrected findings +| ID | Finding | Level | +| --- | --- | --- | +| F-1 | Design revision 1 listed CodeSpace's Codex PTY use and pin as changeable and kept DevGuard's helper, direct-child check, descriptor layout and observe-before-reap fixed (design-revision-1.md L96-L107; CS-RG.md L22); this became the D1 default and C00/C03/C09 (L479; CS-RG.md L47, L88, L164) and entered CodeSpace's reuse policy (codex-reuse.md L72) | source | +| F-2 | No unauthorized execution. One self-reported lapse: a delegated read-only run whose status was unresolved was not stopped and wrote a superseded C00 plan after the hold; no repository or product effect | source (records) | +| F-3 | `HelperCommand` makes DevGuard's client perform the spawn (launch.rs L150-L154), so a governed PTY launch would take PTY and spawn ownership from Codex (N1.2) | source | +| F-4 | Reap-first cannot release a lease falsely, and it leaves a charged Suspect attempt that no later ordinary observation clears; on macOS only a reboot releases it (section 3) | source + test | +| F-5 | No Codex revision checked offers pre-reap observation or owner-controlled reap for PTY children (pin pty.rs L244-L253; main pty.rs L447-L449) | source | +| F-6 | `ChildFds::Attached` (main and prereleases, #47797) delivers close-on-exec descriptors only to the intended child (main pty.rs L405-L408, L526); no stable release has it; runtime behaviour untested | source; behaviour: experiment | +| F-7 | macOS cannot create pipes or sockets close-on-exec atomically. DevGuard's grant descriptors are created under `spawn_guard` (launch.rs L175-L190), its session sockets are not (connect.rs L39-L51), and CodeSpace's Tokio pipe, patch-helper and probe spawns neither take the guard nor close descriptors in the child | source | +| F-8 | Codex exposes no PTY child PID at the pin or on main (`SpawnedProcess`, process.rs L356-L361) | source | +| F-9 | Scope establishment accepts a root only while it is alone in its group (contracts.md, Native policy application) | source | +| F-10 | DevGuard needs Rust 1.95, CodeSpace declares 1.88; a DevGuard client dependency in CodeSpace must stay optional for the `off` build | source | +| F-11 | CodeSpace-only issues, outside CS-RG and not relied on by DevGuard's release rules: tool text promises subtree termination the code does not perform (mcp.rs L124-L125); a PTY handle dropped at eviction, up to 15 minutes after exit, signals a stored numeric pgid (Codex process.rs L273-L277 with CodeSpace process.rs L33); worker setup failure leaves the worker and its directory (runtime.rs L51-L64) | source; runtime not run | + +## 3. Reap-first: safety versus operability +Scenario: the payload root starts a child that stays in the group (`/bin/sh -c "/bin/sleep 3 & exit 0"`), the +root exits, and the existing backend reaps it at once (Codex PTY: immediately; CodeSpace pipe: within about 20 ms). + +| Step | Known | Unknown | Tracking loss | Suspect | Release possible | Evidence | +| --- | --- | --- | --- | --- | --- | --- | +| 1. root exits | the root is a zombie holding its PID and group ID | — | no | no | not yet | source (contracts.md Observation) | +| 2. backend reaps the root | root start identity gone | whether group members belong to the scope | — | — | — | source | +| 3. a descendant is in the group | a process with that group ID exists | whether it is the scope's or a reused group | — | — | — | source | +| 4. no observation adopted it while the root held its PID | — | its membership | — | — | — | source: adoption needs the root to hold its PID, or a known member in the same pass | +| 5. authority observes afterwards | unknown member present, no known member | — | yes, sticky | yes | no | test: `a_root_reaped_before_observation_leaves_its_survivor_as_tracking_loss` (crates/launch/tests/reconcile.rs L521-L560) | +| 6. the descendant exits | group empty | — | still sticky | yes | no | test: same, "The loss is sticky after the survivor ends" (L548-L552) | +| 7. later Observe and reconciler passes | same | — | sticky | yes | no | source: "no later ordinary observation clears them"; the macOS backend always reports `prior_tracking_loss_resolved: false` (crates/macos/src/scope.rs L628) | +| 8. daemon restart | every committed attempt becomes Suspect; bound scopes stay Suspect until reboot | — | lost | yes | no | source (contracts.md Restart); test: `a_daemon_crash_keeps_every_charge_and_restart_fences_old_grants` | +| 9. reboot | the scope is from an earlier boot | — | irrelevant | — | yes, as previous-boot termination | source (contracts.md Reconciliation); test: `reconcile_releases_a_bound_scope_after_a_reboot_as_previous_boot` | + +Contrast: when the owner observes while the root is still unreaped, the survivor is adopted and the attempt is +released after it ends (test `observing_before_reap_tracks_survivors_and_release_waits_for_them`, L470-L516). + +Diagnostic status: the three native tests above ran in the macOS job of DevGuard CI run 36311129096 (head +`2bbe7c5`, native reconciliation suite "13 passed; 0 failed; 4 ignored"). The pathological sequence is therefore +established by an existing diagnostic; no new diagnostic was needed. + +Conclusions: +- Safety against false release: holds (source, test). +- Operability: fails for this pattern. A normally completed workload that leaves a short-lived background + descendant born after the last observation keeps its reservation charged until the host reboots. There is no + unconditional release (contracts.md Reclamation evidence) and no native path that resolves prior tracking loss. + Repeated occurrences shrink admission capacity although the host is idle. (source, test) +- With both CodeSpace backends reaping at once, the observe-before-reap window is effectively zero, so any + survivor not already adopted by an earlier reconciler pass (1-second cadence) triggers this outcome. (inference) +- Frequency in representative agent workloads: unknown (not measured). The existence of the sequence does not + depend on that measurement. + +## 4. Authorization: current mechanism and the RB candidate +| Property | Current design (contracts.md Fenced launch helper) | RB candidate (owner-confirmed identity, no permit) | Status | +| --- | --- | --- | --- | +| One-time launch authorization | one-time permit (secret) + durable claim + first `authorize_run` sets `may_exec` | durable single binding of one OS identity (PID, start, boot) to the attempt, made from the owner's authenticated confirmation; then the same claim and first `authorize_run` | candidate; binding must be durable before any claim | +| Owner/helper relationship | OS: parent is the registered, running owner | same OS check, plus the owner's assertion naming the child | candidate | +| Attempt binding | the permit is the grant's | the presenting peer's OS identity must equal the bound identity; the helper's attempt ticket is a non-secret assertion | candidate | +| Credential isolation | permit on a private descriptor, closed before exec; creation under `spawn_guard` | no permit secret; the owner's session credential and socket window remain (section 7) | partial | +| Duplicate helper refusal | the first claim wins | only the bound identity can claim; a second, different confirmation for the same attempt is refused | candidate | +| Lost replies | lost `BeginLaunch` reply: reconcile, never re-grant; lost authorization reply: no exec, re-presentation gets `may_exec = false` | `BeginLaunch` carries no secret, so a replayed lookup can continue; confirmation is idempotent for the same identity; authorization unchanged | candidate | +| Pre-claim versus post-claim failure | pre-claim refusal leaves the grant unclaimed; post-claim refusal kills the helper and keeps the scope charged | unchanged; plus: a bound but unclaimed identity that is gone could support `NoHelperCreated` | candidate; needs a contract statement | +| Durable claim | scope recorded before binding | unchanged | source | +| Payload-start evidence | transcript READY then descriptor closed at exec, plus exit status | RC (section 6) | candidate | + +Uncovered gaps (RB): +- G-RB1 ordering: the helper may present before the owner's confirmation arrives; a wait or refuse-and-retry rule + within the 250 ms frame deadlines is not defined. +- G-RB2 identity binding: a PID can be reused between spawn and confirmation if the helper dies at once; the + authority must read start and boot identity itself, check parentage and liveness, and possibly the executable + path. Misbinding cases (one process bound to two attempts) need explicit rules. +- G-RB3 the owner must know the child PID: available for Tokio pipe children; not available for Codex PTY + children (F-8). +- G-RB4 wire and journal changes (a confirmation request, presentation without a permit) need version + negotiation and requalification. +- Equivalence argument: under DevGuard's trust scope (cooperative workloads, design.md L12), RB removes the + permit-leak surface; it does not yet have a reviewed threat analysis. RB is therefore an incomplete candidate, + not a required change. + +## 5. Candidate R protocol (governed pipe, macOS, InProcess) as a message sequence +Fact types: OS = observed by the authority from the OS; OWN = owner assertion; HLP = helper assertion; DUR = +durable authority state; INF = inference. + +| # | Situation | Sequence and outcome | Fact types | +| --- | --- | --- | --- | +| 1 | registration | the owner (Runner process) authenticates in a fresh session and registers its instance; identity from the OS peer | OS, DUR | +| 2 | admission | `Admit(attempt, meaning, intent)` records the attempt | DUR | +| 3 | launch commitment | `BeginLaunch` commits; returns launch data (helper path, argv with non-secret ticket), no secret | DUR | +| 4 | what the authority returns | launch data only; nothing that spawns | DUR | +| 5 | launch as data | program = helper, argv = ticket + `--` + payload argv; no `Command` object | candidate | +| 6 | who spawns | CodeSpace's existing Tokio pipe spawn, unchanged except the argv | source (P1 path) | +| 7 | helper identification | the owner sends `ConfirmHelper(attempt, pid)` right after spawn returns | OWN | +| 8 | PID + start/boot binding | the authority reads start and boot identity and parentage for that PID and binds it once | OS, DUR | +| 9 | one-time authorization | durable single binding + existing claim + first `authorize_run` | DUR | +| 10 | who may claim | only the process whose peer identity equals the binding | OS | +| 11 | two helpers | the second is not bound; refused pre-claim | OS, DUR | +| 12 | cancel before spawn | Draining; no binding accepted; the owner reports no helper (`AbandonLaunch`) | OWN, DUR | +| 13 | cancel after spawn, before authorization | Draining blocks binding and authorization; an unclaimed helper exits 125; a claimed one is killed and its scope settles by evidence | DUR, OS | +| 14 | authority response lost | owner looks the attempt up; no secret was lost; no re-admission | DUR | +| 15 | owner response lost | confirmation is re-sent; idempotent for the same identity | DUR | +| 16 | helper response lost | the helper does not exec; re-presentation gets `may_exec = false`; the attempt is fenced, not replayed | DUR (source rule) | +| 17 | duplicate presentation | same identity: `may_exec = false`; other identity: refused | OS, DUR | +| 18 | helper death before claim | owner observes the exit; `AbandonLaunch`; `NoHelperCreated` | OWN, OS | +| 19 | helper death after claim | scope-based settlement | OS, DUR | +| 20 | payload exec failure | the helper reports `exec_failed` on its session and exits 126/127 | HLP, OS | +| 21 | payload start after a lost reply | cannot happen: exec follows only a received `may_exec = true` | source rule | +| 22 | owner death | helpers fail the parent check; unclaimed grants become Suspect and are never released before reboot | source; operability gap | +| 23 | authority restart | committed attempts Suspect; bound scopes Suspect until reboot | source; operability gap | +| 24 | expiry | Prepared expires after 5 s | source | +| 25 | successful completion | root exits, the backend reaps, the reconciler releases if no unknown survivor | source | +| 26 | descendant survival | section 3: sticky Suspect until reboot unless adopted before reap | source + test | +| 27 | termination | CodeSpace's pipe terminate (SIGKILL to the payload PID, which is the helper PID after exec) unchanged; optional DevGuard `Terminate` for the scope | source; optional: owner | +| 28 | resource release | only scope termination, `NoHelperCreated` or previous-boot termination | source | + +R is an incomplete candidate. Its open items: G-RB1..4; RC gaps (section 6); the socket window (section 7); the +operability gaps at rows 22, 23 and 26; UDS mode (credential path, section 10). + +## 6. RC: results through the authority +| Question | Candidate answer | Status | +| --- | --- | --- | +| Messages | helper -> authority: presentation, READY (after credentials closed), `exec_failed{errno}`; authority -> helper: `may_exec` | candidate | +| Durable | claim, binding, `RunAuthorized` (existing); `exec_failed` would need a new journal field | source / candidate (schema change) | +| Transport acknowledgements only | READY | candidate | +| Meaning of READY | pre-exec boundaries done; authorization only; never evidence that the payload started | source (current rule) | +| `exec_failed` | helper message plus exit status 126/127 | candidate | +| Recorded but response lost | the helper still exits by its own rule; the owner reads the record by query | candidate | +| Helper disconnect | session EOF after `may_exec` looks like a successful exec and like a helper killed in between; the exit status decides, as today | source (current ambiguity kept) | +| After restart | only durable records survive; READY does not | source / candidate | +| Missing message as evidence | never evidence of non-execution | source (N2.2) | +| Replay of an old message | bound to attempt and helper identity; refused otherwise | candidate | +| Cross-attempt confusion | attempt key plus bound identity | candidate | + +## 7. Client session-socket window (D6) +- Descriptor: the AF_UNIX stream socket from `socket()` (connect.rs L39), inheritable until `F_DUPFD_CLOEXEC` + (L46) and `drop(initial)` (L51); the parent then connects the same socket (L58-L60). (source) +- Platforms: the code is not platform-gated, so macOS and Linux; Linux could use `SOCK_CLOEXEC`. (source) +- Interval: a few system calls; not measured. (source for the span, duration unmeasured) +- Who can inherit it: any fork in the owner process during that span that does not close unintended descriptors: + CodeSpace Tokio pipe spawns, the patch helper and sandbox probes (the owner is the Gateway in InProcess mode and + the worker in UDS mode, and both run these spawns); DevGuard's own `HelperCommand::spawn` too, because `connect` + does not take `spawn_guard`, and the helper passes every inheritable descriptor to the payload. Codex PTY + children close them. (source) +- Usable in the child: the inherited descriptor names the same socket, which becomes the connected, authenticated + session; the child could read replies or inject frames during that session, but cannot read the owner's + outbound credential. (inference from descriptor semantics; not demonstrated) +- R opens such sessions at every step. (design) +- N2: possible N2.1 (requests in the owner's session) and N2.5 (authenticated channel exposure). Deliberate misuse + is outside DevGuard's cooperative trust scope; accidental interference would fail the request closed. + (inference; not demonstrated) +- Protection boundary: a DevGuard-local mutex covers only forks that take it; CodeSpace's do not. On macOS a + complete fix needs every forking path in the owner process either to serialize with descriptor creation or to + close unintended descriptors in the child. That is a CodeSpace change, which CS-RG alone cannot justify (N1.3); + it could come from an independent CodeSpace descriptor-hygiene change (N1.5). On Linux, `SOCK_CLOEXEC` closes + the window atomically. (source for the mechanisms; the CodeSpace-side remedy is an owner matter) +- Related, unverified: Rust's standard pipe and socket-pair creation on macOS is believed to be non-atomic too, + which would let concurrent CodeSpace spawns leak each other's stdio pipes. Not read in this review. +- Classification: prerequisite of every CodeSpace route (R, X, Y), and a defect of the current DevGuard design + independent of CS-RG. + +## 8. X and Y: exact contribution +| Candidate capability | Solves | Does not solve | Evidence | Remaining prerequisites | +| --- | --- | --- | --- | --- | +| X: generic PTY child PID accessor | the owner can name the numeric PID of the PTY child it asked Codex to spawn | start/boot identity binding (the authority must read it), authorization (RB), lifetime tracking (DevGuard scope evidence), termination safety (K4), pre-reap observation (section 3) | source: absent at pin and main | upstream acceptance, a release, a pin update; plus every R prerequisite | +| Y: `ChildFds::Attached` | delivery of selected close-on-exec descriptors only to the intended PTY child | creation-time races for those descriptors (needs atomic creation, for example `O_CLOEXEC` files), the session-socket window (section 7), reap ownership, observation and reconciliation (section 3), authorization semantics (the permit is still needed), release evidence, terminate behaviour | source (main); behaviour: experiment | a stable release with #47797, a pin update, atomic grant-descriptor creation, section 7, section 3 | + +Neither X nor Y makes the PTY contract complete. + +## 9. Helper supervision (RA) and a group-anchor variant (RG) +RA: the helper forks the payload, waits, and exits with the payload's status after a final observation. +| Aspect | Effect | Status | +| --- | --- | --- | +| Process CodeSpace owns | the helper; the payload becomes a grandchild | source (topology) | +| Terminate and timeout, pipe | CodeSpace sends SIGKILL to the helper PID only (process.rs L360-L384, L706-L740); the payload is orphaned and keeps running | inference: incompatible with N1.1 unless governed terminate also signals the scope, and that equivalence is unproven | +| Terminate and timeout, PTY | Codex signals the group (pty.rs L55-L92), reaching the payload | inference | +| Ctrl+C, foreground group, resize | same group, so the payload receives them; a job-control shell leaves the group (an escape) | inference | +| stdin, EOF, output drain | equivalent only if the helper closes its stdio copies after fork | inference; experiment | +| Exit code | the helper must reproduce the payload's status, including signal deaths | inference; experiment | +| Helper failure after payload start | payload orphaned; CodeSpace reports an exit while work runs | inference: N1.1 violation | +Status: blocked by N1 for pipe; incomplete candidate for PTY, requiring an experiment. + +RG (added in this review): instead of supervising, the helper keeps exec-ing the payload (the payload keeps the +helper's PID) and first leaves a small group anchor in the same group that holds no stdio. A known member in the +group lets the authority adopt later members after the root is reaped. Constraints found in source: scope +establishment requires the root to be alone in its group (F-9), so the anchor could only be created after binding, +and it must be observed (adopted) before the payload can exit; that needs a helper-initiated observation, which +the helper's session does not allow today. Open: anchor lifetime and cleanup after authority loss, SIGHUP when a +PTY session leader exits, visibility of an extra process to users, and whether CodeSpace-visible semantics stay +identical. Status: unverified candidate; requires a DevGuard contract change and an experiment. + +## 10. Termination and existing CodeSpace behaviour that candidates depend on +- DevGuard's release never relies on CodeSpace's or Codex's signals; it relies on scope evidence. (source) +- CodeSpace-visible terminate for R/X/Y stays the existing mechanism: pipe SIGKILL to the payload PID; PTY group + signal. Unchanged from `off`. (source) +- Eviction-time signalling of a stored numeric pgid (F-11) is not relied on and not amplified by governance; it can + still hit an unrelated group, governed or not. (source; reuse effect inference) +- UDS mode: when the Gateway kills the worker (runtime.rs L42-L50), the registered owner dies; its unclaimed grants + become Suspect with no release before reboot (section 5 row 22). UDS mode therefore adds an operability gap on + top of the credential path. (source) +- Worker setup failure before the handshake creates no DevGuard state. (source) + +## 11. Candidate classification +| Item | Status | Solves | Unresolved | Diagnostic | Product change | Requalification | Coupling | Fallback | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| R | incomplete candidate | pipe launch without a CodeSpace backend change and without an extra Codex primitive (none identified so far) | G-RB1..4, RC schema, D6, operability (rows 22, 23, 26) | frequency of section 3 in real workloads; RG feasibility | DevGuard client, helper, wire | C05, C06, wire; CodeSpace governed parity | CodeSpace pins a DevGuard client revision | governed pipe unsupported | +| X | incomplete candidate | numeric PTY child PID | everything in R plus upstream acceptance | none until R is complete | Codex upstream API; pin | as R plus pin qualification | pin to a release with the accessor | governed PTY unsupported | +| Y | incomplete candidate | descriptor delivery to the PTY child | atomic grant-descriptor creation, D6, section 3, stable release | `Attached` behaviour under concurrent spawns | DevGuard; pin | C05, C06; pin qualification | pin to a release with #47797 | governed PTY unsupported | +| RB | incomplete candidate (route-specific: R, X) | removes the permit secret | G-RB1..4; threat analysis | ordering and PID-reuse cases | DevGuard wire | C05, C06 | wire version | keep the permit (Y) | +| RC | incomplete candidate | removes the transcript descriptor | durability of `exec_failed`; schema | none | DevGuard journal and wire | C05 | journal schema | keep the transcript descriptor | +| RE | safe against false release; fails operability for a deterministic pattern | no change needed | reservations unrecoverable before reboot | frequency only | none | none | none | a pre-reap mechanism (RG, RA, or an upstream primitive) or an explicit restriction | +| RA | blocked by N1 for pipe; incomplete candidate for PTY | observation before the root disappears | N1.1 semantics | exit, signal, EOF fidelity | DevGuard helper | C04, C05, C06 | none | RE or RG | +| RG | unverified candidate | adoption after reap without changing CodeSpace's processes | establishment order, helper-side observation, anchor lifetime, PTY SIGHUP | anchor experiment | DevGuard helper and contract | C04, C05, C06 | none | RE with restriction | +| D6 | prerequisite of every CodeSpace route; defect of the current design | — | no N1-compatible complete macOS fix identified | exposure demonstration | DevGuard client (partial); complete fix needs CodeSpace participation | C01/C02 | none | treat as an accepted risk only by an explicit owner decision after evidence | + +No route is contract-complete. Governed execution is unsupported under the current constraints until at least D6 +and the operability gap have an answer that holds N1 and N2. + +## 12. Support matrix +| Platform | Transport | Runner mode | Current status | Missing proof or change | +| --- | --- | --- | --- | --- | +| macOS | pipe | InProcess | unsupported; source-level candidate R identified; protocol incomplete; not implemented; not qualified | G-RB1..4, RC schema, D6, operability | +| macOS | PTY | InProcess | unsupported; candidates X and Y identified; protocol incomplete; not implemented; not qualified | all of the above plus X (upstream) or Y (atomic creation, stable release) | +| macOS | pipe or PTY | UDS | unsupported; as above | plus the worker credential path and worker-death operability | +| macOS | any | any, governance `off` | unchanged | none | +| Linux | any | any | unsupported pending DG-LINUX | platform milestone; Codex main routes Linux PTY launches through a setup helper, which may break the direct-child check (inference) | + +## 13. Decisions that remain +Architecture track (only genuine choices; the fixed principles are not reopened, and no route choice is asked +because no route is complete): +1. D6 direction: pursue a DevGuard-side reduction plus an independent CodeSpace descriptor-hygiene proposal (N1.5), + or first gather evidence on exposure under the cooperative trust scope and decide afterwards. +2. Operability direction for reap-first leaks: authorize a bounded RG experiment (DevGuard fixture, isolated + worktree, not committed), pursue a generic upstream reap-control primitive, or accept governed execution only + for a restricted, documented workload class after frequency evidence. +3. Upstream engagement: approval to submit generic proposals (a PTY child PID accessor; owner-controlled reap) if + the owner wants them pursued; drafting is allowed without approval. +4. UDS mode: defer it (InProcess first) or choose a credential path now. + +Merge track (separate from the above; each needs an explicit approval of its exact head): +| Repository | PR | Head | Base | Base moved since review | Head changed since review | CI | +| --- | --- | --- | --- | --- | --- | --- | +| DevGuard | #12 | `ae85ebb95b68821361ae59d3c60b5344a1d8ab03` | `7e3cbda91308f527d6cc34fba908375e6332bc58` | no | no | 4/4 success | +| CodeSpace | #75 | `1bee230595698b0974df43561bccfce67d7e8cb9` | `794867ef52f530be6bc0d91aa10416d5195367b7` | no | no | 5 success, 6 not selected | +| DevGuard | #11 | `2bbe7c5ed88ad3170bc76985bdecb1fd7434d501` | `7e3cbda91308f527d6cc34fba908375e6332bc58` | no | no | 4/4 success | +| DevGuard | #13 | changes with this document; see the PR | `7e3cbda91308f527d6cc34fba908375e6332bc58` | no | yes (this revision) | pending for the new head | + +## 14. Procedural corrections +- Delegated runs: a status of `not found` does not establish that a run is completed, cancelled, queued or gone. + Treat it as indeterminate until an authoritative state or a result resolves it. Delegated work carries the + instruction revision it was given; before its result is accepted, and when it starts, check that the revision is + still current (for example that no hold has been issued since). +- Base versus head: merging one PR advances `main`; it does not change another PR's head. Updating a PR with the + new base (merge or rebase) creates a new head. This project's rule is stricter: the owner's merge condition + re-checks head and base immediately before merging, so a moved base triggers re-verification of the PR, and an + updated head needs a new approval. That is a project approval rule, not a Git or GitHub necessity. A green CI run + does not replace review of a changed head. diff --git a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md index b9a63ce..b5342db 100644 --- a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md +++ b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md @@ -1,146 +1,105 @@ # Session handoff: CS-RG integration-boundary revalidation (2026-09-27) -> **Status: submitted for the owner's decision; not a design decision.** This handoff records a review and its -> recommendation. It approves no architecture, starts no work unit and changes no ledger status. CS-RG -> implementation stays suspended: CSRG-C00, CSRG-C03 and CSRG-C09 and the CodeSpace-owned managed execution -> transport they assume are not implementation directives. Nothing below authorizes implementation. +> **Status: record for the owner's decision; not a design decision.** It approves no architecture, starts no work +> unit, changes no ledger status and authorizes no implementation. CS-RG implementation stays suspended: +> CSRG-C00, CSRG-C03 and CSRG-C09 and the CodeSpace-owned managed execution transport they assume are not +> implementation directives. +> +> Review status: the review record is submitted; candidate contracts are **not** verified; product support is +> **not** established. The detailed, reviewable reasoning is in the +> [hardened candidate analysis](2026-09-27-cs-rg-boundary-revalidation-analysis.md). ## 1. Why this review happened The owner's directive CS-DG-REASSESS-2026-09-27 and the owner's clarifications of the same day placed the -managed-PTY C00 path on hold and asked for a boundary review before any CS-RG implementation. The review label -"CS-RG integration-boundary revalidation" names an owner-directed review; it is not a work unit. - -The owner fixed these inputs: -- CodeSpace's product semantics, responsibility boundaries and selective use of Codex upstream for execution - mechanisms are constraints of the integration, not options to trade away. DevGuard, a later opt-in, adapts. -- CodeSpace constraints (N1) and DevGuard safety invariants (N2) are joint hard constraints with no priority - between them. A combination that cannot meet both is unsupported; neither is weakened. -- Current code is evidence, not a constraint. The CS-RG planning documents (design revision 1, CSRG-C00/C03/C09 - and the matching CodeSpace text) are audit targets, not requirement sources. -- CS-RG alone never justifies moving a Codex-delegated mechanism (notably PTY) into CodeSpace or refactoring - unaffected CodeSpace paths for DevGuard or for symmetry. CodeSpace changes stay a thin adapter. -- The review stops at a decision packet. A normative design PR, new units and any code come only after the - owner's decision. - -## 2. State at a glance (2026-09-27 13:05 UTC) +managed-PTY C00 path on hold and asked for a boundary review before any CS-RG implementation. "CS-RG +integration-boundary revalidation" names that review; it is not a work unit. + +Fixed by the owner (not open for re-decision): +- CodeSpace constraints (N1: product semantics, responsibility boundaries, selective use of Codex upstream for + execution mechanisms, opt-in orthogonality) and DevGuard safety invariants (N2) are joint hard constraints with + no priority between them. A combination that cannot meet both is unsupported; neither is weakened. +- DevGuard is the opt-in that adapts. CS-RG alone never justifies moving a Codex-delegated mechanism (notably PTY) + into CodeSpace or refactoring unaffected CodeSpace paths. CodeSpace changes stay a thin adapter. +- Current code is evidence, not a constraint. The CS-RG planning documents are audit targets. +- The review stops before normative design and implementation. + +## 2. State at a glance (2026-09-27 13:45 UTC) | Item | Value | | --- | --- | | DevGuard `main` | `7e3cbda91308f527d6cc34fba908375e6332bc58` | | CodeSpace `main` | `794867ef52f530be6bc0d91aa10416d5195367b7` | | Codex pin in CodeSpace | `6b9826e3aa83b1a5947db50f4332cb9c65f1b340` (rust-v0.154.0), unchanged | -| Hold notices | [DevGuard #12](https://github.com/novelKR/DevGuard/pull/12) (head `ae85ebb95b68821361ae59d3c60b5344a1d8ab03`) and [CodeSpace #75](https://github.com/novelKR/CodeSpace/pull/75) (head `1bee230595698b0974df43561bccfce67d7e8cb9`): open, all required checks green, waiting for exact-head merge approval | -| Independent test fix | [DevGuard #11](https://github.com/novelKR/DevGuard/pull/11) (head `2bbe7c5ed88ad3170bc76985bdecb1fd7434d501`): reviewed, green, waiting for exact-head merge approval | -| Product code, pins, services, credentials | unchanged | - -Until #12 and #75 merge, the CS-RG documents on `main` still read as directives. No agent implements from them. - -## 3. Findings -Evidence levels: confirmed (read at the fixed revision), inferred, requires experiment. - -| ID | Finding | Source | Level | -| --- | --- | --- | --- | -| F-1 | The priority inversion is documented, not executed. Revision 1 listed CodeSpace's Codex PTY use and the Codex pin as changeable means and did not list DevGuard's helper, direct-child check, descriptor layout or observe-before-reap; the plan then fixed DG-1's consumer interface for every unit | [design-revision-1.md L96-L107](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/design-revision-1.md#L96-L107), [CS-RG.md L22](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/planning/milestones/CS-RG.md#L22) | confirmed | -| F-1a | That premise became the D1 default and the C00, C03 and C09 definitions, and entered CodeSpace's own reuse policy | [design-revision-1.md L479](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/design-revision-1.md#L479), [CS-RG.md L47](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/planning/milestones/CS-RG.md#L47), [L88](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/planning/milestones/CS-RG.md#L88), [L164](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/planning/milestones/CS-RG.md#L164), [CodeSpace codex-reuse.md L72](https://github.com/novelKR/CodeSpace/blob/794867ef52f530be6bc0d91aa10416d5195367b7/docs/codex-reuse.md#L72) | confirmed | -| F-1b | Before CS-RG, CodeSpace documented selective Codex reuse with PTY through `codex-utils-pty` | [codex-reuse.md L7 and L18 at e94d214](https://github.com/novelKR/CodeSpace/blob/e94d21475643608ad2a466256fb57266b86faa47/docs/codex-reuse.md#L7-L18) | confirmed | -| F-2 | No unauthorized execution was found. One lapse is self-reported: a queued read-only subagent delegated under the old instruction was not stopped and wrote a superseded C00 plan after the hold, without repository or product effect | session records | confirmed | -| F-3 | The core mismatch is DevGuard's launch API: `HelperCommand` makes DevGuard's client perform the spawn, so a governed PTY launch would take PTY and spawn ownership from Codex | [launch.rs L150-L154](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/crates/client/src/launch.rs#L150-L154) | confirmed | -| F-4 | Observation before reap is not a safety invariant: if the owner reaps first, unknown survivors become tracking loss and the attempt stays Suspect; it is never released | [contracts.md, Reconciliation](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/docs/contracts.md#reconciliation) | confirmed; frequency requires experiment | -| F-5 | Codex reaps PTY children internally and reports only an exit code at the pin, at rust-v0.157.1 and on main | [pty.rs L244-L253 at the pin](https://github.com/openai/codex/blob/6b9826e3aa83b1a5947db50f4332cb9c65f1b340/codex-rs/utils/pty/src/pty.rs#L244-L253) | confirmed | -| F-6 | Delivering close-on-exec descriptors only to the intended child (`ChildFds::Attached`) exists on Codex main and in prereleases, first in #47797 (2026-09-24); no stable release has it | [pty.rs L405-L408 at main 41f9084](https://github.com/openai/codex/blob/41f9084b30812db321a0b592def4f500d1e79cf4/codex-rs/utils/pty/src/pty.rs#L405-L408) | confirmed from source; behaviour requires experiment | -| F-7 | macOS cannot create pipes or socket pairs close-on-exec atomically; DevGuard's `spawn_guard` covers the window, but CodeSpace's Tokio pipe, patch-helper and probe spawns neither hold it nor close descriptors in the child | [launch.rs L175-L190](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/crates/client/src/launch.rs#L175-L190) | confirmed | -| F-7a | DevGuard's client session socket has the same window outside `spawn_guard` | [connect.rs L39-L51](https://github.com/novelKR/DevGuard/blob/7e3cbda91308f527d6cc34fba908375e6332bc58/crates/client/src/connect.rs#L39-L51) | window confirmed; exposure suspected | -| F-8 | Codex exposes no PID for PTY children at the pin or on main | `SpawnedProcess` in [process.rs L356-L361](https://github.com/openai/codex/blob/6b9826e3aa83b1a5947db50f4332cb9c65f1b340/codex-rs/utils/pty/src/process.rs#L356-L361) | confirmed | -| F-9 | DevGuard declares Rust 1.95 and CodeSpace 1.88, so a DevGuard client dependency in CodeSpace must stay optional for the `off` build | workspace `Cargo.toml` of both repositories | confirmed | -| F-10 | CodeSpace issues outside CS-RG: the tool text promises subtree termination that the code does not perform; a PTY handle dropped 15 minutes after exit signals a stored numeric pgid (Codex behaviour); worker setup failure leaves the worker and its directory | [mcp.rs L124-L125](https://github.com/novelKR/CodeSpace/blob/794867ef52f530be6bc0d91aa10416d5195367b7/crates/server/src/mcp.rs#L124-L125), [process.rs L33](https://github.com/novelKR/CodeSpace/blob/794867ef52f530be6bc0d91aa10416d5195367b7/crates/runner/src/process.rs#L33), [Codex process.rs L273-L277](https://github.com/openai/codex/blob/6b9826e3aa83b1a5947db50f4332cb9c65f1b340/codex-rs/utils/pty/src/process.rs#L273-L277), [runtime.rs L42-L50](https://github.com/novelKR/CodeSpace/blob/794867ef52f530be6bc0d91aa10416d5195367b7/crates/server/src/runtime.rs#L42-L50) | confirmed from code; not run | - -## 4. Gap resolution -| Gap | Conclusion | Change locus | -| --- | --- | --- | -| Helper spawn ownership | DevGuard exposes the helper launch as data (program, argv, attachments or tickets); CodeSpace's existing backends spawn it | DevGuard client; adapter | -| Descriptor delivery, PTY | either Codex `ChildFds::Attached` after a pin update (route Y) or no descriptors at all (route X), which needs a small generic Codex PID accessor | Codex pin or upstream; DevGuard; adapter | -| Descriptor delivery, pipe | no descriptors: the owner confirms the helper's PID, which it already knows for Tokio children | DevGuard wire; adapter | -| Descriptor creation window | removed by the no-descriptor route, or by creating grant descriptors atomically close-on-exec; CodeSpace taking the guard is excluded | DevGuard | -| Observation before reap | accept Suspect for unobserved survivors after measuring it, or let the helper supervise the payload (PTY) | DevGuard helper | -| UDS worker credential | send it over the existing Runner channel, have the worker read an operator-provisioned credential, or leave UDS-mode `required` unsupported | owner decision | -| Lost replies, output contract, macOS parentage, termination | existing paths fit | adapter only | - -Excluded before comparison because they break N1 or N2: CodeSpace allocating the PTY for a DevGuard spawn; -`off` on Codex with `required` on a CodeSpace-owned transport; converging the `off` backends; CodeSpace holding -DevGuard's guard; inheritable descriptors without protection; a permit through the terminal; a CodeSpace-built -`ProcessDriver`; removing the permit without an equal proof; releasing on reap; a supervising helper for pipe -while terminate stays unchanged. - -Surviving candidates (all designed to meet N1 and N2; none verified by experiment): - -| Candidate | What | Upstream dependency | -| --- | --- | --- | -| R | governed pipe first on macOS InProcess: launch as data, owner-confirmed helper identity, transcript through the authority, reap-first accepted with measurement | none | -| X | R's mechanism for PTY too | a generic PTY child PID accessor in Codex, then a release and a pin update | -| Y | PTY with an attached permit file through `ChildFds::Attached` | a stable Codex release containing #47797 and a pin update | - -Current verdicts: every macOS `required` combination is unsupported until the DevGuard changes land; pipe needs -nothing else; PTY also needs X or Y; UDS mode also needs the credential decision; Linux waits for DG-LINUX; -`off` is unchanged. - -## 5. Recommendation (not a decision) -| Topic | Recommendation | Confidence | -| --- | --- | --- | -| Boundary | DevGuard adapts to CodeSpace's existing execution; CodeSpace adds a thin adapter and generic events; Codex is consumed through normal pin updates and, where missing, a small generic upstream API | high that it meets the constraints as designed | -| First step | R | medium; requires experiment | -| PTY | X, keeping one DevGuard mechanism for both transports; Y as fallback; PTY `required` stays unsupported until one lands | medium-low | -| Observation | accept reap-first at first and measure it | medium | -| UDS mode | credential over the existing Runner channel | low-medium | - -## 6. Decisions needed from the owner -1. Exact-head merge approvals for DevGuard #12, CodeSpace #75, DevGuard #11 and this handoff PR. #12, #11 and this - PR share DevGuard's base; merging one moves the others' base, which needs a branch update and a new head - approval unless the approval allows that move. -2. The boundary statement in section 5. -3. The route for governed PTY (R then X, R then Y, or another order). -4. Whether governed pipe terminate may also call DevGuard `Terminate` (only needed for a supervising helper on - pipe). -5. Whether a generic "attach descriptors" input on CodeSpace's Tokio pipe spawn is acceptable (default: no). -6. The UDS worker credential path, or UDS-mode `required` left unsupported. -7. Accept reap-first with measurement, or require the supervising helper. -8. If X: approval to submit the upstream Codex PR (a local draft is allowed without it). -9. Unit identifiers: a proposal to record the hold PRs and the later normative correction as the next - documentation units (DGP-D08, CSP-D05) and to replace the CS-RG unit sequence with new units instead of - redefining CSRG-C00. -10. Whether to fix the client session-socket window (F-7a) as a DevGuard-only change. - -## 7. How to continue -- Start by confirming state: both `main` heads, the Codex pin and the heads of the open PRs above. -- Do not implement, prototype or plan a CodeSpace-owned PTY or process backend for CS-RG. Do not start any CS-RG - unit. Do not open a normative design PR before the owner's decisions. -- Merge only with an explicit approval tied to the exact head: recheck head and base, merge with +| Hold notices | [DevGuard #12](https://github.com/novelKR/DevGuard/pull/12) (head `ae85ebb95b68821361ae59d3c60b5344a1d8ab03`), [CodeSpace #75](https://github.com/novelKR/CodeSpace/pull/75) (head `1bee230595698b0974df43561bccfce67d7e8cb9`) | +| Independent test fix | [DevGuard #11](https://github.com/novelKR/DevGuard/pull/11) (head `2bbe7c5ed88ad3170bc76985bdecb1fd7434d501`) | +| This record | [DevGuard #13](https://github.com/novelKR/DevGuard/pull/13); its head changes with each revision | +| Product code, pins, dependencies, services, credentials | unchanged | + +All four PRs are open and none is merged. Until #12 and #75 merge, the CS-RG documents on `main` still read as +directives; no agent implements from them. + +## 3. Main conclusions (evidence levels in the analysis) +- The priority inversion is documented, not executed: design revision 1 made CodeSpace's Codex use changeable and + DevGuard's mechanisms fixed, and that premise shaped C00/C03/C09 and CodeSpace's reuse text. (source) +- The core mismatch is DevGuard's launch API: `HelperCommand` makes DevGuard's client perform the spawn. (source) +- Reap-first is safe against false release but not operable: when the backend reaps the root before the + authority adopts a surviving descendant, the attempt stays Suspect with sticky tracking loss even after the + descendant exits, and on macOS only a reboot releases it. Existing DevGuard native tests demonstrate this and + passed on macOS CI. Both CodeSpace backends reap at once. (source + test) +- DevGuard's client session socket is inheritable for a short span before it becomes close-on-exec, outside + `spawn_guard`; CodeSpace's spawns neither take that guard nor close descriptors in the child. This affects every + candidate and the current design; exposure is not demonstrated. (source; exposure unresolved) +- Codex offers no pre-reap observation for PTY children in any checked revision; close-on-exec descriptor + attachment exists only on main and in prereleases; no PTY child PID is exposed. (source) +- No proposed route (R, X, Y) is contract-complete. Governed execution is unsupported under the current + constraints. (analysis sections 11-12) + +## 4. What is open +Architecture track (the analysis, section 13, explains each): +1. Direction for the session-socket window: a DevGuard-side reduction plus an independent CodeSpace + descriptor-hygiene proposal, or evidence on exposure first. +2. Direction for reap-first leaks: a bounded experiment of a DevGuard-side group anchor, a generic upstream + reap-control primitive, or a restricted, documented workload class after frequency evidence. +3. Whether to submit generic upstream proposals (drafting is allowed without approval). +4. UDS mode: defer (InProcess first) or choose a credential path now. + +Merge track (separate; each needs the owner's explicit approval of the exact head): #12, CodeSpace #75, #11 and +this record #13. See the analysis, section 13, for heads, bases and CI. + +## 5. How to continue +- Confirm state first: both `main` heads, the Codex pin, and the heads and bases of the open PRs. +- Do not implement, prototype or plan a CodeSpace-owned PTY or process backend for CS-RG. Do not start a CS-RG + unit. Do not open a normative design PR, change the Codex pin or dependencies, or submit upstream PRs before the + owner decides. +- Merging: only with an explicit approval tied to the exact head. Immediately before merging, re-check head and + base (the owner's merge rule). Merging one PR advances `main` but does not change another PR's head; this + project nevertheless re-verifies a PR whose base moved, and an updated head needs a new approval. Merge with `--match-head-commit`, read the post-merge `main` CI once, preserve it, then fast-forward local `main` and clean - up without deleting remote branches. -- After the decisions: the next task is a normative design correction in DevGuard and CodeSpace that records the - approved boundary, the thin adapter contract, the chosen route and the new unit sequence, each merged only with - its own approval. Product implementation needs a further explicit approval. + up without deleting remote branches. A green CI run does not replace review of a changed head. +- Delegated work: carry the instruction revision with each delegated task; when it starts and before its result + is accepted, check that the revision is still current. A status of `not found` is indeterminate, not proof of + completion, cancellation, queueing or disappearance. - Public records carry no local absolute paths. -## 8. Evidence -Raw evidence and the full review records stay outside commits by repository rule; this handoff carries their -conclusions. Location and manifests (SHA-256 of `MANIFEST.json`): +## 6. Evidence +Raw evidence and full review records stay outside commits by repository rule; the analysis document carries the +reviewable reasoning. Locations (manifests list every file with its SHA-256): | Record | Location | Manifest | | --- | --- | --- | -| Review records (requirements, findings, ownership map, upstream matrix, DevGuard decomposition, gap resolution, decision packet, handoff, directive copy, upstream source snapshots) | `/evidence/cs-rg/boundary-revalidation-2026-09-27/` | `MANIFEST.json` in that directory lists every file with its SHA-256; it is regenerated when records are added, so its own hash is not pinned here | +| Review records, directive copy, upstream source snapshots | `/evidence/cs-rg/boundary-revalidation-2026-09-27/` | `MANIFEST.json` there (regenerated as records are added) | | Codex source snapshots (pin, rust-v0.157.1, main `41f9084`) | same, `stage4/` | `b58a308fbbb2243680847c2c350f8d24323513a5128206801cdeb76ad1842cc4` | | Hold PR records | same, `stage1/` | `309b210c1cf87b795797236355dde8ad97ee776cfb2bbaa339e3d618ccd1a2aa` | -| DevGuard #11 local experiments, review and exact-head CI | `/evidence/dg1-delivery/stuck-probe-sample-time/` | `138dc8b513096b80802b6fc54d790dac92eec9475429e2a34b2e9096b68e51a0` (experiments) | -| Superseded C00 materials (pre-investigation, late harness plan) | `/evidence/codespace-delivery/csrg-c00/` | `c1690398c85ff02b1178c51dc6d9613a155171835055f54f49c37e546828ea2b` | - -Not run: behaviour of Codex main's `Attached` path (needs a Codex workspace build the host's memory did not -allow); frequency of reap-first Suspect cases (needs real workloads); a supervising helper prototype (would -precede the decision). The DevGuard #11 reproduction kept its run helper but not each case's invocation; that -limit is stated in the PR. - -## 9. Pitfalls met -- A subagent whose status reads "not found" is queued, not gone. A hold must reach it when it starts. -- Korean counterparts are maintained documents with their own structure; match sections by content, not by - position. -- CodeSpace documentation avoids unexplained work-package numbers; explain a CS-RG item where it is mentioned. -- On macOS every per-grant descriptor created with `pipe()`, `socketpair()` or `socket()` is inheritable until - it is duplicated or flagged; any descriptor design has to account for concurrent forks. +| DevGuard #11 experiments, review and exact-head CI | `/evidence/dg1-delivery/stuck-probe-sample-time/` | `138dc8b513096b80802b6fc54d790dac92eec9475429e2a34b2e9096b68e51a0` (experiments) | +| Superseded C00 materials | `/evidence/codespace-delivery/csrg-c00/` | `c1690398c85ff02b1178c51dc6d9613a155171835055f54f49c37e546828ea2b` | + +Diagnostics: the reap-first sequence is covered by existing DevGuard native tests (analysis section 3). Not run: +Codex main `Attached` behaviour; frequency of reap-first leaks in real workloads; exposure through the session +socket window; a group-anchor or supervising-helper experiment. + +## 7. Pitfalls met +- A delegated run's `not found` status is indeterminate; a hold must reach delayed work before its result is used. +- Korean counterparts are maintained documents with their own structure; match sections by content. +- CodeSpace documentation avoids unexplained work-package numbers. +- On macOS every descriptor created with `pipe()`, `socketpair()` or `socket()` is inheritable until it is + duplicated or flagged; any descriptor design must account for concurrent forks in the same process. +- "Not released" is not "operable": check whether a conservative state can ever be left without a reboot. From 2cdf8487dc65f6e0e646c92fa538cfb6dc7accc4 Mon Sep 17 00:00:00 2001 From: NovelKR <38731466+novelKR@users.noreply.github.com> Date: Mon, 28 Sep 2026 02:17:25 +0900 Subject: [PATCH 4/5] docs(handoff): tighten CS-RG evidence claims --- ...27-cs-rg-boundary-revalidation-analysis.md | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation-analysis.md b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation-analysis.md index 6303d2e..829bf4a 100644 --- a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation-analysis.md +++ b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation-analysis.md @@ -20,7 +20,7 @@ source decides. | ID | Review point | Checked | Verdict | Correction | | --- | --- | --- | --- | --- | | RR-1 | Conclusions ("pipe needs nothing else", "PTY needs only a small precondition") are stronger than the evidence | the earlier handoff text; sections 3-12 below | confirmed | every route is an incomplete candidate; no combination is shown supportable (section 12) | -| RR-2 | Reap-first safety and operability were conflated | contracts.md Observation, Limits, Reconciliation; crates/launch/tests/reconcile.rs L470-L560; crates/macos/src/scope.rs L628; macOS CI run 36311129096 | confirmed and strengthened: the leak is deterministic and has no recovery before reboot on macOS | section 3; RE reclassified | +| RR-2 | Reap-first safety and operability were conflated | contracts.md Observation, Limits, Reconciliation; crates/launch/tests/reconcile.rs L470-L560; crates/macos/src/scope.rs L628; macOS CI run 36311129096 | confirmed and strengthened: when root identity and known-member evidence are both lost before a surviving descendant is adopted, tracking loss is sticky and the checked macOS path has no ordinary recovery before reboot | section 3; RE reclassified | | RR-3 | RB/RC were called required without a protocol | earlier packet; contracts.md Fenced launch helper | confirmed | RB/RC are incomplete candidates; protocol and gaps in sections 4-5 | | RR-4 | Authority-mediated results need defined meaning | contracts.md Transcript, Command-line owner | confirmed | section 6 | | RR-5 | The client socket window gates R; a DevGuard-local mutex is not enough | crates/client/src/connect.rs L39-L51; crates/client/src/launch.rs L29-L36, L150-L154 | confirmed; the window also exists in the current DevGuard design, not only in R | section 7; D6 becomes a prerequisite of every CodeSpace route | @@ -43,11 +43,11 @@ No review point was contradicted by source. One point was strengthened: RR-2 (se | F-1 | Design revision 1 listed CodeSpace's Codex PTY use and pin as changeable and kept DevGuard's helper, direct-child check, descriptor layout and observe-before-reap fixed (design-revision-1.md L96-L107; CS-RG.md L22); this became the D1 default and C00/C03/C09 (L479; CS-RG.md L47, L88, L164) and entered CodeSpace's reuse policy (codex-reuse.md L72) | source | | F-2 | No unauthorized execution. One self-reported lapse: a delegated read-only run whose status was unresolved was not stopped and wrote a superseded C00 plan after the hold; no repository or product effect | source (records) | | F-3 | `HelperCommand` makes DevGuard's client perform the spawn (launch.rs L150-L154), so a governed PTY launch would take PTY and spawn ownership from Codex (N1.2) | source | -| F-4 | Reap-first cannot release a lease falsely, and it leaves a charged Suspect attempt that no later ordinary observation clears; on macOS only a reboot releases it (section 3) | source + test | +| F-4 | Reap-first has a confirmed failure path: if the root has been reaped and no root-PID or known-member evidence remains to establish a surviving descendant's membership, tracking loss becomes sticky; that attempt remains charged after the descendant exits, and the checked macOS implementation has no ordinary recovery before reboot (section 3) | source + test | | F-5 | No Codex revision checked offers pre-reap observation or owner-controlled reap for PTY children (pin pty.rs L244-L253; main pty.rs L447-L449) | source | -| F-6 | `ChildFds::Attached` (main and prereleases, #47797) delivers close-on-exec descriptors only to the intended child (main pty.rs L405-L408, L526); no stable release has it; runtime behaviour untested | source; behaviour: experiment | -| F-7 | macOS cannot create pipes or sockets close-on-exec atomically. DevGuard's grant descriptors are created under `spawn_guard` (launch.rs L175-L190), its session sockets are not (connect.rs L39-L51), and CodeSpace's Tokio pipe, patch-helper and probe spawns neither take the guard nor close descriptors in the child | source | -| F-8 | Codex exposes no PTY child PID at the pin or on main (`SpawnedProcess`, process.rs L356-L361) | source | +| F-6 | `ChildFds::Attached` (main and checked prereleases, #47797) is intended to deliver close-on-exec descriptors only to the intended child (main pty.rs L405-L408, L526); it was absent from the checked stable rust-v0.157.1; runtime behaviour remains untested | source; behaviour: experiment | +| F-7 | DevGuard's current client session socket is created inheritable and only then duplicated close-on-exec, outside `spawn_guard` (connect.rs L39-L51). Grant descriptors are created under `spawn_guard` (launch.rs L175-L190), while the checked CodeSpace Tokio pipe, patch-helper and probe spawns neither take that guard nor close unintended descriptors in the child. General Rust/macOS pipe and socket-pair atomic-close-on-exec behaviour was not established by this review | source; general Rust/macOS behaviour: unverified | +| F-8 | Codex exposes no PTY child PID at the checked pin or checked main (`SpawnedProcess`, process.rs L356-L361); stable rust-v0.157.1 was not separately established for this point | source | | F-9 | Scope establishment accepts a root only while it is alone in its group (contracts.md, Native policy application) | source | | F-10 | DevGuard needs Rust 1.95, CodeSpace declares 1.88; a DevGuard client dependency in CodeSpace must stay optional for the `off` build | source | | F-11 | CodeSpace-only issues, outside CS-RG and not relied on by DevGuard's release rules: tool text promises subtree termination the code does not perform (mcp.rs L124-L125); a PTY handle dropped at eviction, up to 15 minutes after exit, signals a stored numeric pgid (Codex process.rs L273-L277 with CodeSpace process.rs L33); worker setup failure leaves the worker and its directory (runtime.rs L51-L64) | source; runtime not run | @@ -81,8 +81,7 @@ Conclusions: descendant born after the last observation keeps its reservation charged until the host reboots. There is no unconditional release (contracts.md Reclamation evidence) and no native path that resolves prior tracking loss. Repeated occurrences shrink admission capacity although the host is idle. (source, test) -- With both CodeSpace backends reaping at once, the observe-before-reap window is effectively zero, so any - survivor not already adopted by an earlier reconciler pass (1-second cadence) triggers this outcome. (inference) +- With both checked CodeSpace backends reaping promptly, the pre-reap observation window is not under DevGuard's control. A survivor that has not already been adopted can enter the sticky-loss path when, after reap, neither root identity nor a known member can establish its membership. The frequency of that condition in representative workloads is unknown. (source + inference) - Frequency in representative agent workloads: unknown (not measured). The existence of the sequence does not depend on that measurement. @@ -139,7 +138,7 @@ durable authority state; INF = inference. | 18 | helper death before claim | owner observes the exit; `AbandonLaunch`; `NoHelperCreated` | OWN, OS | | 19 | helper death after claim | scope-based settlement | OS, DUR | | 20 | payload exec failure | the helper reports `exec_failed` on its session and exits 126/127 | HLP, OS | -| 21 | payload start after a lost reply | cannot happen: exec follows only a received `may_exec = true` | source rule | +| 21 | payload start after a lost authorization reply to the helper | cannot happen under the current rule: exec follows only a `may_exec = true` response actually received by the helper. This does not resolve owner-facing or result-query response loss, where execution may have occurred while the owner remains uncertain | source rule / candidate distinction | | 22 | owner death | helpers fail the parent check; unclaimed grants become Suspect and are never released before reboot | source; operability gap | | 23 | authority restart | committed attempts Suspect; bound scopes Suspect until reboot | source; operability gap | | 24 | expiry | Prepared expires after 5 s | source | @@ -160,7 +159,7 @@ operability gaps at rows 22, 23 and 26; UDS mode (credential path, section 10). | Meaning of READY | pre-exec boundaries done; authorization only; never evidence that the payload started | source (current rule) | | `exec_failed` | helper message plus exit status 126/127 | candidate | | Recorded but response lost | the helper still exits by its own rule; the owner reads the record by query | candidate | -| Helper disconnect | session EOF after `may_exec` looks like a successful exec and like a helper killed in between; the exit status decides, as today | source (current ambiguity kept) | +| Helper disconnect | session EOF after `may_exec` can look like a successful exec or a helper killed before exec. Exit status is recorded as process outcome but does not, by itself, prove whether payload exec occurred; absent separate durable evidence, execution-start uncertainty remains | source principle / candidate contract needed | | After restart | only durable records survive; READY does not | source / candidate | | Missing message as evidence | never evidence of non-execution | source (N2.2) | | Replay of an old message | bound to attempt and helper identity; refused otherwise | candidate | @@ -245,7 +244,7 @@ identical. Status: unverified candidate; requires a DevGuard contract change and | RE | safe against false release; fails operability for a deterministic pattern | no change needed | reservations unrecoverable before reboot | frequency only | none | none | none | a pre-reap mechanism (RG, RA, or an upstream primitive) or an explicit restriction | | RA | blocked by N1 for pipe; incomplete candidate for PTY | observation before the root disappears | N1.1 semantics | exit, signal, EOF fidelity | DevGuard helper | C04, C05, C06 | none | RE or RG | | RG | unverified candidate | adoption after reap without changing CodeSpace's processes | establishment order, helper-side observation, anchor lifetime, PTY SIGHUP | anchor experiment | DevGuard helper and contract | C04, C05, C06 | none | RE with restriction | -| D6 | prerequisite of every CodeSpace route; defect of the current design | — | no N1-compatible complete macOS fix identified | exposure demonstration | DevGuard client (partial); complete fix needs CodeSpace participation | C01/C02 | none | treat as an accepted risk only by an explicit owner decision after evidence | +| D6 | prerequisite of every CodeSpace route; defect of the current design | — | no complete N1+N2-compatible macOS fix identified in this review | exposure demonstration and protection-boundary analysis | DevGuard client and/or adapter/platform changes | C01/C02 plus affected integration qualification | depends on chosen protection | keep governed combinations unsupported until N2 compliance is established; changing N2 would require a separate owner requirement change, not a route-level risk waiver | No route is contract-complete. Governed execution is unsupported under the current constraints until at least D6 and the operability gap have an answer that holds N1 and N2. @@ -257,11 +256,19 @@ and the operability gap have an answer that holds N1 and N2. | macOS | PTY | InProcess | unsupported; candidates X and Y identified; protocol incomplete; not implemented; not qualified | all of the above plus X (upstream) or Y (atomic creation, stable release) | | macOS | pipe or PTY | UDS | unsupported; as above | plus the worker credential path and worker-death operability | | macOS | any | any, governance `off` | unchanged | none | -| Linux | any | any | unsupported pending DG-LINUX | platform milestone; Codex main routes Linux PTY launches through a setup helper, which may break the direct-child check (inference) | +| Linux | any | any, governance `off` | unchanged by this review | existing CodeSpace support scope applies | +| Linux | any | any, DevGuard-governed / `required` | unsupported pending DG-LINUX and integration validation | platform milestone; checked Codex main routes Linux PTY launches through a setup helper, which may affect the current direct-child mechanism (inference) | -## 13. Decisions that remain -Architecture track (only genuine choices; the fixed principles are not reopened, and no route choice is asked -because no route is complete): +## 13. Decisions that remained at this analysis snapshot + +This section records the open choices as of the 2026-09-27 analysis. It is **not** a living decision list. Later +owner directions are recorded in DevGuard issue #14 and CodeSpace issue #76 and supersede this section where they +differ. In particular, later owner direction explicitly permits DevGuard to evaluate Codex and other external +dependencies behind adapters and a flexible, explicit upstream-pin policy; that permission does not itself approve +a specific dependency revision, product implementation, pin change, upstream submission, or merge. + +Architecture track at the time of this snapshot (fixed principles were not reopened, and no route choice was asked +because no route was complete): 1. D6 direction: pursue a DevGuard-side reduction plus an independent CodeSpace descriptor-hygiene proposal (N1.5), or first gather evidence on exposure under the cooperative trust scope and decide afterwards. 2. Operability direction for reap-first leaks: authorize a bounded RG experiment (DevGuard fixture, isolated From edf5e2f20f88feed55822a078abffc18af5f9a4e Mon Sep 17 00:00:00 2001 From: NovelKR <38731466+novelKR@users.noreply.github.com> Date: Mon, 28 Sep 2026 02:17:39 +0900 Subject: [PATCH 5/5] docs(handoff): mark CS-RG record as a dated snapshot --- .../2026-09-27-cs-rg-boundary-revalidation.md | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md index b5342db..329f722 100644 --- a/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md +++ b/docs/handoff/2026-09-27-cs-rg-boundary-revalidation.md @@ -8,6 +8,10 @@ > Review status: the review record is submitted; candidate contracts are **not** verified; product support is > **not** established. The detailed, reviewable reasoning is in the > [hardened candidate analysis](2026-09-27-cs-rg-boundary-revalidation-analysis.md). +> +> **Dated snapshot:** repository state and open decisions below describe the 2026-09-27 review point. The living +> trackers are DevGuard issue #14 and CodeSpace issue #76; later owner directions and merges there supersede this +> snapshot without turning this document into a normative design. ## 1. Why this review happened The owner's directive CS-DG-REASSESS-2026-09-27 and the owner's clarifications of the same day placed the @@ -41,10 +45,11 @@ directives; no agent implements from them. - The priority inversion is documented, not executed: design revision 1 made CodeSpace's Codex use changeable and DevGuard's mechanisms fixed, and that premise shaped C00/C03/C09 and CodeSpace's reuse text. (source) - The core mismatch is DevGuard's launch API: `HelperCommand` makes DevGuard's client perform the spawn. (source) -- Reap-first is safe against false release but not operable: when the backend reaps the root before the - authority adopts a surviving descendant, the attempt stays Suspect with sticky tracking loss even after the - descendant exits, and on macOS only a reboot releases it. Existing DevGuard native tests demonstrate this and - passed on macOS CI. Both CodeSpace backends reap at once. (source + test) +- Reap-first has a confirmed non-operable path: when the backend reaps the root before a surviving descendant is + adopted and neither root identity nor known-member evidence remains to establish that descendant's membership, + tracking loss becomes sticky. The attempt stays charged after the descendant exits, and the checked macOS path + has no ordinary recovery before reboot. Existing DevGuard native tests demonstrate this path and passed on + macOS CI. Both checked CodeSpace backends reap promptly. (source + test) - DevGuard's client session socket is inheritable for a short span before it becomes close-on-exec, outside `spawn_guard`; CodeSpace's spawns neither take that guard nor close descriptors in the child. This affects every candidate and the current design; exposure is not demonstrated. (source; exposure unresolved) @@ -53,8 +58,13 @@ directives; no agent implements from them. - No proposed route (R, X, Y) is contract-complete. Governed execution is unsupported under the current constraints. (analysis sections 11-12) -## 4. What is open -Architecture track (the analysis, section 13, explains each): +## 4. What was open at this snapshot +The analysis, section 13, explains these choices as they stood on 2026-09-27. Later owner directions are tracked +in DevGuard #14 and CodeSpace #76. In particular, the owner later explicitly allowed DevGuard to evaluate Codex +and other external dependencies behind adapters and a flexible explicit upstream-pin policy; this does not by +itself approve a concrete dependency, pin, implementation, upstream submission or merge. + +Architecture track at this snapshot: 1. Direction for the session-socket window: a DevGuard-side reduction plus an independent CodeSpace descriptor-hygiene proposal, or evidence on exposure first. 2. Direction for reap-first leaks: a bounded experiment of a DevGuard-side group anchor, a generic upstream @@ -100,6 +110,7 @@ socket window; a group-anchor or supervising-helper experiment. - A delegated run's `not found` status is indeterminate; a hold must reach delayed work before its result is used. - Korean counterparts are maintained documents with their own structure; match sections by content. - CodeSpace documentation avoids unexplained work-package numbers. -- On macOS every descriptor created with `pipe()`, `socketpair()` or `socket()` is inheritable until it is - duplicated or flagged; any descriptor design must account for concurrent forks in the same process. +- The reviewed DevGuard client `socket()` path creates an inheritable descriptor and only later duplicates it + close-on-exec. This review did not establish a universal claim for Rust/macOS `pipe()` or `socketpair()` creation; + descriptor designs must distinguish verified creation semantics from assumptions when analyzing concurrent forks. - "Not released" is not "operable": check whether a conservative state can ever be left without a reboot.