fix(settings): write settings.json through a symlink instead of over it #1250
No reviewers
Labels
No labels
burndown-2026-06
burndown-2026-08
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/advocate
role/director
role/exec
role/frontend
role/gamedev
role/human
role/platform
role/qa
role/science
role/sysadmin
state
ambient
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/agentic-os!1250
Loading…
Reference in a new issue
No description provided.
Delete branch "aos/claude/sy55"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #1248, with a corrected diagnosis. The issue blamed
Path.home()resolving to the session home. That is true but not the cause, and the fix it proposed (refuse to run inside a shadow) would have been wrong.Actual mechanism
A staged shadow home makes every entry under
.claudea symlink back to the host file, so~/.claude/settings.jsoninside a session already pointed at/Users/kai/.claude/settings.json. The run should have converged the host.Both writers ended in
os.replace(tmp, path).os.replaceswaps the link itself rather than writing through it, so the run severed the symlink, left a real file in the throwaway session home, and printed success while the host kept the settings the run was meant to change.Reproduced against the pre-fix function:
Fix
Resolve the path before the atomic write.
docs/native-shadow.mdalready states the contract this restores: an entry that already exists resolves to the host through its symlink, and only a file the session newly creates stays in the session. The writers now also return and report the path that actually took the write, so a caller sees which home it landed in rather than which one it aimed at.No shadow detection and no new flag. With write-through the hand run does the right thing from inside a session, which is what made the refuse-by-default shape unnecessary.
scripts/install-session-name.pycarried the identical bug against the same file and is fixed the same way. Both scripts stay standalone and stdlib-only for the ansible role, so the fix is duplicated rather than factored out, matching howload_settingsandwrite_settingsare already duplicated across the pair.Verification
Four new tests, two per writer: a symlinked path stays a symlink and the target takes the content, and a plain path still gets a plain file. Both fail against the pre-fix code. Full suite at 723 passed, full pre-commit suite green.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com