#1619·graphify

Windows SKILL.md field review (v0.9.5): broken interpreter guard, backslash/encoding corruption, ghost flags — with verified fixes

Author: edtrackaiCreated Jul 3, 2026Updated Sep 16, 2026

Environment: Windows 11 Pro, Claude Code (Git Bash + PowerShell 7 available), graphifyy 0.9.5 installed via uv tool install, skill at ~/.claude/skills/graphify/ (name: graphify-windows).

I did a line-by-line review of the Windows skill template (SKILL.md + references/) and verified the riskiest findings on a real machine. Two failures reproduce today on a stock Windows + uv setup; the rest are latent Windows bugs, doc/spec mismatches, and two ghost flags the skill references but never implements. I've patched all of these locally and validated the fixes — happy to turn this into a PR if you want it.

Findings are grouped by severity. Line numbers refer to the shipped v0.9.5 SKILL.md (712 lines) and reference files.


A. Reproduced on a stock Windows install

A1. Interpreter guard is broken on Windows (SKILL.md:649-661)

The subcommand guard recovers a lost graphify-out/.graphify_python by parsing a shebang:

GRAPHIFY_BIN=$(which graphify 2>/dev/null)
PYTHON=$(head -1 "$GRAPHIFY_BIN" | tr -d '#!')

On Windows the entry point is graphify.exe — a PE32+ binary (verified with file). head -1 reads MZx... garbage, the character-class guard catches it, and the fallback writes python3 — which on a typical Windows box resolves to the Microsoft Store WindowsApps stub, not a real interpreter (verified: which python3.../WindowsApps/python3). Net effect: delete graphify-out/ once and every query / path / explain / --update is broken until a full rebuild, with a confusing Store-stub error.

Fix (validated): don't parse shebangs; probe real interpreters the same way Step 1 does — uv tool dirpipx environment → active env, each gated on "$PY" -c "import graphify" (the import check also filters out the Store stub). Bash version below works on Git Bash and Linux/macOS:

find_graphify_python() {
    if command -v uv >/dev/null 2>&1; then
        UV_DIR=$(uv tool dir 2>/dev/null | tr -d '\r' | tr '\\' '/')
        for PY in "$UV_DIR/graphifyy/Scripts/python.exe" "$UV_DIR/graphifyy/bin/python"; do
            if [ -x "$PY" ] && "$PY" -c "import graphify" 2>/dev/null; then printf '%s' "$PY"; return 0; fi
        done
    fi
    if command -v pipx >/dev/null 2>&1; then
        PIPX_VENVS=$(pipx environment --value PIPX_LOCAL_VENVS 2>/dev/null | tr -d '\r' | tr '\\' '/')
        for PY in "$PIPX_VENVS/graphifyy/Scripts/python.exe" "$PIPX_VENVS/graphifyy/bin/python"; do
            if [ -x "$PY" ] && "$PY" -c "import graphify" 2>/dev/null; then printf '%s' "$PY"; return 0; fi
        done
    fi
    for PY in python3 python; do
        if command -v "$PY" >/dev/null 2>&1 && "$PY" -c "import graphify" 2>/dev/null; then
            "$PY" -c "import sys; print(sys.executable.replace(chr(92), '/'))" | tr -d '\r'
            return 0
        fi
    done
    return 1
}

A2. cp1252 crashes on non-ASCII output; references/query.md lacks encodings

query.md gives Cyrillic examples ("обработчик" → handler) but its Python blocks use bare read_text() / write_text() (lines 34, 43, 86, 203, 271) — unlike SKILL.md's blocks, which consistently pass encoding="utf-8". On Windows the default is cp1252, so the vocab writer crashes on exactly the cross-language corpora the doc describes. Printing non-ASCII node labels through a pipe fails the same way — reproduced:

> python -c "print('обработчик')" | cat
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-9

Fix (validated): prefix interpreter invocations with PYTHONUTF8=1 (covers file I/O and piped stdout in one move), plus add the missing encoding='utf-8' args in query.md for belt-and-braces.


B. Latent Windows bugs (will fire under documented configurations)

B1. Backslash INPUT_PATH corrupts every Python one-liner

Nearly every block says "Replace INPUT_PATH with the actual path" and splices it into a Python string literal (Path('INPUT_PATH'), root='INPUT_PATH'). A natural Windows substitution corrupts silently or loudly:

  • 'C:\temp\proj'\t becomes a TAB — wrong path, no error
  • 'C:\Users\me'\U raises SyntaxError: (unicode error) truncated \UXXXXXXXX escape

There is no warning anywhere in the skill. Fix: one rule near the top — substitute INPUT_PATH with forward slashes (C:/Users/me/project). Also (Resolve-Path INPUT_PATH) at SKILL.md:122 is unquoted and breaks on paths containing spaces (Resolve-Path 'INPUT_PATH').

B2. BOM poisoning under Windows PowerShell 5.1 (SKILL.md:120-122)

