install.sh: Windows has no supported dependency-lock path, and --no-deps can't update an install with an older manifest format
Summary
On Windows, install.sh can never produce managed-runtime-receipt.json, so scripts/verify_managed_install.py will always report the install as unmanaged/failed — even on a freshly downloaded, checksum-verified v2.0.1 release archive installed with the documented --target=claude flow.
Root cause 1 — Windows is excluded from the supported dependency-lock matrix
In install.sh, the platform gate is:
boundary_ok=(system=="linux" and libc_name=="glibc" and pair(libc_version)>=(2,17)) or (system=="darwin" and pair(mac_version)>=(11,0))boundary_ok is only ever true for Linux (glibc) or macOS. On Windows it's always False, so PYTHON_TARGET always ends in unsupported, and the case statement falls through to:
✗ No verified dependency lock target for cpython|3.10|windows|amd64|none|none|unsupported. Re-run with --no-deps; moving-range fallback is disabled.This happens regardless of Python version (tested cpython 3.10 on Windows amd64) — Windows is categorically excluded from ever reaching the case statement's per-target branches.
However, the release archive ships dependency-evidence files for Windows targets that are never reachable:
control-plane/dependency-evidence/runtime-windows-cp311.json
control-plane/dependency-evidence/runtime-windows-cp312.json
control-plane/dependency-evidence/development-windows-cp311.json
control-plane/dependency-evidence/development-windows-cp312.jsonSince boundary_ok never evaluates true on Windows, none of the case branches that would map to a runtime-windows-cp31x DEPENDENCY_TARGET_ID can ever be hit — these evidence files appear to be dead weight, or the Windows case branches were dropped from the case statement by mistake.
Root cause 2 — --no-deps also can't update an existing install with an older manifest format
Re-running with --no-deps to at least refresh skill files (skipping the Python venv step) fails differently:
✗ Refusing to overwrite unowned file: ~/.claude/skills/ads/SKILL.mdThe existing install's ownership manifest (~/.claude/skills/.claude-ads-claude.manifest.json) is a JSON object ({"version":1,"target":"claude","installedVersion":"2.0.1","files":[...],"directories":[...],...}), but the current install.sh's previously_owned_file() reads a different, tab-separated line format (V\t1, T\t<target>, F\t<path>, D\t<path>) written to a manifest path via MANIFEST_TMP. Since the two formats don't match, every existing file is treated as unowned, and the installer safely refuses to touch it — which is correct defensive behavior, but it means an install performed by an earlier installer version can never be updated in place by the current one without manual manifest surgery.
Repro steps
- On Windows (tested: Windows 11, cpython 3.10.11, amd64), download and checksum-verify the
v2.0.1release archive. bash install.sh --target=claude --source=local→ fails with "No verified dependency lock target for cpython|3.10|windows|amd64|none|none|unsupported."bash install.sh --target=claude --source=local --no-deps→ fails with "Refusing to overwrite unowned file: .../ads/SKILL.md" (on a machine with a pre-existing install from an older installer version).
Impact
verify_managed_install.pycannot pass on Windows today, for any user, regardless of Python version — there's no supported path to a "managed" install on this platform.- Users who installed with an older installer version cannot use the current installer to update in place; it silently refuses every file rather than erroring loudly about the manifest-format mismatch, which is safe but not very actionable — a clearer message ("existing manifest is unrecognized format vX, cannot verify ownership") would help.
Suggested fixes
- Either wire up real Windows
casebranches using the existingruntime-windows-cp311/runtime-windows-cp312evidence files, or explicitly document that--no-depsis required on Windows (and make the error message say so instead of "moving-range fallback is disabled"). - Have the installer detect a manifest-format mismatch and print a clear, specific message (e.g. "existing manifest at ... is format vN, this installer writes vM — manual migration or
uninstall.shfirst") rather than a generic per-file refusal.
Happy to share the exact manifest JSON / installer output if useful.
Source: AgriciDaniel/claude-ads