aterm bundles bakes session-shadow paths into every launcher PATH when regenerated from a native session #1323

Open
opened 2026-08-27 08:31:05 +00:00 by coilyco-ops · 0 comments
Owner

Symptom

Running aterm bundles from inside a native AOS session writes launchers whose baked fallback PATH contains the session shadow root. A dry-run from such a session produced this entry in every one of the 7 role launchers:

<session shadow home>/.local/libexec/agent-git-attribution

The shadow root is a temporary directory. It is exposed to temporary-root purges by design, so a persistent app launcher that points at it is stale the moment that session ends. It also leaked unrelated session-local entries such as a Homebrew Cellar Go libexec/bin path and the kitty Contents/MacOS directory.

This matters more than an ordinary dead PATH entry because the baked list is the fallback the wrapper uses precisely when the login shell lookup fails, which is the case where correctness counts.

The gap

livePathEntries in aterm/bundle.go:318 is the filter, and it drops only three things:

  • empty entries
  • duplicates
  • entries that fail os.Stat or are not directories

A session-shadow path exists at generation time, so os.Stat passes and it is baked in. Called from aterm/bundle.go:244 as livePathEntries(os.Getenv("PATH")), so it takes whatever PATH the generating process happened to carry.

The generator has no notion that some existing directories are session-scoped rather than durable.

Repro

  1. Launch a role app so a native session shadow exists
  2. From inside that window, run aterm bundles --dry-run --json
  3. Read the baked= assignment in any item's launcher
  4. Observe the session shadow root in the list

Suggested fix

AOS_NATIVE_SESSION and AOS_NATIVE_SESSION_PROJECTS are set exactly when a shadow exists, so the generator can read them rather than guess. Have livePathEntries additionally drop any entry rooted under the resolved session shadow, and consider dropping anything under the resolved temporary root for the same reason.

Worth a test alongside aterm/bundle_test.go:31, which currently asserts only that the generation-time PATH is baked in as the fallback. That assertion is what makes the current behavior look intentional.

Secondary observation, not part of this issue

The generated launchers bake AOS_BIN to a resolved path that may be the ~/.local/bin/aos script rather than the Homebrew binary, depending on the generating shell's PATH order. Homebrew warns about the same shadow on upgrade. Flagging it only so whoever picks this up knows the resolution is PATH-order sensitive.

Workaround in use

Regenerating from a clean login environment produces a correct baked PATH:

arch -arm64 /usr/bin/env -i HOME=<home> USER=<user> TERM=xterm-256color /bin/zsh -lc \
  'aterm bundles --output-dir <apps dir>'

All 7 bundles now carry zero session-shadow entries.

Filed by Vera (sysadmin). aterm is shared tooling other seats build on, so the build hands over to Agentic Platform Engineer under boundary-build-foundational-software. Related to the LaunchServices staleness in the sibling issue, since both are gaps in what aterm bundles does after it writes files.

## Symptom Running `aterm bundles` from inside a native AOS session writes launchers whose baked fallback PATH contains the session shadow root. A dry-run from such a session produced this entry in every one of the 7 role launchers: ``` <session shadow home>/.local/libexec/agent-git-attribution ``` The shadow root is a temporary directory. It is exposed to temporary-root purges by design, so a persistent app launcher that points at it is stale the moment that session ends. It also leaked unrelated session-local entries such as a Homebrew Cellar Go `libexec/bin` path and the kitty `Contents/MacOS` directory. This matters more than an ordinary dead PATH entry because the baked list is the fallback the wrapper uses precisely when the login shell lookup fails, which is the case where correctness counts. ## The gap `livePathEntries` in `aterm/bundle.go:318` is the filter, and it drops only three things: * empty entries * duplicates * entries that fail `os.Stat` or are not directories A session-shadow path exists at generation time, so `os.Stat` passes and it is baked in. Called from `aterm/bundle.go:244` as `livePathEntries(os.Getenv("PATH"))`, so it takes whatever PATH the generating process happened to carry. The generator has no notion that some existing directories are session-scoped rather than durable. ## Repro 1. Launch a role app so a native session shadow exists 2. From inside that window, run `aterm bundles --dry-run --json` 3. Read the `baked=` assignment in any item's `launcher` 4. Observe the session shadow root in the list ## Suggested fix `AOS_NATIVE_SESSION` and `AOS_NATIVE_SESSION_PROJECTS` are set exactly when a shadow exists, so the generator can read them rather than guess. Have `livePathEntries` additionally drop any entry rooted under the resolved session shadow, and consider dropping anything under the resolved temporary root for the same reason. Worth a test alongside `aterm/bundle_test.go:31`, which currently asserts only that the generation-time PATH is baked in as the fallback. That assertion is what makes the current behavior look intentional. ## Secondary observation, not part of this issue The generated launchers bake `AOS_BIN` to a resolved path that may be the `~/.local/bin/aos` script rather than the Homebrew binary, depending on the generating shell's PATH order. Homebrew warns about the same shadow on upgrade. Flagging it only so whoever picks this up knows the resolution is PATH-order sensitive. ## Workaround in use Regenerating from a clean login environment produces a correct baked PATH: ``` arch -arm64 /usr/bin/env -i HOME=<home> USER=<user> TERM=xterm-256color /bin/zsh -lc \ 'aterm bundles --output-dir <apps dir>' ``` All 7 bundles now carry zero session-shadow entries. Filed by Vera (sysadmin). `aterm` is shared tooling other seats build on, so the build hands over to Agentic Platform Engineer under `boundary-build-foundational-software`. Related to the LaunchServices staleness in the sibling issue, since both are gaps in what `aterm bundles` does after it writes files.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os#1323
No description provided.