Conversation
When PAIR installs Ollama or LM Studio, add that engine's command-line directory to the installing user's persistent PATH on that device, and give back only what PAIR recorded when the engine or the application is removed. Open a new terminal after either operation. Local installs only. An install driven by a cluster peer deliberately skips the PATH step, because rewriting the login shell of whoever is sitting at the target node is not a paired peer's decision. Ownership is recorded outside the engine directory and before the PATH is touched, so a crash mid-update is retryable and an uninstall can delete exactly what PAIR added. Existing entries, user-edited shell snippets, and engines the user installed themselves are preserved. The record also notes that PAIR ran the installer, which is what lets a reinstall re-adopt an engine whose vendor owns its location: LM Studio writes ~/.lmstudio, so the path is no evidence of who put it there. A record that exists but cannot be parsed fails an uninstall rather than reporting a clean one. Windows persists through HKCU\Environment; the other platforms append a self-identifying block to the login shell's profiles. Both run under a cross-process lock that covers the whole read-modify-write, bounded so a hung peer times out instead of wedging the operation, and both publish through a temporary file and an atomic rename. A profile rewrite re-checks the file's fingerprint before renaming, so a concurrent editor's save is refused rather than silently discarded. The platform uninstallers release the entries before deleting the data directory that holds the records, and keep that data when the release fails so a reinstall can finish the cleanup. Two changes are adaptations to this tree rather than part of the feature. runCommand takes a variadic environment for install commands that declare overrides, so the launch closure in lifecycle.go accepts and ignores it; a launch carries its own environment separately. writeJSONAtomic keeps this tree's unique temporary name and gains the file flush and directory flush that the ownership records need, since a rename alone says nothing about bytes reaching stable storage. Signed-off-by: Chris Kelsey <ckelsey@nvidia.com>
ckelseynv
force-pushed
the
feat/engine-install-path-gh
branch
from
September 22, 2026 23:18
a6828dc to
a1b3de5
Compare
kjlubick
self-requested a review
September 23, 2026 13:46
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.
Changelog title
Installed engines are added to your PATH automatically
Changelog body
ollamaorlmsworks in a new terminal without extra setup.Bumps
Summary
When PAIR installs Ollama or LM Studio, the engine manager adds that engine's
command-line directory to the installing user's persistent PATH on that device,
and gives back only what it recorded once the engine or the application is
removed. Open a new terminal after either operation.
Local installs only. An install driven by a cluster peer deliberately skips the
PATH step, because rewriting the login shell of whoever is sitting at the target
node is not a paired peer's decision.
Ownership is the whole design
Nothing else can identify what PAIR put on a user's PATH, so an ownership record
is written outside the engine directory and before the PATH is touched. That
ordering makes a crash mid-update retryable, and it means an uninstall deletes
exactly what PAIR added. Existing entries, user-edited shell snippets, and
engines the user installed themselves are all preserved, because an entry PAIR
cannot prove it wrote is an entry it must not remove.
The record also notes that PAIR ran the installer, which is a longer-lived fact
than any individual PATH entry. It is what lets a reinstall re-adopt an engine
whose vendor owns its location — LM Studio writes
~/.lmstudio, so the pathalone is no evidence of who put it there. A record that exists but cannot be
parsed fails an uninstall rather than reporting a clean one, since the
alternative is claiming the entries were released while stranding them.
Platform behavior
Windows persists through
HKCU\Environment; the other platforms append aself-identifying block to the login shell's profiles, resolving
ZDOTDIRforzsh users. Both run under a cross-process lock that covers the entire
read-modify-write rather than just the write, bounded with a timeout so a hung
peer cannot wedge the operation while the engine's own mutex is held. Both
publish through a temporary file and an atomic rename, with the file and its
directory flushed — a rename is atomic for a concurrent reader but says nothing
about bytes reaching stable storage.
A profile rewrite re-checks the file's size and modification time immediately
before the rename, so a concurrent editor's save is refused as retryable rather
than silently discarded. A symlinked profile is written through to its target
instead of being replaced by a regular file.
The platform uninstallers release the entries before deleting the data directory
that holds the records, and keep that data when the release fails, so a
reinstall can finish the cleanup instead of leaving entries nothing can
identify.
A manifest whose CLI resolves to a relative path is rejected with a warning
rather than anchored to the daemon's working directory, which would publish an
entry that means nothing.
{install_dir}is resolved first, so a manifestwritten against the documented placeholders works.
Adaptations to this tree
Two changes are not part of the feature and exist because this tree has moved
independently. Both are called out so they are reviewed as decisions rather than
skimmed as noise.
runCommandtakes a variadic environment, for install commands that declareoverrides. The launch closure in
lifecycle.gotherefore accepts and ignoresit: a launch carries its own environment separately, and the one call site
passes none, so merging two sources there would be dead code pretending to be
a feature.
writeJSONAtomickeeps this tree's unique temporary filename and gains thefile and directory flush the ownership records need. It states
0600explicitly rather than inheriting it from
os.CreateTemp, becauseTestSettingsOverrideRestrictsExistingPermissionsrequires the write torestrict an already group-readable file, not merely avoid widening one. The
temporary is also named after its target, since the same helper now writes
port overrides, desired state, and PATH records.
Test plan
nvpair-engine-managerGo suite passes on this tree, on Linux aswell as Windows. Running it on Linux was not optional: the permissions test
above skips on Windows, and the first push of this branch failed on it.
TestUninstallTerminatesRunningInstancefails in a container, but it does soidentically on unmodified
develop, so it is an environment artifact ratherthan a regression — it passes on the hosted runners.
go vetclean forlinux,darwin, andwindows;gofmtclean.cross-handle lock exclusion and a bounded wait, a symlinked profile written
through rather than replaced, the concurrent-edit refusal, and a
ZDOTDIRparsing table including a trailing comment after an unquoted value and an
unreadable
.zshenv.pass after: republishing PATH for an engine living outside the install
directory, and reporting failure for an unreadable ownership record.
files here are byte-identical to the versions those checks ran against.
Not covered by automation, and worth native verification before release: real
OS PATH changes, the Windows registry functions (compiled but not executed by
CI), the installer's uninstall branch, and live vendor installations. Tests use
temporary profiles, fake engines, and in-memory Windows PATH values throughout,
so no run can edit a developer's own account.