Step 1 saves the interpreter path with Out-File -Encoding utf8. Under PS 5.1 — which the skill explicitly supports (the Troubleshooting section is about the 5.1 console) — that writes a UTF-8 BOM, so every later $(cat graphify-out/.graphify_python) produces a \xef\xbb\xbf-prefixed path that fails to exec. Fix (validated): write the file from Python (open(...,'w',encoding='utf-8').write(sys.executable)) — no BOM, no trailing newline, and it also normalizes to forward slashes.

B3. Shell contradiction: bolded instruction says PowerShell, every block is bash (SKILL.md:127)

"In every subsequent block, run Python through the saved interpreter — & (Get-Content graphify-out\.graphify_python) in place of a bare python3"

…but all subsequent blocks are bash ($(cat ...)), where the PS form is a parse error — and the bash blocks' \"-escaping breaks if an agent obediently transcribes them into PowerShell. Step B2's CHUNK_PATH derivation (SKILL.md:287-290) is also PowerShell inside an otherwise-bash step. Fix: declare one shell ("Step 1 is PowerShell; run everything else with bash") or port Step 1 to bash too (my local patch does the latter — the uv/pipx detection from #831 ports cleanly, see A1).

B4. Step 1 has no failure gate

If install fails, $GRAPHIFY_PYTHON stays $null, an empty .graphify_python is written, and every later step degenerates into cryptic -c: command not found errors far from the root cause. Fix: after re-detect, if still empty → print an explicit ERROR with install commands and stop. Related: the pip install graphifyy fallback assumes pip is on PATH — python -m pip install graphifyy is the reliable form on Windows.

B5. Unquoted $(cat graphify-out/.graphify_python) throughout

Interpreter paths containing spaces (e.g. a venv under C:\Users\First Last\...) word-split. Quoting it — "$(cat graphify-out/.graphify_python)" — costs nothing.


C. Spec/doc mismatches

C1. Ghost flag --no-cluster (SKILL.md:162)

The large-flat-corpus gate instructs the agent to suggest --no-cluster, but the flag isn't in Usage and no step implements it. Since cluster() returns {community_id: [node_ids]}, the implementation is two lines in Step 4: communities = {0: list(G.nodes())} with a "Full Corpus" label, skipping Step 5. (Validated locally.)

C2. Ghost flag --force (SKILL.md:452)

The #479 shrink-guard error says "re-run a full build with --force", but --force isn't in Usage and Step 4 never passes it. export.to_json() already accepts force= — it just needs wiring: to_json(..., force=IS_FORCE).

C3. Watch-flag filename mismatch

watch.py writes graphify-out/needs_update (no dot — watch.py:749/894/920), and add-watch.md:50 documents it that way, but Step 9's cleanup removes .needs_update (SKILL.md:609). The dotted file never exists, so the real flag survives cleanup.

C4. update.md: backup instruction placed after the step it must precede

"Before the merge step, save the old graph: cp graphify-out/graph.json graphify-out/.graphify_old.json" appears at update.md:179 — after the merge block (line ~85) and the diff block that consumes the backup. An agent executing top-to-bottom reaches it too late and the post-update diff silently no-ops (the diff block's if old_data: swallows it).

C5. Small ones

  • Frontmatter name: graphify-windows doesn't match the directory/command name graphify (hosts register it as graphify anyway).
  • Usage line 13's comment says the default run produces an "Obsidian vault", but Step 6 makes obsidian opt-in (--obsidian); HTML is the default.
  • Usage documents --html as "(this flag is a no-op)" — probably better dropped.
  • Plain --obsidian (without --obsidian-dir) is implemented in Step 6 but missing from Usage.
  • Chunk file naming is inconsistent: _0N (B2 snippet) vs NN (B3) — worth standardizing on zero-padded two digits.
  • The two "no API key" callouts (SKILL.md:176 and :183) largely duplicate each other and could merge.

D. Suggestion: dispatcher structure for token economy

SKILL.md is 712 lines and all of it loads on every invocation, but the most common flow on an existing graph (query) needs ~30 of them. The references/ pattern is already established (query/update/exports/…); moving the build pipeline (Steps 1–9, ~550 lines) into references/build.md and keeping SKILL.md as a ~145-line dispatcher (Usage, fast path, global execution rules, routing table, interpreter guard) cuts the common case's context cost by ~75-80%. I've run this restructure locally with all of the above folded in and it works well — glad to PR it, either as one change or with section A/B/C fixes split out first.


Verification notes: A1 (PE entry point, Store-stub python3), A2 (UnicodeEncodeError repro + PYTHONUTF8=1 fix), B2 (BOM-free write), and the rewritten interpreter guard were all tested on Windows 11 + Git Bash + uv-installed graphifyy 0.9.5. C1/C2/C3 were verified against the installed package source (cluster() return shape, to_json(force=) signature, watch.py flag name).