Repository navigation
Description
Summary
pkgs/mcpx/internal/source/source.go's abs() (line 118-126, main 90f69428) still expands any leading ~ under the caller's own home — the exact bug #1702 pinned for search paths and fixed for searchpath in #2967 (landed 6bee50a8). The source resolver was missed:
func abs(path, dir string) string {
if filepath.IsAbs(path) { return path }
if strings.HasPrefix(path, "~") {
if home, err := os.UserHomeDir(); err == nil {
return filepath.Join(home, strings.TrimPrefix(path, "~"))
}
}
...
return filepath.Join(dir, path)
}So ~alice/secret.ts, ~nosuchuser/x.ts, and ~scratch are silently resolved to $HOME/alice/secret.ts, $HOME/nosuchuser/x.ts, and $HOME/scratch — inventing a home for users who may not exist — instead of being treated as literal relative entries under Dir, which is the rule #1702's triage pinned (only a bare ~ and ~/... name the caller's home) and which the two sibling helpers now implement:
pkgs/mcpx/internal/searchpath/searchpath.go:127—path == "~" || strings.HasPrefix(path, "~/")(fixed in fix(mcpx): searchpath expands only ~ and ~/ — ~name stays a literal relative entry (#1702) #2967)pkgs/mcpx/internal/daemon/routes_dashboard.go:84—dir == "~" || strings.HasPrefix(dir, "~/")
source.abs() is the path resolver for every string-shaped mcpx source setting (launchers, prefixes, error hooks — see the package doc): it is reached via @file:, @dir:, and the bare-value probe path in Resolve().
Evidence (verified this run)
Laptop probe ~/git/fleet/probe-2026-10-31-laneB-sourcetilde/ — the real source.go copied verbatim (md5 ea6a8c5521476daf62b8a0e089104007, matching both clones at main 90f69428), with a test calling the real abs() / Resolve() with HOME set to a temp dir and Dir set to a different temp dir containing a literal ~alice/ directory:
abs("~alice/secret.ts") = "$HOME/alice/secret.ts" want "$DIR/~alice/secret.ts"
abs("~nosuchuser/x.ts") = "$HOME/nosuchuser/x.ts" want "$DIR/~nosuchuser/x.ts"
abs("~scratch") = "$HOME/scratch" want "$DIR/~scratch"
Resolve("@file:~alice/secret.ts") text="FROM_HOME_ALICE" (read $HOME/alice/secret.ts)
want "FROM_LITERAL_TILDE" (the file under $DIR/~alice/)
Controls behaved correctly and are not in the failure list: abs("~/own.ts") = $HOME/own.ts and abs("~") = $HOME.
Why it matters
A source value beginning ~name reads a different file than the one named — a lookalike file under the caller's own home — with no error. For launcher/prefix/hook sources that is silent wrong-code execution potential if such a lookalike exists, and a confusing "file not found / wrong content" failure otherwise. It also makes the three mcpx path helpers disagree: the same ~alice/... string means one thing as a search-path entry and another as a source value.
Acceptance criteria
-
source.abs()expands only a bare~and~/...to the caller's home;~nameentries resolve literally againstDir, matchingsearchpath.absolute()and the dashboard helper. - Regression test in
pkgs/mcpx/internal/source/source_test.gocovering~,~/x,~alice/x,~nosuchuser/x, and~scratch(mirroringTestOnlyBareTildeAndTildeSlashExpandHome), including an end-to-endResolve("@file:~alice/...")case that reads the literal directory. - No other mcpx path helper retains the bare-
HasPrefix(path, "~")form (grep-verifiable).
Dedup / relations
- mcpx(searchpath): a ~name search-path entry is silently resolved under the caller's own home — ~alice/scripts becomes $HOME/alice/scripts #1702 (closed) covered
searchpathonly; its fix fix(mcpx): searchpath expands only ~ and ~/ — ~name stays a literal relative entry (#1702) #2967 touchedsearchpath.goonly. This is the missed sibling helper from the same rule, not a duplicate filing of mcpx(searchpath): a ~name search-path entry is silently resolved under the caller's own home — ~alice/scripts becomes $HOME/alice/scripts #1702. - mcpx(source): a directory source reads non-regular files — a FIFO blocks resolution forever, and a socket or symlink-to-directory aborts it #1727 (open,
mcpx(source)) is about non-regular files in directory sources — different defect, same package.
Activity
dezren39 commentedon Oct 31, 2026
🤖 Agent note · filed
new-issue-creator-code-b)new-issue-creator-code-b, run 2026-10-31T07:50Zsource.goabs()(md5-matched to main90f69428) resolves~alice/secret.ts,~nosuchuser/x.ts, and~scratchunder the caller's$HOMEinstead of literally underDir, and thatResolve("@file:~alice/secret.ts")reads the$HOME/alicelookalike file. This is the sibling helper missed by the mcpx(searchpath): a ~name search-path entry is silently resolved under the caller's own home — ~alice/scripts becomes $HOME/alice/scripts #1702 / fix(mcpx): searchpath expands only ~ and ~/ — ~name stays a literal relative entry (#1702) #2967 searchpath fix; the pinned rule (only bare~and~/...expand) is already implemented insearchpath.go:127androutes_dashboard.go:84.~/git/fleet/probe-2026-10-31-laneB-sourcetilde/(laptop); evidence body in the goal hidden_files (issue-code-mcpx-source-tilde-body.md)2 remaining items
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
fix(mcpx): source abs() expands home only for bare ~ and ~/ (#2988) (#…
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done
alice case); gofmt clean; grep confirms no bare HasPrefix(path, "") form remains in pkgs/mcpx; Codex review clean at 0fc4e7c.