Skip to content

fix(storage): resolve a Home behind a symlink the same way before it exists - #210

Merged
ThomasK33 merged 7 commits into
mainfrom
fix/home-symlink-socket
Oct 30, 2026
Merged

ThomasK33 merged 7 commits into
mainfrom
fix/home-symlink-socket

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Why

The first create in a new Home reached through a symlink must succeed, and create must not report a failure for a session that runs. Today that first create exits 6 with HOST_UNREACHABLE, and the session it reports as failed keeps running. The CLI resolves the Home before create makes it and keeps the symlinked path. The host resolves the Home after create made it and gets the real path. So the two hash different /tmp/agent-tty/<hash> folders, and the CLI dials a socket that does not exist.

What changed

  • canonicalHomePath takes the real path of the deepest ancestor of a Home that resolves, then appends the parts below it. It gives the same path before and after the Home exists.
  • resolveHome, the CLI check of --home and AGENT_TTY_HOME, and the registry's normalizeHomePath all use it. So home forget now also finds a removed Home that you name through a symlink.
  • The registry's forget removes the entry stored under the path as written, or else the canonical one, and returns the path it removed. So home forget and gc still remove an entry after an ancestor of its stored path became a symlink, and home forget reports that entry.

Scope

Blast Radius

Every command resolves its Home through this function. A Home that exists resolves to the same real path as before. Only a Home that does not exist yet changes. It now follows symlinks in its existing ancestors. CLI JSON and error codes do not change.

Verification

  • I ran the repro from create: first create in a new home behind a symlink fails with HOST_UNREACHABLE and leaves the session running #206 with a new Home behind a symlink. On main the first create exits 6 and both sessions run. On this branch both create calls exit 0, both sessions run, and destroy leaves no host. A symlinked $HOME with the default Home gives the same before and after.
  • Each fix comes after a commit with tests that fail without it. The first test commit covers create in a new Home behind a symlink, resolveHome before and after the Home exists, and forget through a symlink. The second covers forget, home forget and gc after a stored path's parent became a symlink, and those pass on main too. The third covers a stale entry next to the entry it now resolves to.
  • Every npm run verify step exits 0 locally when the tests run in CI's layout. Under the load of a fully parallel npm run verify, the export-fixtures color-grid screenshot test and the backend-selection semantic snapshot test can time out. Both also fail that way on main. The verify-session-lifecycle check exits 0, because no covered code changed.

Fixes #206


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

The first create in a Home that does not exist yet and sits behind a
symlink exits 6 with HOST_UNREACHABLE, while its session keeps running.
These tests fail on main: the CLI and the host compute different paths
for the same Home, and home forget misses a removed Home by the
symlinked path it was created through.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
…exists

The CLI resolved a new Home before create made it, so realpath failed
and it kept the symlinked path. The host resolved the same Home after
create made it and got the real path. The two hashed different paths for
/tmp/agent-tty/<hash>, so create dialed a socket that did not exist and
exited 6 with HOST_UNREACHABLE while the session ran.

canonicalHomePath resolves the deepest existing ancestor and appends the
parts that do not exist yet. resolveHome, validateHomePath and
normalizeHomePath all use it, so every process derives one Home, one
socket path and one registry key whether or not the Home exists.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T14:33:17.305282Z b6751ec Manual request
🔒 Security Review ✅ Completed 2026-10-30T14:33:44.666641Z b6751ec Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 84f56e10e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 84f56e10e0

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…me a symlink

A Home registered under a path whose parent later became a symlink can
no longer be forgotten: forget and home forget resolve the path through
the new symlink and miss the stored entry, and gc reports the Home
deregistered on every run while homes.json keeps it. These tests pass
on main and fail on 84f56e1.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
normalizeHomePath now follows symlinks in the existing ancestors of a
removed Home. When an ancestor of a stored path became a symlink, the
canonical key no longer equaled the stored entry, so forget, home forget
and gc's deregistration left the entry in homes.json. forget now also
matches the path as written, and home forget passes it the path as
written.

The canonicalHomePath doc now says "deepest ancestor that resolves",
because it also walks past an ancestor that exists but fails realpath.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
Pick up #208 so quality-gates-macos runs the tests on this branch.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 08950300db

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 08950300db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/storage/homeRegistry.ts Resolved
Comment thread src/cli/commands/home/forget.ts Resolved
…e wrong path

When a registry holds a stale entry and the entry it now resolves to,
forget of the stale path removes both. home forget then reports the
canonical path instead of the entry it removed. Both tests fail on
0895030.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
forget matched both the path as written and its canonical form, so with
a stale entry and the entry it now resolves to, it removed both. It now
removes the entry stored under the path as written when there is one,
and else the canonical entry. forget returns the removed path, and home
forget reports that path instead of the canonical one.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: b6751ec61c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: b6751ec61c

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 0cd7c60 Oct 30, 2026
12 of 13 checks passed
@ThomasK33
ThomasK33 deleted the fix/home-symlink-socket branch October 30, 2026 15:06
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.

create: first create in a new home behind a symlink fails with HOST_UNREACHABLE and leaves the session running

1 participant