Skip to content

fix(codex): add git-worktree gitdir to sandbox writable_roots#36

Merged
cskwork merged 1 commit into
mainfrom
fix/codex-sandbox-worktree-gitdir
May 17, 2026
Merged

fix(codex): add git-worktree gitdir to sandbox writable_roots#36
cskwork merged 1 commit into
mainfrom
fix/codex-sandbox-worktree-gitdir

Conversation

@cskwork
Copy link
Copy Markdown
Owner

@cskwork cskwork commented May 17, 2026

Summary

Codex workspace-write sandbox was blocking Learn→Done merge gate writes when the workspace is attached as a git worktree (the default Symphony hook). Auto-injects the worktree's gitdir path into sandbox_workspace_write.writable_roots.

Symptom

Live observation on SMA-25 (codex worker): worker reached Learn after 5 normal phase transitions, then merge gate died with:

fatal: unable to write .git/worktrees/SMA-25/index.lock
error: unable to create temporary file: Operation not permitted
fatal: failure to merge

Worker honestly self-reported "Ticket is now blocked with the exact gate failure captured" → state: Blocked. No fake success.

Root cause

_scan_workspace_symlinks only collected resolved targets of top-level symlinks (kanban/, etc.). It missed the workspace's .git pointer file (gitdir: <abspath>), so the host repo's .git/worktrees/<ID>/ admin dir was never added to writable_roots even though every git invocation inside the worktree writes there.

Fix

_scan_workspace_symlinks now also parses .git pointer files and unions their gitdir targets into the result. Same helper signature, every call site unchanged.

Defensive cases:

  • .git is a directory (normal repo, not a worktree) → skipped
  • .git file present but no gitdir: line → silently no-op (no raise)
  • Multiple workspaces share a host repo → set-union dedupes

Verified

  • pytest tests/test_backends.py -q → 72 passed (3 new regression tests + 69 existing).
  • Manual: with the fix, codex's git merge-tree --write-tree main symphony/<ID> no longer raises Operation not permitted against .git/worktrees/<ID>/objects/.

Test plan

  • CI green
  • Live re-run of a codex-routed ticket reaches Done (or Blocked for a legitimate reason, not sandbox)

Default Symphony hooks attach the workspace as a git worktree of the host
repo, which stores the worktree's admin state under
`<host_repo>/.git/worktrees/<ID>/`. The workspace's `.git` is a one-line
pointer file (`gitdir: <abspath>`), not a directory. Every `git`
invocation inside the worktree writes `index.lock`, `HEAD.lock`, refs,
etc. under that admin dir, and the Learn→Done merge gate's
`git merge-tree --write-tree` writes temp files there too.

Without that path in `sandbox_workspace_write.writable_roots`, codex
0.130 (workspace-write mode) refuses the writes and the gate fails with:

  fatal: unable to write .git/worktrees/<ID>/index.lock
  error: unable to create temporary file: Operation not permitted
  fatal: failure to merge

Symphony then surfaces the worker as `Blocked` with no obvious cause
visible in the agent log — observed live on SMA-25 (codex worker), where
the agent honestly reported "Ticket is now blocked with the exact gate
failure captured" rather than overclaiming Done.

Fix: `_scan_workspace_symlinks` now also reads the workspace `.git`
pointer file and contributes the parsed gitdir target to the
auto-injected writable_roots list. Symlink scan + worktree pointer scan
are union'd; the existing helper signature stays the same so every call
site (`_prepare_command_and_env`, tests) is unchanged.

Defensive: a `.git` *directory* (normal repo) is skipped, and a garbled
`.git` file without a `gitdir:` line is treated as "no extra writable
root" (no raise). Three regression tests cover all three branches.
@cskwork cskwork merged commit f1a5307 into main May 17, 2026
0 of 3 checks passed
@cskwork cskwork deleted the fix/codex-sandbox-worktree-gitdir branch May 17, 2026 10:01
cskwork added a commit that referenced this pull request May 17, 2026
SMA-25 (Verify autocommitExclude mechanism from PR #23) ran on codex
and was self-Blocked at Learn when the merge gate failed against
.git/worktrees/<ID>/ — the codex sandbox refused writes through the
worktree's git admin dir. That sandbox gap is fixed separately in PR #36.

The agent reached Learn with substantive artefacts though, and they are
worth keeping independently of the merge-gate failure. This recovers
only the high-signal files from `symphony/SMA-25` (commit 9c964fe) and
leaves out the per-turn status echoes and raw pytest JSON/diff runs
that were progress noise rather than reference material.

Recovered:
- docs/SMA-25/{explore,plan,work,qa,learn}/* — phase deliverables
  (Explore notes + reuse inventory, implementation plan, work + qa-rewind
  summaries, QA api-surface + details, Learn details).
- docs/features/SMA-25/index.md — As-Is/To-Be one-pager.
- docs/llm-wiki/workspace-auto-commit-excludes.md — new wiki entry
  covering the opt-in `symphony.autocommitExclude` mechanism, including
  the base-squash safety case Explore surfaced beyond the original brief.
- docs/llm-wiki/INDEX.md — one new row for the wiki entry (kept the
  existing SMA-24 `orchestrator-phase-transition` row, which the
  SMA-25 branch had silently dropped because it was forked before SMA-24
  merged into main).

Not recovered (deliberately):
- docs/SMA-25/todo/turn-*-status.md + blocker.md (stale-worker echo logs
  from the pre-restart cycle).
- docs/SMA-25/qa/runs/*.json + qa/diff/*.diff (raw pytest output, large
  and reproducible from `pytest` directly).
- src/symphony/*.py / tests/*.py / pyproject.toml / __init__.py changes
  on the SMA-25 branch — those were forked before PR #19/#21/#22/#23/#34/
  #36 landed and would revert main.
@cskwork cskwork mentioned this pull request May 17, 2026
2 tasks
cskwork added a commit that referenced this pull request May 17, 2026
Patch release rolling up two post-0.6.1 reliability fixes:

- #36 codex sandbox writable_roots now auto-includes the git-worktree
       gitdir (`.git/worktrees/<ID>/`). Without this, codex 0.130 with
       workspace-write sandbox refused index.lock / merge-tree writes,
       causing Learn-stage Symphony tickets to surface as "Blocked" with
       no obvious cause. Surfaced live on SMA-25.

- #37 (docs-only) recovered the high-signal codex artefacts from the
       blocked SMA-25 branch — Explore through Learn deliverables plus
       a new `workspace-auto-commit-excludes` wiki entry. Included here
       only because the release notes should point at the wiki entry as
       canonical reference for the autocommitExclude opt-in.

All changes are bug fixes restoring intended behavior — no user-facing
feature additions or signature changes. Pinning pyproject.toml and
src/symphony/__init__.py in lockstep.
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