Skip to content

fix(pstack): escalate project-skill writes under Codex's read-only .agents - #31

Merged
mstephenholl merged 1 commit into
mainfrom
fix/codex-sandbox-agents-dir
Oct 20, 2026
Merged

mstephenholl merged 1 commit into
mainfrom
fix/codex-sandbox-agents-dir

Conversation

@mstephenholl

Copy link
Copy Markdown
Owner

Why

The first Codex smoke run on a fresh Codex 0.163.0 install passed 8 of 10 scenarios. The two failures came from gaps in the port, not from upstream's skills:

  • create-verification-skill wrote its skill where Codex can't find it. Codex's workspace-write sandbox keeps a repo's .agents/ and .codex/ read-only, while .claude/ and .cursor/ stay writable. mkdir -p .agents/skills/verify-users failed with Read-only file system. The agent fell back to upstream's .cursor/skills/verify-users and linked .claude/skills to it, and Codex never reads that folder. A real Codex session hits the same wall, because pstack-harness says where project skills go but not that Codex blocks writing there.
  • why skipped its synthesizer. The task said "run only the source control investigator". The agent read "only" as covering Step 4 too, and wrote the report itself.

Two more problems in the smoke test turned up along the way:

  • smoke.py ran codex exec -s workspace-write with no way to answer an escalation request. Escalation only worked if the user's config.toml set an approvals reviewer, which the port README assumes, and a fresh install doesn't. Foreign seats need escalation too, for claude -p.
  • Every codex exec into a repo adds a [projects."<repo>"] trust_level = "trusted" entry to ~/.codex/config.toml. Deleting a run left its entry behind, and two runs left 21 of them.

What changes

  • pstack-harness: a new path rule for Codex. Run shell commands that write under a repo's .agents/, ~/.agents/, or ~/.claude/ with sandbox_permissions: "require_escalated" and a justification from the first attempt. If the write is denied, stop and tell the user. Never fall back to a folder like .cursor/skills/. foreign seats already has the same escalate-first rule for claude.
  • smoke.py:
    • run_codex passes --approve-for-me in place of -s workspace-write. That flag implies workspace-write, and codex exec refuses to combine them.
    • The why task now says to still run the synthesizer.
    • A new forget_trust() removes this run's trust entries when the run ends. It drops only [projects."…"] tables under the run's output directory, and keeps the file mode.
  • port/README.md: the smoke-test section documents both changes.

Risk

  • With --approve-for-me, Codex's automatic reviewer decides on escalation requests during smoke runs instead of failing them. That matches what a person approving prompts in a session does. The foreign_seat check already accepts a seat the reviewer denied, as long as the run reports it.
  • forget_trust edits ~/.codex/config.toml while no codex process from the run is still going. It rewrites the file by replacing it with a temp file and keeps the mode. A config with no matching entries is left untouched.

Proof

  • I probed the sandbox in a fresh repo:

    • Without escalation, .agents and .codex can't be written, and .claude and .cursor can.
    • With --approve-for-me, an escalated mkdir -p .agents/skills/probe and an apply_patch add under .agents/skills/ both succeed.
  • I reran smoke.py --harness codex --only why,create-verification-skill with this branch's harness line, and both passed:

    • create-verification-skill: 2 checks, with the skill in .agents/skills/ and linked from .claude/skills/.
    • why: 5 checks, with the synthesizer spawned on gpt-6-astra.

    After that run, config.toml held no trust entries from it.

  • New unit tests:

    • run_codex passes --approve-for-me and no sandbox flag.
    • forget_trust removes only the run's entries, keeps a sibling directory whose name shares the prefix, keeps the mode, and leaves a missing config alone.
  • Each new test fails when its fix is undone: putting -s workspace-write back, or dropping the separator from the prefix check.

  • All 257 unit tests pass, and check.py reports 0 problems.

…gents

Codex's workspace-write sandbox keeps a repo's .agents/ read-only, so a
skill that writes a project-local skill there failed its mkdir, and the
agent fell back to upstream's .cursor/skills/, where Codex never looks.
pstack-harness now says to run those writes with require_escalated from
the first attempt, and to stop rather than write elsewhere when denied.

The smoke test could not exercise that, because codex exec had no way to
answer an escalation request unless the user's config.toml happened to set
an approvals reviewer. Codex runs now pass --approve-for-me. The why
scenario's task now says the synthesizer still runs, since "run only the
source control investigator" read as skipping it. The test also removes
the trust entries each codex exec adds to ~/.codex/config.toml.
@mstephenholl
mstephenholl merged commit 9ee1e4e into main Oct 20, 2026
9 checks passed
@mstephenholl
mstephenholl deleted the fix/codex-sandbox-agents-dir branch October 20, 2026 23:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant