* fix(update): self-heal a venv left half-built by an interrupted install
An update killed mid dependency-install (Ctrl-C, terminal close, WSL OOM)
could leave the venv with pip wiped and core deps (e.g. Pillow) missing,
with no automatic recovery — the user had to manually run ensurepip +
reinstall.
Drop an install-scoped .update-incomplete breadcrumb right before the dep
install and clear it only after core-dependency verification passes. On the
next launch (any command except 'update' itself), if the marker is present,
unconditionally bootstrap pip via ensurepip then re-run the .[all] install +
verification, then clear the marker. Failure leaves the marker for retry and
prints the manual recovery command. Never raises — recovery cannot block
launch.
* fix(update): address review — stderr-only recovery output, single-flight lock, gitignore marker
- Route all recovery output (status lines + streamed pip/uv install via
fd-level dup2) to stderr so protocol-on-stdout launches (hermes acp)
never get install noise on the JSON-RPC stream.
- Single-flight O_EXCL lockfile (.update-incomplete.lock) so a gateway
start + CLI launch (or two profiles) can't run concurrent installs
into the shared venv; stale locks (>1h) are broken for the next launch.
- gitignore .update-incomplete + lock so source-tree installs keep a
clean git status and update's autostash skips them.
- Document why the loose 'update' argv substring match is intentional
(over-match defers one launch; under-match would race the real update).
- 4 new tests: lock held → skip, stale lock broken, lock released,
output lands on stderr only.