Repository navigation
Description
Third slice of #43: a sandbox check before each Codex coding run, with the result recorded and passed to the harness.
Problem
Forgecell finds out what a Codex coding sandbox denies only after the model has spent a run hitting it. codex sandbox (Codex CLI ≥ 0.167) runs a command inside the same seatbelt or landlock sandbox with no model call. A manual probe on macOS showed that a workspace-write sandbox denies writing ~/Library/Caches/go-build and denies ps, while writing the workspace works.
Decisions
-
Codex only, coding runs only. Before the approved-plan coding stage starts a native Codex coding run, the Lab runs a fixed probe through the same provider executable:
codex sandbox -c sandbox_mode="workspace-write", the same--add-dir/writable_rootsandshell_environment_policy.setoverrides the coding run will get, and-C <workspace>. The probe is a fixed POSIXsh -cscript, not repository code. Other harnesses get no probe. -
What it checks. Each check is one line of
name=okorname=denied:workspace-write: create and remove a temporary file in the workspace;harness-cache-write: create and remove one in the harnessGOCACHE;spawn: run a child process;ps: runps -p $$;localhost-bind: open a listening TCP socket on 127.0.0.1, only ifpython3orncis on PATH inside the sandbox, otherwiseunknown.
A probe that can't run at all (an old Codex without
sandbox, or a timeout of 20 s) is recorded asunavailablewith the reason. Coding still starts, as today. -
Recorded, then passed on. The result goes on the coding attempt (
sandboxProbe: the Codex version, each check and its state). The coding instruction gets one short sentence naming the denied capabilities, for example "The sandbox denies: ps, localhost-bind. Don't run checks that need them; the Lab runs the approved checks outside the sandbox." If nothing is denied, the instruction is unchanged. -
Fail closed on our own cache. If
harness-cache-writeisdenied, coding doesn't start: the Molecule is blocked with "Harness Go cache is not writable inside the Codex sandbox", because the run would hit it anyway. -
Declared environment skips, for this repository's tests. The coding run's environment gets
FORGECELL_SANDBOX_DENIESset to the denied capabilities, comma-separated (unset when none are denied). Add a small test helper to the Go module, for exampletestenv.SkipIfSandboxDenies(t, "ps"), which skips with an explicit reason when that capability is listed. Use it in the tests that needps(TestIssueQueueCancellationStopsPipeHoldingDescendant) or a local socket (the installer HTTPS lifecycle tests). CI and the Lab's checks never set the variable, so those tests still run there. -
Protocol bump if the probe changes the adapter contract: one past whatever main has when this is planned.
Acceptance
- Unit tests with a fake
codexthat echoes chosen probe lines: the probe result is parsed, recorded on the attempt, and turned into the instruction sentence andFORGECELL_SANDBOX_DENIES. - A fake probe that reports
harness-cache-write=deniedblocks the stage before any coding process starts. - A missing
codex sandboxsubcommand or a timeout recordsunavailableand coding proceeds. - Claude Code and Cursor runs, and Codex analysis and review runs, get no probe and no new variable.
- With
FORGECELL_SANDBOX_DENIES=ps, theps-dependent test skips with a reason that names it; without it, the test runs as before. - No real Codex, model or network in tests.
Answers from the first intake
- Where the coding stage lives:
resumeApprovedIssueinlab/internal/molecule/readiness_run.go. That is where Give Codex coding runs a writable Lab-owned Go build cache (#43, slice 1) #396 attaches the harness cache (resources.HarnessGoCache,harness.WithHarnessCache), creates theHarnessAttemptand blocks the stage withfinishFailed. The probe, the recordedsandboxProbe, the instruction sentence,FORGECELL_SANDBOX_DENIESand the Decision 4 block all go there, next to the cache code. - Local-socket test skips are out of this slice. The installer HTTPS lifecycle check is the Python script
scripts/test-native-installer.py, which a Go helper can't skip. The Go tests that open local sockets are spread throughlab/internal/ui(httptest). The probe still checkslocalhost-bind, and the coding instruction still names it when it's denied, so the harness knows not to run those tests inside the sandbox. Decision 5 now covers onlyps: addSkipIfSandboxDeniesand use it inTestIssueQueueCancellationStopsPipeHoldingDescendant(lab/internal/intake/queue_test.go). Otherps-dependent tests the planner finds may use it too.
Activity
Probe the Codex sandbox before coding and declare environment skips (#…
DanielNetzer commentedon Nov 8, 2026
Fixed in #407.