fix(container-gateway): make the macOS path work end to end - #1304
Merged
Merged
Conversation
Three defects, found while doctoring a sandbox where `podman` worked by hand but not through the gateway. Backend discovery trusted `podman machine inspect` for the socket path. That command renders the path from the *caller's* TMPDIR, not the machine's, so a hook, agent, or launchd service whose TMPDIR differs is told a path that does not exist and discovery reports no backend at all. Only the basename is stable, so the finders now return candidate lists and `discover` takes the first that exists, with the per-user temp directory from `getconf DARWIN_USER_TEMP_DIR` as the second podman candidate. `_docker_socket` gets the same shape: the context result and the `~/.docker` fallback are now both candidates rather than either/or. `CONTAINER_HOST` / `DOCKER_HOST` were committed in the baseline with a project-relative `unix://./…` value. That can never have worked: a `unix://` URL's authority is parsed as a host component, so `unix://./x` dials `/.//x`, `unix://x` dials `/x/`, and `unix:x` dials `//`. Only `unix:///abs/path` connects, and an absolute path is per-machine, so both variables move out of the committed baseline and join the `allowUnixSockets` pair in the gitignored `.claude/settings.local.json`. The spec had recorded this as a resolved open question in favour of the relative form, concluded from URL parsing rather than a connection; it now records what actually happens. The TMPDIR troubleshooting entry prescribed `env.TMPDIR` in project settings to get a per-project scratch directory. Claude Code sets TMPDIR itself when it builds the sandbox and that assignment wins, so the setting is accepted and silently does nothing. The entry now says so, and the doctor's probe 4 reports a shared session root as a pass instead of sending the operator after a fix that cannot work. Probe 3 gains an explicit check for a non-absolute value, because stripping `unix://` leaves a relative path that stats fine and hides the real cause. Also fixes an unrelated blocker found on the way: the touch overlay polled `lsof -U -n` before the O(1) `signing_in_flight` check, and on a loaded host that enumeration outlasts the signature it is meant to observe, so the overlay never appeared and its test failed one run in three. Asking the cheap question first keeps `lsof` out of the hot loop on the wrapped path entirely. Specs are synced to main: the two pr-management-code-review fixes (#1299, #1300) are recorded in the family contract, the GitHub Copilot harness from #1287 gets copilot-runtime.md, and .last-sync moves to the current tip. Generated-by: Claude Opus 5
21 tasks
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
envblock could never have worked.CONTAINER_HOST/DOCKER_HOSTshipped a project-relativeunix://./…value. Aunix://URL's authority is parsed as a host component, sounix://./xdials/.//x,unix://xdials/x/, andunix:xdials//. Onlyunix:///abs/pathconnects (verified against podman 6.1.0). An absolute path is per-machine, so both variables move out of the committed baseline into the gitignored.claude/settings.local.json, beside theallowUnixSocketspair.TMPDIRdiffered from the machine's.podman machine inspectrenders the socket path from the caller'sTMPDIR, so a hook, agent, or launchd service is told a path that does not exist and discovery reports no backend — on a host wherepodmanworks fine by hand. The finders now return candidate lists anddiscovertakes the first that exists, withgetconf DARWIN_USER_TEMP_DIRas the second podman candidate.TMPDIRtroubleshooting entry prescribed a fix that cannot work. Claude Code setsTMPDIRwhen it builds the sandbox and that assignment wins overenv.TMPDIRfrom any settings file, so the setting is accepted and silently does nothing. Doctor probe 4 now reports a shared session root as a pass instead of sending the operator after it.Found while doctoring a sandbox where
podmanworked by hand but not through the gateway. Each defect alone is enough to break the macOS path end to end.Type of change
.claude/skills/<name>/) — eval fixtures updated belowtools/<system>/*.md)tools/*/withpyproject.toml)docs/,README.md,CONTRIBUTING.md)projects/_template/)prek, workflows, validators)Test plan
prek run --all-filespassesuv run pytest/ruff check/mypypassesSessionStarthook with noTMPDIRoverride and serves both sockets, andpodman inforeaches it from inside the sandbox through the absoluteCONTAINER_HOST.Evidence for the URL claim, gathered against podman 6.1.0:
CONTAINER_HOSTunix://./.apache-magpie-local/run/podman.sock/.//.apache-magpie-local/…unix://.apache-magpie-local/run/podman.sock/.apache-magpie-local//run/…unix:.apache-magpie-local/run/podman.sock//unix:///Users/…/.apache-magpie-local/run/podman.sockRFC-AI-0004 compliance
No sandbox surface widens. The change removes two committed
envvalues and records that the socket allow-list entries were always per-machine. The privacy-LLM tick covers the newcopilot-runtime.md, which records the GitHub-hosted-model boundary rather than changing it.Linked issues
Refs #1298 (container gateway), #1287 (Copilot harness), #1299 and #1300 (the code-review fixes back-filled into the spec).
Notes for reviewers
Adopter migration — this removes committed settings. Anyone who installed the gateway before this PR has
CONTAINER_HOST/DOCKER_HOSTin their committed.claude/settings.json. After upgrading they must move both to.claude/settings.local.jsonwith absolute paths;setup-isolated-setup-verify12c now fails a relative value and quotes it, so the upgrade path surfaces rather than silently leaving a broken gateway.A spec claim is reversed.
specs/container-gateway.mdrecorded both open questions as resolved in favour of the relative form. That conclusion was drawn from how the URL parses, not from a connection. The spec now says so explicitly, because the next person to read it should know the claim was tested and failed rather than never considered.One unrelated fix rode along, because it blocked the commit.
agent-isolation'stest_git_signs_a_commit_through_the_wrapperfailed ~1 run in 3 on pristinemainunder the full suite and passed in isolation. The cause is real and not test-only: the watcher polledlsof -U -n— which enumerates every unix socket on the machine — before the O(1)signing_in_flightcheck, and on a loaded host that enumeration outlasts the signature it is meant to observe, so the touch overlay never appeared. Asking the cheap question first keepslsofout of the hot loop entirely on the wrapped path. 5/5 green after, was 1-in-3 failing. Happy to split it out if you'd rather review it separately.Spec sync.
.last-syncwas 10 commits behind. Most of those commits updated their own specs inline; the two genuine gaps were the pr-management-code-review fixes and the Copilot harness, both now written. The marker moves to the current tip, so the claim it makes is true.Probe 3's new check earns its place. Stripping
unix://leaves a relative path thatstats successfully — the socket really does exist relative to the cwd — so the old probe passed the file check and the real cause disappeared into the generic failure branch. The explicit shape check is what makes the misconfiguration legible.🤖 Generated with Claude Code