fix(codex): add git-worktree gitdir to sandbox writable_roots#36
Merged
Conversation
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.
2 tasks
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
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Codex
workspace-writesandbox 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 intosandbox_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:
Worker honestly self-reported "Ticket is now blocked with the exact gate failure captured" →
state: Blocked. No fake success.Root cause
_scan_workspace_symlinksonly collected resolved targets of top-level symlinks (kanban/, etc.). It missed the workspace's.gitpointer file (gitdir: <abspath>), so the host repo's.git/worktrees/<ID>/admin dir was never added to writable_roots even though everygitinvocation inside the worktree writes there.Fix
_scan_workspace_symlinksnow also parses.gitpointer files and unions their gitdir targets into the result. Same helper signature, every call site unchanged.Defensive cases:
.gitis a directory (normal repo, not a worktree) → skipped.gitfile present but nogitdir:line → silently no-op (no raise)Verified
pytest tests/test_backends.py -q→ 72 passed (3 new regression tests + 69 existing).git merge-tree --write-tree main symphony/<ID>no longer raisesOperation not permittedagainst.git/worktrees/<ID>/objects/.Test plan