Skip to content

Windows + uv: Step 1 pins an ephemeral uv cache interpreter, and the subcommand guard still parses graphify.exe as a shebang #3943

Description

@jnrod03-rgb

Two interpreter-resolution defects in the SKILL.md installed by
graphify install --platform claude. Both are still present in v0.9.72
(graphify/skill.md at that tag: Step 1 lines 73 and 89 for the uv half,
line 673 for the guard; the only change to this file since v0.9.61 is the
.graphify_root write).

Related: #1619 (A1 reports the guard half; its PR #1962 was closed without
merging), #2183 (same guard, pipx -E shebang), #513 (same head -1 on
graphify.exe, but in the post-commit hook, closed). The uv-cache half below
does not appear in any of them.

1. Step 1's uv branch resolves a throwaway cache environment

Step 1 tries uv first:

_UV_PY=$(uv tool run --from graphifyy python -c "import sys; print(sys.executable)" 2>/dev/null)

uv tool run does not run inside the environment created by
uv tool install graphifyy. It provisions a separate, cached environment and
reports that one. On Windows 11 with graphify installed via
uv tool install graphifyy:

$ uv tool run --from graphifyy python -c "import sys; print(sys.executable)"
Downloading graphifyy (1.4MiB)
Installed 30 packages in 1.84s
%LOCALAPPDATA%\uv\cache\archive-v0\<hash>\Scripts\python.exe

$ uv tool dir
%APPDATA%\uv\tools

The persistent interpreter the CLI actually runs from is
%APPDATA%\uv\tools\graphifyy\Scripts\python.exe. The cache path is written to
graphify-out/.graphify_python and every later step is pinned to it. Two
consequences:

  • The cache entry can be evicted (uv cache clean, uv cache prune), and the
    sidecar then points at nothing.
  • It can be a different graphify version than the installed CLI: in the run
    above it downloaded the latest release while the installed tool was older. The
    skill steps and the graphify CLI then silently disagree on versions.

The import graphify check does not catch either case, because the cache
interpreter imports fine on the day it is resolved.

2. The subcommand guard parses a PE binary as a shebang

"Interpreter guard for subcommands" still does:

PYTHON=$(head -1 "$GRAPHIFY_BIN" | tr -d '#!')
case "$PYTHON" in *[!a-zA-Z0-9/_.@-]*) PYTHON="python3" ;; esac

On Windows the launcher is a console-script .exe (first two bytes MZ). In my
run (Claude Code, Git Bash) this produced
command substitution: ignored null byte in input and exit 127: the binary
bytes were executed as a command. When the allowlist does catch it, the
fallback is python3, which on Windows is often the Microsoft Store stub
(as #1619 describes). Either way nothing validates the result before it is
written to .graphify_python. Step 1 does better here: its shebang branch
gates the candidate on "$_SHEBANG" -c "import graphify" before accepting it.
The guard is the same parse without the gate, so the two blocks already
disagree.

Expected

A resolved interpreter should be the durable environment the CLI runs from, and
it should be proven to import graphify before it is persisted.

Proposal (both blocks)

  1. Resolve the persistent tool environment directly, before any run-once
    command:
    "$(uv tool dir)/graphifyy/Scripts/python.exe" (Windows) or
    "$(uv tool dir)/graphifyy/bin/python" (POSIX); likewise
    pipx environment --value PIPX_LOCAL_VENVS for pipx.
  2. Only parse a shebang when the launcher is a text script: skip it if the path
    ends in .exe or the first two bytes are MZ.
  3. Reject any candidate whose path contains a uv cache segment
    (/cache/archive-v0/), and drop uv tool run --from graphifyy as a source
    of the interpreter path.
  4. Gate every candidate on "$PY" -c "import graphify" and apply the same
    resolution function in Step 1 and in the guard, so the two cannot drift again
    (SKILL.md subcommand interpreter guard writes a graphify-less Python (pipx shebang carries -E); Step 1's hardening never applied #2183 shows they already have).

The find_graphify_python function in #1619 covers points 1, 2 and 4; point 3
is the new part.

Environment: observed on graphifyy 0.9.55 installed with uv tool install,
skill installed with graphify install --platform claude, Claude Code on
Windows 11 (Git Bash); now on 0.9.65. Upstream text checked against v0.9.72.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions