<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="root">PROP-019 — VibeVM Version Manager (VVM)</title>
  <status stage="impl" state="done" comment="C 2026-07-25: the vvm is implemented across the whole verb set with dense implements/verifies coverage; fact grain 2026-07-24"/>
  <p p="1"><fact id="status-line" status="impl/done">**Status: IMPLEMENTED** (specified 2026-06-17 in an owner-requested design
session and verified against the tree 2026-07-25 by the spec-actualization
campaign — the vvm ships across the whole verb set, with dense specmap
`implements` / `verifies` coverage); **revised
to v2 the same day** after the owner found two architectural flaws in v1
(see §9): (a) making `$VIBEVM_HOME` the single source of truth forced a
console reload on every switch/reinstall, and (b) replacing the running
distribution locks its files (the `.exe`, and any future DLLs). v2 keeps the
v1 command surface but reworks the internals: a live `current` pointer
file + `current_exe()` ground truth (env demoted to advisory), the *whole
distribution directory* as the immutable unit of install/switch, content-
cheap diff-copy between instances, and a new `vibe vars` reconciliation
command. **v3 landed 2026-09-11:** source builds still hold developer/managed
checkouts by reference, while a binary release owns the exact clean source
snapshot that produced it; `vibe` + essential `vibe-index` ship and activate
together; mutable remote version labels create immutable local `#N`
generations. §9 records the resulting decisions.</fact></p>
  <p p="2"><fact id="related" status="spec/done">**Related:** [PROP-018](PROP-018-agentic-standalone-modes.xml) (VVM is a
second *standalone-mode* citizen after `vibe skill` — pure algorithm, no
LLM; §2.1), [PROP-016](PROP-016-source-mirrors.xml) (the source mirrors VVM
clones from when run outside a source tree), [PROP-000 §7](PROP-000.xml#registry)
and [PROP-000 §20](PROP-000.xml#token-secrecy) (the publish token VVM never
touches), [`VIBEVM-SPEC.md`](../../VIBEVM-SPEC.md) (CLI-first posture), and
the repo's `rust-toolchain.toml` (the pin VVM honours when building).</fact></p>
  <section id="motivation" title="1. Motivation">
    <section id="problem" title="1.1 The problem — vibevm cannot install itself">
      <list ordered="false" p="3">
        <item><fact id="problem-assumes-binary" status="spec/done">Every other capability of vibevm assumes a `vibe` binary already exists.</fact></item>
        <item><fact id="problem-manual" status="spec/done">Getting it there is, today, an unspecified manual act: clone, `cargo build`,
  find the artifact, put it on `PATH`.</fact></item>
        <item><fact id="problem-no-story" status="spec/done">There is no story for *which* version you built, switching between versions,
  reclaiming the disk a Rust build tree eats, or doing any of it on a clean
  machine.</fact></item>
      </list>
      <p p="4"><fact id="forces-lead" status="spec/done">Two further forces shaped v2 (§9):</fact></p>
      <list ordered="false" p="5">
        <item><fact id="force-no-reload" status="spec/done">the owner iterates fast and must not have to **reload the console** after
  each `self install`/`use`;</fact></item>
        <item><fact id="force-multi-file" status="impl/done">and a distribution is **more than one file** (`vibe`, `vibe-index`, exact
  source, manifest and future assets), any executable member of which may lock
  while running.</fact></item>
      </list>
    </section>
    <section id="what" title="1.2 What VVM is — a self-distribution manager">
      <list ordered="false" p="6">
        <item><fact id="VVM-COMMAND-GROUP" status="spec/done">VVM is a command group, `vibe self`, described as
  **"VibeVM Version Manager / VVM"**.</fact></item>
        <item><fact id="VVM-CAPABILITIES" status="impl/done">It either builds a selected version from git or installs a verified native
  release, places the whole `vibe` + `vibe-index` distribution under a managed
  prefix, exposes both commands through stable shims, tracks the exact source
  tree, and lets the user update, switch, inspect, roll back, garbage-collect,
  and remove immutable local instances.</fact></item>
        <item><fact id="VVM-PLATFORMS" status="spec/done">It runs on Windows, macOS, and Linux, across the shells those platforms use.</fact></item>
      </list>
    </section>
    <section id="not-install" title="1.3 What this is NOT — not `vibe install`">
      <p p="7"><fact id="req-not-install" status="spec/done">`req r1`</fact></p>
      <list ordered="false" p="8">
        <item><fact id="INSTALL-IS-PACKAGES" status="spec/done">`vibe install` is the **package** manager (PROP-003 / PROP-017): it resolves
  packages a *project* depends on into that project.</fact></item>
        <item><fact id="VVM-IS-TOOL" status="spec/done" action="continue" actionstage="doc" audience="user">VVM manages *the vibevm tool itself* on *the machine* — a user-global
  prefix, not a project's `vibedeps/`.</fact></item>
        <item><fact id="NO-SHARED-PATHS" status="spec/done">The two never share code paths; `vibe self` is its own command group so the
  package verbs stay uncontaminated.</fact></item>
      </list>
    </section>
  </section>
  <section id="decisions" title="2. Decisions">
    <section id="standalone" title="2.1 VVM is a standalone, algorithmic capability">
      <p p="9"><fact id="req-standalone" status="spec/done">`req r1`</fact></p>
      <list ordered="false" p="10">
        <item><fact id="NO-LLM-NO-AGENT" status="spec/done">VVM needs no LLM and no host agent.</fact></item>
        <item><fact id="STANDALONE-CLASS" status="spec/done">In PROP-018's terms it is a **standalone-mode** capability — the second
  after `vibe skill` — behaving identically with or without an agent.</fact></item>
        <item><fact id="FULLY-SCRIPTABLE" status="spec/done" action="continue" actionstage="doc" audience="user">It is fully scriptable: every interactive prompt has a non-interactive flag
  equivalent, so VVM works from a bare terminal, CI, or an agent transcript.</fact></item>
      </list>
    </section>
    <section id="surface" title="2.2 Command surface — `vibe self` (+ `vibe vars`)">
      <p p="11"><fact id="req-surface" status="spec/done">`req r5`</fact></p>
      <p p="12"><fact id="SELF-NAMING" status="spec/done">`vibe self` — named after rustup's `self` (a tool that manages its own
versions), and unambiguous where `man` collided with the Unix manual page:</fact></p>
      <list ordered="false" p="13">
        <item><fact id="CMD-INSTALL" status="impl/done" action="continue" actionstage="doc" audience="user">`self install &lt;selector&gt;` — from a source execution, build the selected
  ref; from a managed binary execution, an explicit `X.Y.Z` installs that
  native release. Flags: `--release` / `--profile &lt;debug|release&gt;` (source
  lane); `--mirror &lt;gitverse|github&gt;` (force the source lane); `--force`
  (always allocate a fresh immutable `#N`).</fact></item>
        <item><fact id="CMD-UPDATE" status="impl/done" action="continue" actionstage="doc" audience="user">`self update` follows the running origin, and moves versions FORWARD (owner
  ruling 2026-09-14, replacing the v1 rule that a binary execution refreshes its
  own number and never jumps). A source/worktree execution rebuilds its exact
  source root at `latest`. A binary execution reads the release channel's
  newest-release aggregate manifest and goes to the **newest published
  release**: newer than the running version installs and activates it by the
  same verified path an explicit `X.Y.Z` takes. The SAME version is not a no-op
  — a release may be rebuilt under its own number, so the manifest's bundle
  digest decides: changed bytes install a fresh immutable `#N`, unchanged bytes
  reuse the instance already held and cost no bundle download at all. A newest
  release OLDER than the running one (withdrawn on the far side) never walks the
  machine backwards. `--force` installs a fresh `#N` whatever the digest says.
  `self install stable` enters this same path, since `stable` IS the newest
  release (§2.3).</fact></item>
        <item><fact id="CMD-REINSTALL" status="impl/done" action="continue" actionstage="doc" audience="user">`self reinstall` refreshes the version that is RUNNING, without changing which
  version that is: a binary execution refetches its own release's manifest and
  bundle, a source/worktree execution rebuilds its exact source root — either
  way landing a fresh immutable `#N`. It takes no `--force`, because a fresh
  instance is the whole verb; `--profile` / `--release` steer the source lane
  exactly as on `update`. The pair is deliberate (owner ruling 2026-09-14):
  refetching the current version and moving to a new release are two acts, so
  they carry two names.</fact></item>
        <item><fact id="CMD-OFFLINE" status="impl/done" action="continue" actionstage="doc" audience="user">Every `self` verb honours the offline posture the process resolved —
  `--offline`, `VIBE_OFFLINE` or `[net] offline`, layered as PROP-010
  `##OFFLINE-LAYERING` says. Under it a release-lane verb is refused before its
  first request, naming the verb and the address it would have needed; a
  source-lane rebuild runs without fetching when the checkout and the crates
  are already local, and is refused the same way otherwise. The domain never
  reads the environment for this: the composition root hands the resolved
  posture in.</fact></item>
        <item><fact id="CMD-IMPORT" status="impl/done">`self import &lt;PATH&gt; --tag &lt;X.Y.Z&gt;` — put a legacy READY-BUILT local `vibe`
  executable into the inventory as an immutable `tag:` instance, reusing the
  store/placer/lock/state machinery (built 2026-08-20 for the 1.0.0
  distributive; the install.ps1 of the zip calls it instead of duplicating
  `state.toml`). Local by construction: no network, no signature machinery —
  the file is streamed through SHA-256 once, and that digest drives
  idempotence: same tag + same digest reuses the instance; same mutable tag +
  different digest creates a new local `#N` beside the preserved old one.
  `--replace-candidate` remains compatibility spelling. Optional `--commit`, `--profile`
  (default `release`); inactive by default — `--use` runs the same complete
  shim/PATH/pointer/advisory-HOME activation as every other lane.</fact></item>
        <item><fact id="CMD-BOOTSTRAP" status="impl/done">`self bootstrap --manifest DISTRIBUTIONS.json --version X.Y.Z
  --release-base URL` is the narrow machine-facing cold-start verb. It verifies
  the temporary bootstrap against the aggregate, downloads one bounded native
  bundle anonymously, verifies/extracts it, and activates it; `--force` makes
  another generation.</fact></item>
        <item><fact id="CMD-USE" status="impl/done" action="continue" actionstage="doc" audience="user">`self use &lt;selector&gt;` — make a version active by repointing the live
  `current` file — **no console reload** (§2.5). `--eval` prints the shell
  line for an integrity-checked shell-local override instead of touching the
  durable environment.</fact></item>
        <item><fact id="CMD-ROLLBACK" status="impl/done" action="continue" actionstage="doc" audience="user">`self rollback` atomically swaps to the immediate `previous` instance;
  repeating it toggles the two pointers without deleting either generation.</fact></item>
        <item><fact id="CMD-LS" status="impl/done">`self ls` (alias `list`) prints copyable `&lt;kind&gt;:&lt;id&gt;#N` selectors and
  provenance. `*` marks the active pointer; `&gt;` independently marks the
  actually running executable, including a direct developer-worktree binary.</fact></item>
        <item><fact id="CMD-CURRENT-WHICH" status="impl/done">`self current` shows active and running identities separately. `self which
  [vibe|vibe-index|source]` prints that member's absolute path; `self source`
  prints exactly the running/active source root for shell composition.</fact></item>
        <item><fact id="CMD-REMOVE" status="spec/done">`self remove &lt;selector&gt;` (aliases `rm`, `del`, `uninstall`) — safe by
  default (§2.9).</fact></item>
        <item><fact id="CMD-GC" status="spec/done">`self gc` — reclaim disk (§2.10).</fact></item>
        <item><fact id="CMD-DOCTOR" status="spec/done">`self doctor` (+ `--fix`) — verify the install and environment (§2.11).</fact></item>
        <item><fact id="CMD-ENV" status="impl/done">`self env` — print safely quoted `VIBEVM_SHELL_HOME` + advisory
  `VIBEVM_HOME` activation for a shell.</fact></item>
        <item><fact id="CMD-RELOCATE" status="spec/done">`self relocate &lt;path&gt;` — repoint source provenance to a moved checkout and
  clear the instances built from the abandoned tree (§2.17). Flags:
  `--from &lt;old-path&gt;` (override the inferred old location); `-y`/`--yes`
  (non-interactive); `--dry-run`.</fact></item>
      </list>
      <p p="14"><fact id="CMD-VARS" status="spec/done">Top-level **`vibe vars`** (§2.14) prints the runtime variable context —
the values vibevm *actually* uses (derived from `current_exe`) versus what
the environment says — so scripts never break on a stale `$VIBEVM_HOME`.</fact></p>
    </section>
    <section id="selectors" title="2.3 Version selectors and resolution">
      <p p="15"><fact id="req-selectors" status="spec/done">`req r2`</fact></p>
      <p p="16"><fact id="SELECTOR-DEF" status="spec/done">A *selector* names what to install or use; resolution is deterministic:</fact></p>
      <list ordered="false" p="17">
        <item><fact id="SEL-LATEST" status="spec/done">`latest` → tip of branch `main`.</fact></item>
        <item><fact id="SEL-STABLE" status="spec/done">`stable` → highest semantic-version git tag (the newest release).</fact></item>
        <item><fact id="SEL-SEMVER" status="impl/done">`X.Y.Z` → a tag; tries `X.Y.Z` then `vX.Y.Z`, always peeling an
  annotated tag to its exact commit before recording/building.</fact></item>
        <item><fact id="SEL-COMMIT" status="spec/done">a hex commit-ish → a commit.</fact></item>
        <item><fact id="SEL-CANONICAL" status="spec/done">the canonical `&lt;kind&gt;:&lt;id&gt;` form (as `self ls` prints) → that exact id.</fact></item>
        <item><fact id="SEL-INSTANCE" status="impl/done">`&lt;kind&gt;:&lt;id&gt;#N` → one exact immutable local generation; the terminal
  `#N` disambiguates repeated content under the same mutable remote label.</fact></item>
        <item><fact id="SEL-BARE" status="spec/done">any other bare name → branch, then tag, then commit (hex commits and
  `X.Y.Z` tags are classified before this point).</fact></item>
      </list>
      <list ordered="false" p="18">
        <item><fact id="SEL-FORCE-FLAGS" status="spec/done">`--tag` / `--branch` / `--commit` force interpretation, mapping to
  fully-qualified git refs so a name that is both never resolves by accident.</fact></item>
        <item><fact id="SEL-DEFAULTS" status="impl/done">No selector: `install` → `latest`; `remove` opens an interactive picker (or
  refuses without a TTY); `use` requires an explicit selector.</fact></item>
      </list>
    </section>
    <section id="layout" title="2.4 On-disk layout — instances, the `current` pointer, manifests">
      <p p="19"><fact id="req-layout" status="spec/done">`req r2`</fact></p>
      <list ordered="false" p="20">
        <item><fact id="INSTANCE-UNIT" status="spec/done">The **unit of install and switch is a whole distribution directory**
  (an *instance*: the binary plus future DLLs/assets), not a single file
  (§9.3).</fact></item>
        <item><fact id="CANONICAL-ID" status="spec/done">A version has a **canonical id** `&lt;kind&gt;:&lt;id&gt;` (`kind ∈ {tag, branch,
  commit}`); each id may have several instances (one per install).</fact></item>
      </list>
      <fence p="21">$VIBEVM_INSTALL_ROOT/            install base — default: ~/.vibe
└─ opt/
   ├─ bin/                       ← on PATH; stable shims
   │   ├─ vibe, vibe-index       POSIX shims
   │   └─ vibe.cmd, vibe-index.cmd   Windows shims
   └─ vibevm/
       ├─ current                ← live pointer: the active instance dir
       ├─ previous               ← immediate rollback pointer
       ├─ state.toml             inventory: every instance + its metadata
       ├─ versions/&lt;kind&gt;/&lt;id&gt;/&lt;instance&gt;/   immutable distribution dirs
       │       bin/{vibe[.exe],vibe-index[.exe]}
       │       source/                 binary lane: extracted exact source tree
       │       vibevm-source.zip       binary lane: retained verification source
       │       DISTRIBUTION.json       binary lane: component/source identity
       │       .vvm-manifest.toml      source-build diff-copy inventory
       ├─ build/                 shared cargo --target-dir (gc-able)
       └─ src/.mirror/           shared managed source clone; external developer
                                 worktrees stay outside and are referenced</fence>
      <list ordered="false" p="22">
        <item><fact id="ROOT-DEFAULT" status="impl/done" action="continue" actionstage="doc" audience="user">`$VIBEVM_INSTALL_ROOT` (the install BASE) defaults to `~/.vibe` → root
  `~/.vibe/opt` in normal use (owner ruling 2026-08-20, the release-1.0
  install path; until then the default base was the bare home dir → `~/opt`).
  An explicit override keeps its meaning — `&lt;override&gt;/opt` — and one env var
  still relocates everything; tests pin it to a temp dir. A managed binary
  keeps recognising its own root by shape (the trailing `opt` component),
  so both generations of store keep working.</fact></item>
        <item><fact id="INSTANCE-COUNTER" status="spec/done">`&lt;instance&gt;` is a monotonic counter (§9.4) — never a hash of the payload
  (§9.2).</fact></item>
        <item><fact id="SHIM-STABLE" status="spec/done">The shim dir is stable; switching repoints `current`, never the shim.</fact></item>
        <item><fact id="SOURCES-BY-REF" status="impl/done">Development/managed checkouts are held **by reference** and their untracked
  caches are never copied. A binary instance instead owns a clean tracked-file
  snapshot under its own `source/`, because shipped skills may require the
  matching vibevm source even on a machine without a checkout (§2.16).</fact></item>
      </list>
    </section>
    <section id="activation" title="2.5 Activation — live `current` file + `current_exe` truth">
      <p p="23"><fact id="req-activation" status="spec/done">`req r2`</fact></p>
      <p p="24"><fact id="ACTIVATION-LAW" status="spec/done" action="continue" actionstage="doc" audience="user">**Switching must not reload the console and must not overwrite a running
file.** (v1's "env is truth" violated the first; see §9.1.) The model has
four layers:</fact></p>
      <list ordered="true" p="25">
        <item><fact id="LAYER-CURRENT-EXE" status="impl/done">**`current_exe()` → the running process's truth.** A modern managed `vibe` lives
   at `…/opt/vibevm/versions/&lt;kind&gt;/&lt;id&gt;/&lt;instance&gt;/bin/vibe[.exe]`
   (legacy root-level binaries remain readable), so it
   derives its own version id, `VIBEVM_HOME` (= its instance dir), and
   `VIBEVM_INSTALL_ROOT` (walk up to `opt`) from its own path — no env var
   needed. Outside a managed location (dev `cargo run`, a bare copy), it
   falls back to env, then defaults.</fact></item>
        <item><fact id="LAYER-SHELL-OVERRIDE" status="impl/done">**`VIBEVM_SHELL_HOME` → explicit shell-local selection.** `self use --eval`
   and `self env` emit it together with advisory HOME; stable shims prioritize
   this non-durable override so evaluation really switches both `vibe` and
   `vibe-index` even while durable `current` names another instance.</fact></item>
        <item><fact id="LAYER-CURRENT-FILE" status="spec/done">**`current` file → the live active instance.** The shim reads
   `$shimdir/../vibevm/current` on **every** launch and execs that instance.
   `self use` rewrites `current` → the **next** `vibe` in the **same shell**
   uses it. No reload (the shim reads a file, not the shell's frozen env).</fact></item>
        <item><fact id="LAYER-ENV-ADVISORY" status="spec/done" action="continue" actionstage="doc" audience="user">**`$VIBEVM_HOME` / `$VIBEVM_INSTALL_ROOT` (env) → advisory.** Still set
   durably for external `JAVA_HOME`-style tools, but no longer the source of
   truth. They may lag (new shells only); `vibe vars` (§2.14) reconciles,
   and a managed `vibe` whose `current_exe`-derived home disagrees with the
   env prints a one-line stderr warning at startup (suppressed outside a
   managed run).</fact></item>
      </list>
      <list ordered="false" p="26">
        <item><fact id="SHIMS-MINIMAL" status="impl/done">The four shims (`bin/{vibe,vibe.cmd,vibe-index,vibe-index.cmd}`) are minimal:
  use explicit shell override, else resolve `current`, then fall back to
  `$VIBEVM_HOME`; exec the corresponding member or print "no active vibevm — run
  `vibe self use &lt;selector&gt;`".</fact></item>
        <item><fact id="SHIMS-BOTH-FORMS" status="impl/done">Both POSIX and `.cmd` forms exist for both commands (Git Bash
  won't resolve `.cmd`; cmd/PowerShell won't run an extensionless script).</fact></item>
      </list>
    </section>
    <section id="path" title="2.6 PATH and durable environment management">
      <p p="27"><fact id="req-path" status="spec/done">`req r2`</fact></p>
      <p p="28"><fact id="ENV-MGMT-LEAD" status="spec/done">VVM detects OS and shell and manages durable settings under strict rules:</fact></p>
      <list ordered="false" p="29">
        <item><fact id="PATH-SHIM-DIR" status="spec/done" action="continue" actionstage="doc" audience="user">**The shim dir on `PATH`** (stable; set once).</fact></item>
        <item><fact id="ENV-ADVISORY" status="spec/done">**`VIBEVM_HOME` / `VIBEVM_INSTALL_ROOT`** as *advisory* env (§2.5) —
  repointed on `self use` for external tools; truth lives in `current` +
  `current_exe`.</fact></item>
      </list>
      <p p="30"><fact id="env-rules-lead" status="spec/done">Rules:</fact></p>
      <list ordered="false" p="31">
        <item><fact id="RULE-IDEMPOTENT" status="spec/done">**idempotent** (a marker guards the edit; no duplicate lines/entries),</fact></item>
        <item><fact id="RULE-NEVER-CLOBBER" status="spec/done">**never clobber** (only our entry is added; the rest of `PATH` is
  preserved),</fact></item>
        <item><fact id="RULE-OS-AWARE" status="impl/done">**OS/shell-aware** (Windows: raw `HKCU\Environment` — read WITHOUT
  variable expansion, the value's registry kind preserved (`REG_SZ` stays
  `REG_SZ`, `REG_EXPAND_SZ` stays `REG_EXPAND_SZ`, so a PATH carrying
  `%USERPROFILE%\…` survives untouched), entries compared normalised
  (expand+full-path+case-insensitive) for dedup while unrelated raw entries
  are preserved byte-for-byte, and a `WM_SETTINGCHANGE` broadcast after the
  write; the earlier `[Environment]` API form silently expanded references
  and lost the kind. POSIX: a marked block in the detected shell's rc —
  bash/zsh/fish/`.profile`),</fact></item>
        <item><fact id="RULE-CONSENT" status="impl/done">and **consent + honesty** (interactive maintenance uses confirm / `-y` /
  `self doctor --fix`; executing the native bootstrap one-liner is itself the
  explicit install consent). Output says whether PATH was already live or only
  durable for new shells. Activation writes stable PATH first, flips `current`
  second, and updates advisory `VIBEVM_HOME` last, so a failure cannot point the
  durable version variable ahead of the live selector.</fact></item>
        <item><fact id="RULE-INJECTABLE-SEAM" status="spec/done">The durable writer is an injectable seam so tests exercise the POSIX rc
  path in a temp file and never mutate the real machine.</fact></item>
      </list>
    </section>
    <section id="build" title="2.7 Install pipelines — source builds and verified native bundles">
      <p p="32"><fact id="req-build-pipeline" status="spec/done">`req r2`</fact></p>
      <p p="33"><fact id="PIPELINE-OVERVIEW" status="impl/done">Both lanes end identically: stage a complete `vibe` + `vibe-index`
distribution, record a fresh immutable local instance, then flip `current`.
The source lane builds from a referenced checkout; the binary lane verifies a
bounded published bundle and installs its owned clean source snapshot.</fact></p>
      <list ordered="false" p="34">
        <item><fact id="STEP-LOCATE" status="impl/done">**Locate source (§2.16).** Source builds have two referenced origins:</fact></item>
        <item><fact id="LOC-EXTERNAL" status="spec/done">*external* — `self install` run inside a committer's own checkout
    (outside the install root): build it **in place**, never touch its git
    state, and record its canonical absolute path as provenance so a later
    `self install &lt;id&gt;` can rebuild from the remembered location (a *linked
    source*).</fact></item>
        <item><fact id="LOC-MANAGED" status="impl/done">*managed* — a shared clone vibevm owns under `src/.mirror`: created once,
    then updated **incrementally** with `git fetch`/`checkout` (or
    `pull`; stash first if dirty), never re-cloned, so a full rebuild
    (hours, in a large future) is avoided.</fact></item>
        <item><fact id="STEP-RESOLVE" status="spec/done">**Resolve.** The selector (§2.3) → a concrete commit, recorded.</fact></item>
        <item><fact id="STEP-BUILD" status="impl/done">**Build.** `cargo build [--release] -p vibe-cli -p vibe-index` into the **shared**
  `build/` target dir (§9.3 — never the source tree's `target/`; load-
  bearing on Windows and keeps the dev tree clean), honouring
  `rust-toolchain.toml`.</fact></item>
        <item><fact id="STEP-VIBE-ONLY" status="impl/done">**Build the two essential binaries only.** `vibe-index` is part of VibeVM's
  own distribution because on-premises registry owners require it. The
  terminal products (vibeterm,
  vibeframe) and the GUI launchers (vibe-launcher) used to be packaged
  into the instance alongside `vibe`; they have moved to a separate
  products repo (`vibevm-term`) and now publish themselves to `PATH`
  through their own version-manager (`&lt;product&gt; self install`). The
  install pipeline here builds `vibe` + `vibe-index` — `vibe term` / `vibe frame`
  resolve the terminal apps through `$VIBEVM_&lt;APP&gt;` → the active
  instance's packaged `&lt;app&gt;/` (back-compat) → `PATH`, with an in-place
  fallback for `vibe tree` when no terminal app is available
  (`spec://term-common/PROP-vvm#root` is the products-repo twin).</fact></item>
        <item><fact id="STEP-PLACE" status="impl/done">**Place a source build by diff-copy (§2.15).** Both binaries are placed under
  `instance/bin/`; unchanged files may be hardlinked from the prior source
  instance. The distribution is placed into a
  fresh instance dir, copying only files that changed versus the previous
  instance and hardlinking the rest — so a 2 GB distribution where only
  `vibe.exe` changed costs one file copy, never a full re-copy or a payload
  hash (§9.2). If nothing changed, no new instance is made.</fact></item>
        <item><fact id="STEP-BINARY-BUNDLE" status="impl/done">**Install a binary bundle.** Select one of the four fixed targets from
  `DISTRIBUTIONS.json`; enforce independent size limits; verify outer bundle,
  embedded manifest, both binaries, and `vibevm-source.zip` by SHA-256; reject
  traversal, collisions, symlinks, special files, extras, and decompression
  overflow; cap source files/materialized nodes/depth/path at
  200,000/400,000/256/4096; retain the source ZIP and extract it under
  `source/`. The inventory retains the authenticated manifest digest that
  anchors later `doctor`/reuse checks.</fact></item>
        <item><fact id="STEP-MUTABLE-IMMUTABLE" status="impl/done">**Remote labels are mutable; local instances are not.** A republished
  `v1.0.0` may have new bytes, but it becomes `tag:1.0.0#N+1`. A non-forced
  identical bundle reuses an instance only after re-verifying its manifest,
  binaries, retained source ZIP, and extracted source file set/content.</fact></item>
        <item><fact id="STEP-RECORD-FLIP" status="impl/done">**Record + activate.** `state.toml` gets the instance (id, instance,
  commit, toolchain, profile, time, origin, source_path). Every activating
  lane then uses one failure-ordered transaction: publish the stable shims,
  persist their generic PATH, journal/flip `current` + `previous`, and update
  advisory `VIBEVM_HOME` last.</fact></item>
      </list>
      <p p="35"><fact id="NO-OVERWRITE-EVER" status="spec/done">Because every install writes a **new** instance dir and switching is a
pointer flip, **no in-use file is ever overwritten** — the running process
keeps its instance dir intact; no lock, no reload, for the `.exe` or any
DLL, on any OS (§9.3).</fact></p>
    </section>
    <section id="tools" title="2.8 Required toolchain — a single source of truth">
      <p p="36"><fact id="req-tools" status="spec/done">`req r2`</fact></p>
      <list ordered="false" p="37">
        <item><fact id="TOOLS-LIST" status="impl/done" action="continue" actionstage="doc" audience="user">A from-source build needs **git**, a **Rust toolchain** (rustc + cargo,
  stable ≥ 1.93, edition 2024 — via rustup so the pin resolves), and a
  **system linker / C toolchain** (Windows: VS Build Tools; macOS: Xcode CLT;
  Linux: `build-essential`). A native binary install needs none of those;
  its bootstrap uses only the platform download/TLS facilities.</fact></item>
        <item><fact id="NO-OPENSSL" status="spec/done">OpenSSL is deliberately not required (rustls).</fact></item>
        <item><fact id="TOOLS-TABLE-SSOT" status="spec/done">The list lives once as a `REQUIRED_TOOLS` table — `(name, min_version,
  check_command, help_url)` — read by `self doctor` (§2.11) and asserted by a
  test; it is the runnable form of "how to update the stack" (§7).</fact></item>
        <item><fact id="TOKEN-NOT-TOOL" status="spec/done">The publish token is **never** in this set (§2.13).</fact></item>
      </list>
    </section>
    <section id="remove" title="2.9 Removal — safe by default">
      <p p="38"><fact id="req-remove" status="spec/done">`req r2`</fact></p>
      <p p="39"><fact id="REMOVE-SAFE" status="spec/done">`self remove` never silently wipes everything:</fact></p>
      <list ordered="false" p="40">
        <item><fact id="RM-SELECTOR" status="impl/done">`self remove &lt;selector&gt;` — remove that version or exact `#N`.
  For binary bundles, `--bin` removes only `bin/`, `--src` only the owned
  `source/` + retained source ZIP, and the default removes the whole instance.
  A scoped removal reports the deliberately partial, non-activatable result;
  external worktrees are never deleted and shared managed-source effects are
  reported separately.</fact></item>
        <item><fact id="RM-PICKER" status="spec/done">`self remove` with no selector — an **interactive picker**; a non-
  interactive context errors with a hint, never a wipe.</fact></item>
        <item><fact id="RM-ALL" status="spec/done">`self remove --all` — every version, behind the flag **and** a re-confirm.</fact></item>
        <item><fact id="RM-PROTECTED" status="impl/done" action="continue" actionstage="doc" audience="user">The **active** version and the **running** instance are protected:
  removing an active but non-running instance needs `--force`; the actually
  running instance is never removable, even with that flag.</fact></item>
      </list>
      <p p="41"><fact id="EXTERNAL-NEVER-REMOVED" status="spec/done">External sources (committer trees) are **never** removed — VVM only forgets
their provenance record; the managed `src/.mirror` clone is VVM's to drop.</fact></p>
    </section>
    <section id="gc" title="2.10 Garbage collection — `self gc`">
      <p p="42"><fact id="req-gc" status="spec/done">`req r2`</fact></p>
      <p p="43"><fact id="GC-LEAD" status="spec/done">`self gc` reclaims disk:</fact></p>
      <list ordered="false" p="44">
        <item><fact id="GC-BUILD" status="spec/done">`--build` — clean the shared Rust build cache (`build/`); forces a rebuild
  next install but touches no installed instance.</fact></item>
        <item><fact id="GC-PRUNE-OTHERS" status="impl/done">`--prune-others` — remove every instance except the active and its immediate
  rollback target (plus referenced source), behind a re-confirm.</fact></item>
      </list>
      <list ordered="false" p="45">
        <item><fact id="GC-BEST-EFFORT" status="spec/done">Instances are pruned **best-effort**: a dir still locked by a running
  process is skipped and collected on a later run (on POSIX the unlink
  succeeds and the inode lives until the process exits).</fact></item>
        <item><fact id="GC-REFCOUNT-SAFE" status="spec/done">Hardlinked files are refcount-safe — removing one instance never corrupts
  another that shares inodes (§2.15).</fact></item>
        <item><fact id="GC-AUTO-PRUNE-BINARY" status="impl/done">**No install silently erases rollback history.** Binary and source
  generations remain until explicit `remove` or `gc --prune-others`; the
  immediate `previous` target survives pruning.</fact></item>
        <item><fact id="GC-SCOPE" status="spec/done">`self gc` operates **only** inside the install root and **never** touches
  the shared `~/.cargo` caches.</fact></item>
      </list>
    </section>
    <section id="introspection" title="2.11 Introspection — `doctor`, `ls`, `current`, `which`, `env`">
      <p p="46"><fact id="req-introspection" status="spec/done">`req r2`</fact></p>
      <list ordered="false" p="47">
        <item><fact id="DOCTOR-VERIFIES" status="impl/done">`self doctor` verifies end to end: both generated stable shims have their
  expected content/mode and their directory is on `PATH`; every source-built
  active/running instance still matches its placement manifest; source origins
  have their required build tools; and a modern binary origin does **not**
  require Rust/git but must pass the authenticated manifest-rooted full
  bundle/source integrity check. Source paths must still resolve to an actual
  vibevm root. A legacy
  single-binary import is labeled partial with an update recipe but its payload
  is still verified. It exits non-zero while problems remain; `--fix` repairs
  shims/PATH with consent and recomputes the result.</fact></item>
        <item><fact id="LS-READS-CURRENT" status="impl/done">`self ls` / `current` / `which` read the **`current` file** for the active
  selection and `current_exe()` for the independently running selection. A
  direct worktree executable wins over cwd and over an unrelated active binary.</fact></item>
        <item><fact id="ENV-PRINTS" status="spec/done">`self env` prints shell-specific activation lines.</fact></item>
      </list>
    </section>
    <section id="bootstrap" title="2.12 Cold-start (bootstrap)">
      <p p="48"><fact id="req-bootstrap" status="spec/done">`req r2`</fact></p>
      <list ordered="false" p="49">
        <item><fact id="COLD-START-PATH" status="impl/done" action="continue" actionstage="doc" audience="user">Stable endpoints provide one-line native cold start: Bash for Linux/macOS/WSL
  and PowerShell for Windows. Each bounded script downloads
  `DISTRIBUTIONS.json`, verifies a small raw `vibe` bootstrap by size/SHA-256,
  then that Rust binary verifies and atomically installs the full bundle.</fact></item>
        <item><fact id="BOOTSTRAP-SCRIPT-LATER" status="impl/done" action="continue" actionstage="doc" audience="user">The scripts are shipped release assets `install.sh` and `install.ps1` and
  are exposed by stable `https://vibevm.org/install.*` redirects. Homebrew,
  WinGet, and CMD installers remain outside this slice.</fact></item>
      </list>
    </section>
    <section id="security" title="2.13 Security and trust">
      <p p="50"><fact id="req-security" status="spec/done">`req r2`</fact></p>
      <p p="51"><fact id="ACE-ACCEPTED" status="spec/done">Building an arbitrary ref is arbitrary code execution — inherent to a build
tool the user invokes deliberately, and accepted.</fact></p>
      <p p="52"><fact id="sec-constraints-lead" status="spec/done">Constraints:</fact></p>
      <list ordered="false" p="53">
        <item><fact id="SEC-TLS" status="spec/done">host-key (SSH) / TLS verification never disabled on clone;</fact></item>
        <item><fact id="SEC-TOKEN" status="impl/done">binary install/update reads public releases anonymously; the publish token is
  **never** read by VVM nor shown by `vibe vars`;</fact></item>
        <item><fact id="SEC-SCOPE" status="spec/done">VVM operates only inside the install root and the declared, consented
  environment edits;</fact></item>
        <item><fact id="SEC-STORE-PATHS" status="impl/done">every store mutation is lexically contained and rejects symlink/reparse or
  special-file ancestors; `current`, `previous`, and recovery-journal paths
  must name exact inventoried instances before they can be applied;</fact></item>
        <item><fact id="SEC-TREE-UNTOUCHED" status="spec/done">the committer's own source tree is **never** mutated (§2.7, §2.16).</fact></item>
      </list>
    </section>
    <section id="vars" title="2.14 `vibe vars` — reconciling actual vs environment">
      <p p="54"><fact id="req-vars" status="spec/done">`req r1`</fact></p>
      <list ordered="false" p="55">
        <item><fact id="VARS-PURPOSE" status="spec/done">Scripts must know the **real** runtime context even when `$VIBEVM_HOME` is
  stale (§9.1).</fact></item>
        <item><fact id="VARS-CONTENT" status="spec/done" action="continue" actionstage="doc" audience="user">`vibe vars` prints the project's env-configurable variables —
  `VIBEVM_INSTALL_ROOT`, `VIBEVM_HOME` (whose *actual* values are derived from
  `current_exe`, §2.5), plus `VIBE_INVOKED_BY`, `VIBE_UNATTENDED`, `VIBE_LOG`
  — in `NAME=VALUE` form. The publish token is deliberately excluded.</fact></item>
      </list>
      <list ordered="false" p="56">
        <item><fact id="VARS-PLAIN" status="spec/done">`vibe vars` — **actual** values, one `NAME=VALUE` per line.</fact></item>
        <item><fact id="VARS-DIFF" status="spec/done">`vibe vars diff` — `NAME=VALUE [ENV_VALUE]`; the bracket appears only when
  the environment differs from the actual.</fact></item>
        <item><fact id="VARS-FULL" status="spec/done">`vibe vars full` — two tables, `# ACTUAL` then `# ENVIRONMENT`.</fact></item>
        <item><fact id="VARS-FULL-DIFF" status="spec/done">`vibe vars full diff` — both tables, differing names marked
  `NAME=VALUE [*]`.</fact></item>
      </list>
      <p p="57"><fact id="VARS-SEMANTICS" status="spec/done">"actual" for the VVM vars is the `current_exe`-derived value (falling back
to env/default outside a managed run); "environment" is the raw env. A
script reads `vibe vars` and knows exactly the context it runs in.</fact></p>
    </section>
    <section id="instances" title="2.15 Distribution instances and diff-copy">
      <p p="58"><fact id="req-instances" status="spec/done">`req r2`</fact></p>
      <p p="59"><fact id="DIFF-COPY-LAW" status="impl/done">Source-built instances use the existing diff-copy optimization. Binary
bundles take the verification-first lane: their publisher digests are checked
on download and again before an identical local instance is reused.</fact></p>
      <list ordered="false" p="60">
        <item><fact id="MANIFEST-PER-INSTANCE" status="spec/done">Each instance carries `.vvm-manifest.toml`: per dist file `(rel, size,
  mtime, hash?)`. The build dir is **persistent** (shared `--target-dir`),
  so cargo preserves the mtime of unchanged outputs across builds.</fact></item>
        <item><fact id="COMPARE-RULES" status="spec/done">On install, for each dist file: compare to the previous instance's
  manifest entry — by **cheap content hash for small files** (≤ a
  threshold) and by **`(size, mtime)` for large files** (stat only, never
  read). Unchanged → **hardlink** the previous instance's file into the new
  one (zero copy). Changed/new → **copy** from the build output. Hardlink
  failure (cross-volume / unsupported) → copy.</fact></item>
        <item><fact id="DEDUP-SKIP" status="spec/done">If **every** file is unchanged, no new instance is made — `current` stays
  ("already up to date"). `--force` always makes a fresh instance.</fact></item>
        <item><fact id="STAGE-ATOMIC" status="impl/done">The complete new instance is staged then atomically renamed; stable PATH is
  ensured, `current` flips, and only then advisory `VIBEVM_HOME` updates.</fact></item>
        <item><fact id="GC-SAFE-IMMUTABLE" status="spec/done">gc is refcount-safe (§2.10); instances are immutable after publish.</fact></item>
      </list>
      <list ordered="false" p="61">
        <item><fact id="SCALES-MULTI-GB" status="spec/done">This scales to a multi-GB distribution: an 80 GB asset that did not change
  is shared by hardlink; only the changed `vibe.exe` is copied (§9.2, §9.6).</fact></item>
        <item><fact id="VIBETERM-PARTICIPATES" status="spec/done">The `vibeterm/` subtree (~220 MB, ~3-4 k files) participates in the same
  diff-copy: small files hashed by the ≤16 MiB rule, the Electron binary by
  `(size, mtime)`; an unchanged vibeterm is hardlinked file-by-file free, and a
  rebuild that changed nothing dedup-skips the whole instance.</fact></item>
      </list>
    </section>
    <section id="provenance" title="2.16 Source provenance and linked sources">
      <p p="62"><fact id="req-provenance" status="spec/done">`req r2`</fact></p>
      <p p="63"><fact id="PROVENANCE-LEAD" status="impl/done">A developer checkout's caches are never copied. Source builds reference a
checkout; binary bundles carry a clean tracked snapshot. Each instance records
its **origin**, source path, commit, and payload identity:</fact></p>
      <list ordered="false" p="64">
        <item><fact id="PROV-MANAGED" status="impl/done">`managed` — the VVM-owned clone at `src/.mirror` (VVM updates it via git
  and may drop it on `remove`/`gc`).</fact></item>
        <item><fact id="PROV-EXTERNAL" status="spec/done">`external` — a committer's own checkout, identified by its **canonical
  absolute path** (`source_path`); VVM never modifies or removes it, only
  remembers where it is.</fact></item>
        <item><fact id="PROV-BINARY" status="impl/done">`binary` — a prebuilt artifact. Two roads enter this origin: the legacy LOCAL
  import (`self import`, shipped 2026-08-20) hashes the supplied file itself —
  SHA-256 computed on this machine, stored as `payload_sha256`, driving
  reuse/refusal. The fetched native release path is keyed by the publisher's bundle/component/source
  digests and owns its extracted clean source under the immutable instance.
  The two must not be conflated: local import verifies
  nothing about authorship, only integrity-identity of the bytes it was
  handed.</fact></item>
      </list>
      <list ordered="false" p="65">
        <item><fact id="LINKED-SOURCE" status="spec/done">The remembered `source_path` makes an external source a **linked source**:
  `self install &lt;id&gt;` can rebuild from the recorded location from anywhere,
  without being in the checkout and without copying it.</fact></item>
        <item><fact id="INSTANCE-SELF-CONTAINED" status="impl/done">Every installed binary bundle is self-contained **with** its matching source
  because packaged skills may need it. `self source` returns
  `instance/source`; for source/worktree executions it returns the canonical
  external/managed git root. A missing owned source is a diagnosed broken
  bundle, not a cwd fallback.</fact></item>
      </list>
    </section>
    <section id="relocate" title="2.17 Relocate — repointing provenance after a checkout move">
      <p p="66"><fact id="req-relocate" status="spec/done">`req r2`</fact></p>
      <list ordered="false" p="67">
        <item><fact id="relocate-motivation" status="spec/done">A committer's checkout is not pinned in place: it is cloned, moved, renamed,
  re-organised on disk. When it moves, every *external* instance's remembered
  `source_path` (§2.16) goes stale — a later linked-source rebuild would miss —
  and the pile of instances built from the abandoned tree clutters `self ls`.</fact></item>
        <item><fact id="RELOCATE-VERB" status="spec/done">`self relocate &lt;new-path&gt;` is the maintenance verb for that move.</fact></item>
      </list>
      <list ordered="false" p="68">
        <item><fact id="REL-VALIDATE" status="spec/done">**Validate the new location.** `&lt;new-path&gt;` must resolve to a real vibevm
  source tree (the `find_source_root` shape — workspace `Cargo.toml` +
  `crates/vibe-cli`); a path that is not a checkout is refused before anything
  mutates. The new path is canonicalised and `\\?\`-stripped exactly as install
  records it (§2.16), so the rewritten `source_path` matches the form every
  other record carries.</fact></item>
        <item><fact id="REL-INFER-OLD" status="spec/done">**Infer the old location.** With no `--from`, the old path is the source
  provenance already recorded on the installed external instances (the common
  value when one checkout moved). `--from &lt;old-path&gt;` states it explicitly for
  an ambiguous inventory. There is nothing to relocate when no external
  instance records a source tree — the command says so and exits, never invents
  a move.</fact></item>
        <item><fact id="REL-REPOINT-PRUNE" status="spec/done">**Repoint, then prune.** Two effects, in one atomic `state.toml` rewrite:
  *(a)* every external instance whose `source_path` is the old location is
  **repointed** to the new one — so linked-source rebuilds (§2.16) resolve to
  the live tree; *(b)* the **built instance directories** sourced from the old
  tree are **removed** — they are provenance-stale artifacts of the abandoned
  checkout, and their records are forgotten.</fact></item>
        <item><fact id="REL-ACTIVE-KEPT" status="impl/done">**Active, actually running, and immediate rollback instances are never
  deleted.** Their directories remain, their matching provenance is repointed,
  and `current`/`previous` are repaired as one journaled transaction before
  any stale instance removal. Removing a protected version is `self remove`'s
  job (§2.9).</fact></item>
        <item><fact id="REL-CONSENT" status="spec/done">**Consent and scriptability.** Removing instances is irreversible, so the
  default is an **interactive warning** that lists what is repointed and what
  is removed, behind a confirm. `-y`/`--yes` (or `--unattended`) skips it for
  scripts and CI; a non-TTY run without `--yes` errors rather than silently
  applying (the same contract as `self remove`/`gc`, §2.9, §2.10). `--dry-run`
  prints the plan and changes nothing. `--json` emits the plan and the applied
  result. A no-op (old already equals new) is reported, not an error.</fact></item>
      </list>
      <p p="69"><fact id="RELOCATE-SCOPE" status="spec/done">Relocate touches only `state.toml` and the install root's own `versions/`
instance dirs. It never touches a committer's source tree (external sources are
held by reference, §2.16), never the shared `build/` cache (that is `self gc`,
§2.10), and never `~/.cargo`.</fact></p>
    </section>
    <section id="release-production" title="2.18 Native release production — four hosts, one verified release">
      <p p="70"><fact id="req-release-production" status="impl/done">`req r1`</fact></p>
      <list ordered="false" p="71">
        <item><fact id="RELEASE-TARGETS" status="impl/done">The closed native matrix is
  `x86_64-pc-windows-msvc`, `x86_64-unknown-linux-musl`,
  `x86_64-apple-darwin`, and `aarch64-apple-darwin`. Each host builds only its
  own native target; no successful cross-compile is passed off as host proof.</fact></item>
        <item><fact id="RELEASE-HOST-SCRIPTS" status="impl/done">Four dependency-light wrappers under `distribution/release/` drive
  `cargo xtask dist build`. Construction runs without tests by default;
  `--checks`, `--tests`, and `--self-check` are explicit opt-ins, and
  `--upload` remains the manual build-plus-upload convenience. The Linux
  wrapper installs the Rust musl target through rustup and fails early with an
  actionable message when the required `musl-gcc` is absent; it never invokes
  a package manager or sudo.</fact></item>
        <item><fact id="RELEASE-CLEAN-SNAPSHOT" status="impl/done">The producer resolves one immutable commit OID, reads every identity/file
  from that OID, and materializes it offline from the local Git object store.
  Raw `git archive` supplies committed bytes only; a deterministic ZIP encoder
  normalizes ordering, timestamp and modes. The nested source ZIP uses STORED
  entries so its bytes have no architecture-dependent compressor input; the
  outer platform ZIP still applies DEFLATE for transport. A fail-closed
  portability gate rejects tracked paths that cannot have one identity on all
  four targets (including non-ASCII/case/reserved-name collisions), and the
  same 200,000-file / 400,000-node / 256-component / 4096-byte path limits are
  enforced before emission and extraction. Optional gates run
  in a disposable source tree and separate Cargo target cache; release
  compilation starts from a fresh expansion. The user's checkout, untracked
  files, and caches are never cleaned, reset, or copied.</fact></item>
        <item><fact id="RELEASE-SOURCE-ARCHIVE" status="impl/done">Every platform ZIP embeds the same byte-identical
  `vibevm-source.zip`, its source tree OID and SHA-256, plus `vibe`,
  `vibe-index`, `DISTRIBUTION.json`, `LICENSE.md`, and `README.md`. The raw
  bootstrap asset is byte-identical to the bundle's `vibe` component.</fact></item>
        <item><fact id="RELEASE-MUTABLE" status="impl/done">**Every version label is mutable by default.** `dist prepare --version V`
  deletes any existing draft or published release for `V`, moves or creates
  `vV` at the exact source commit, and creates one fresh draft. Four hosts may
  independently replace their ZIP/bootstrap/fragment triplets. The exact
  direct `refs/tags/vV` → commit binding is verified after prepare and again
  immediately before publication. `dist finalize`
  requires and remotely verifies all twelve platform assets, publishes
  `DISTRIBUTIONS.json` + both installers, then publishes with GitHub
  `make_latest=legacy` so rebuilding an older SemVer cannot demote a newer one.</fact></item>
        <item><fact id="RELEASE-SECRETS" status="impl/done">Compilation, build scripts, tests, self-check, Git, and built-binary probes
  receive no publish credential. Host wrappers disable command tracing before
  retaining an upload credential. CI builds `xtask` without credentials and
  runs that already-built trusted binary only in narrow prepare/upload/finalize
  steps carrying `GITHUB_TOKEN`.</fact></item>
      </list>
    </section>
  </section>
  <section id="architecture" title="3. Architecture — seams and cells">
    <p p="72"><fact id="req-architecture" status="spec/done">`req r2`</fact></p>
    <p p="73"><fact id="SEAMS-LEAD" status="spec/done">VVM is built from testable seams so the slow, machine-mutating parts are
mockable and unit tests never clone, build, or edit the real environment:</fact></p>
    <list ordered="false" p="74">
      <item><fact id="SEAM-VERSION-STORE" status="spec/done">`VersionStore` — the install-root layout (§2.4), instances, `current`,
  `state.toml`, manifests.</fact></item>
      <item><fact id="SEAM-SOURCE-PROVIDER" status="spec/done">`SourceProvider` — git: resolve a selector to a commit; clone/update a
  managed source; record external provenance.</fact></item>
      <item><fact id="SEAM-BUILDER" status="spec/done">`Builder` — runs `cargo` for a profile/toolchain; mocked in tests.</fact></item>
      <item><fact id="SEAM-PLACER" status="spec/done">`Placer` — the diff-copy of a distribution into a new instance (§2.15).</fact></item>
      <item><fact id="SEAM-ENV-PERSISTER" status="spec/done">`EnvPersister` — the durable `PATH`/env edits (§2.6), injectable.</fact></item>
      <item><fact id="SEAM-BUNDLE" status="impl/done">`release_manifest` + the VVM bundle reader — strict aggregate/fragment/bundle
  wire contracts, bounded anonymous download, safe ZIP extraction, and
  byte-level reusable-instance verification.</fact></item>
      <item><fact id="SEAM-TOOL-DOCTOR" status="spec/done">`ToolDoctor` — the `REQUIRED_TOOLS` table and checks (§2.8).</fact></item>
      <item><fact id="SEAM-VARS" status="spec/done">`vars` — the actual-vs-environment resolver (§2.14), `current_exe`-aware.</fact></item>
    </list>
    <list ordered="false" p="75">
      <item><fact id="RESOLVE-ORDER" status="spec/done">A managed `vibe` resolves its root/active from `current_exe` + the `current`
  file; env is the fallback.</fact></item>
      <item><fact id="MODULE-PLACEMENT" status="spec/done">The command lives as `cli/vvm.rs` + `cli` for
  `vibe vars`, with logic under `commands/vvm/` (split across module-grain
  files to hold the file-length budget). conform and specmap stay green.</fact></item>
    </list>
  </section>
  <section id="mvp" title="4. MVP scope">
    <list ordered="false" p="76">
      <item><fact id="MVP-VERBS" status="impl/done">The full verb set on all three platforms: `self install` (external in-place +
  managed clone paths, debug + release, diff-copy into instances), `self use`
  (live `current`, no reload), `self ls`/`current`/`which`, `self remove`
  (safe + `--all`), `self gc` (build cache + prune), `self doctor` (+ `--fix`),
  `self env`, `self relocate` (§2.17), `self import` (legacy local ready-built
  payload), `self bootstrap` (native bundle), `self rollback`, `self source`,
  component-aware `self which`, and `vibe vars`.</fact></item>
      <item><fact id="MVP-RESOLUTION-ENV" status="spec/done">Selector resolution per §2.3; durable
  PATH/advisory-env per §2.6 across Windows (cmd/PowerShell/Git Bash), macOS
  (zsh/bash), Linux (bash/zsh/fish).</fact></item>
      <item><fact id="MVP-DIFF-COPY" status="spec/done">diff-copy with hardlink sharing is in scope (§2.15).</fact></item>
      <item><fact id="MVP-LINKED-SOURCES" status="spec/done">Linked sources (§2.16) are in scope (the `source_path`
  record + rebuild-from-remembered).</fact></item>
    </list>
  </section>
  <section id="out-of-scope" title="5. Out of scope (now)">
    <list ordered="false" p="77">
      <item><fact id="OOS-BINARY" status="impl/done">**Retired from out-of-scope:** fetched, publisher-digest-bound native bundles
  ship through `self bootstrap`, binary `self update`, and explicit SemVer
  `self install` (§2.7, §2.12).</fact></item>
      <item><fact id="OOS-OFFLINE" status="spec/done">offline / vendored builds;</fact></item>
      <item><fact id="OOS-SIGNATURES" status="spec/done">cryptographic signature verification;</fact></item>
      <item><fact id="OOS-REFLINK" status="spec/done">reflink/CoW placement (hardlink is the portable choice).</fact></item>
      <item><fact id="oos-pointer" status="spec/done">These are §6.</fact></item>
    </list>
  </section>
  <section id="far-backlog" title="6. Far backlog">
    <list ordered="false" p="78">
      <item><fact id="FB-BINARY-INSTALL" status="impl/done">**Shipped, no longer backlog:** binary refresh/install is origin-directed
  rather than a separate `--binary` switch; remote labels stay mutable and
  local counter instances immutable.</fact></item>
      <item><fact id="FB-BOOTSTRAP-SCRIPT" status="impl/done">**Shipped, no longer backlog:** bounded Bash and PowerShell one-line
  bootstrap assets (§2.12).</fact></item>
      <item><fact id="FB-OFFLINE" status="spec/done">Offline builds via vendoring or a registry mirror.</fact></item>
      <item><fact id="FB-REFLINK" status="spec/done">Reflink/CoW placement where the filesystem supports it (§2.15).</fact></item>
      <item><fact id="FB-SIGNATURES" status="spec/done">Signature/provenance verification of the resolved ref.</fact></item>
    </list>
  </section>
  <section id="maintenance" title="7. Maintenance &amp; evolution — updating the stack">
    <p p="79"><fact id="req-maintenance" status="spec/done">`req r1`</fact></p>
    <p p="80"><fact id="runnable-lead" status="spec/done">Knowledge is runnable, so updates are mechanical:</fact></p>
    <list ordered="false" p="81">
      <item><fact id="MAINT-TOOLS-TABLE" status="spec/done">the **required tools** are the `REQUIRED_TOOLS` table (§2.8, asserted by a
  test);</fact></item>
      <item><fact id="MAINT-PROFILE-CONST" status="spec/done">the **default profile** is one constant (§2.2);</fact></item>
      <item><fact id="MAINT-RUST-PIN" status="spec/done">the **Rust pin** is `rust-toolchain.toml` (read, not hard-coded);</fact></item>
      <item><fact id="MAINT-MIRRORS" status="spec/done">the **clone mirrors** are PROP-016's `mirrors.toml`.</fact></item>
    </list>
  </section>
  <section id="acceptance" title="8. Acceptance">
    <p p="82"><fact id="req-acceptance" status="spec/done">`req r2`</fact></p>
    <list ordered="false" p="83">
      <item><fact id="ACC-FRESH-CLONE" status="impl/done">From a fresh clone, the source lane builds both essential binaries. From a
  clean machine, the Bash/PowerShell bootstrap installs the verified native
  pair plus source and durably exposes stable shims; output states whether the
  current shell already sees PATH.</fact></item>
      <item><fact id="ACC-USE-NO-RELOAD" status="spec/done">`self use` switches the active version and the **next** `vibe` in the
  **same shell** is the new one — no reload (`current` file).</fact></item>
      <item><fact id="ACC-NO-INUSE-REPLACE" status="spec/done">Reinstalling the running version replaces no in-use file (new instance +
  pointer flip); the running process is unharmed.</fact></item>
      <item><fact id="ACC-DIFF-COPY" status="impl/done">A source-built distribution where only one essential binary changed copies
  that file; unchanged source-build members may be hardlinked. The binary
  lane instead verifies every bounded payload member and source before reuse.</fact></item>
      <item><fact id="ACC-VARS" status="spec/done">`vibe vars` reports actual vs environment; `vibe vars diff`/`full`/`full
  diff` per §2.14; the publish token never appears.</fact></item>
      <item><fact id="ACC-REMOVE-GC-SAFE" status="spec/done">`self remove` never wipes without `--all` + reconfirm; `self gc` never
  touches `~/.cargo`; external sources are never modified or removed.</fact></item>
      <item><fact id="ACC-RELOCATE" status="spec/done">`self relocate &lt;new&gt;` repoints external `source_path` records and removes the
  stale instance dirs built from the old tree, keeping the active instance; the
  active's source is repointed, not deleted. `--dry-run` changes nothing; a
  non-TTY run without `--yes` errors.</fact></item>
      <item><fact id="ACC-IMPORT" status="impl/done">`self import` is inactive by default (`--use` alone activates);
  importing the same tag with the same payload SHA-256 reuses the instance and
  allocates nothing; a different payload under the same mutable tag creates a
  new `#N` and preserves the previous instance; `--replace-candidate` remains
  compatibility spelling.</fact></item>
      <item><fact id="ACC-BINARY-BUNDLE" status="impl/done">A native bootstrap installs both essential binaries and the matching exact
  source tree under one immutable instance; `self source` returns its absolute
  path, normal same-digest refresh is a verified no-op, `--force` allocates the
  next `#N`, and rollback restores the previous generation.</fact></item>
      <item><fact id="ACC-RUNNING-PROVENANCE" status="impl/done">With two source worktrees and one binary install, `self ls/current/which`
  distinguish active `*` from running `&gt;`; a directly executed worktree
  reports its own executable/root/commit and never borrows cwd or active
  binary provenance.</fact></item>
      <item><fact id="ACC-RELEASE-CONTOUR" status="impl/done">Each fixed host wrapper defaults to build-only/no tests; explicit gates are
  opt-in. All four fragments must bind the same commit/tree/source archive
  before mutable release finalization; build subprocesses receive no publish
  credentials.</fact></item>
      <item><fact id="ACC-FLOOR-GREEN" status="spec/done">Full `self-check.sh` green; conform 0/0/0; specmap clean.</fact></item>
    </list>
  </section>
  <section id="rationale" title="9. Design rationale &amp; questions explored">
    <p p="84"><fact id="rationale-lead" status="spec/done">The decisions above were reached by working through several sharp questions;
recording them so a cold reader sees *why*, not just *what*.</fact></p>
    <section id="rationale-truth" title="9.1 Why `current` file + `current_exe`, not `$VIBEVM_HOME` (v1)">
      <list ordered="false" p="85">
        <item><fact id="V1-ENV-TRUTH" status="spec/done">v1 made `$VIBEVM_HOME` the single source of truth for the active version.
  Environment variables are inherited at process start, so a shell's
  `$VIBEVM_HOME` is frozen until the shell is reloaded — every `self use`/
  reinstall forced "open a new terminal".</fact></item>
        <item><fact id="FIX-LIVE-FILE" status="spec/done">The fix: the **shim reads a live `current` file** each launch (filesystem
  is live → instant switch in the same shell).</fact></item>
        <item><fact id="FIX-CURRENT-EXE" status="spec/done">A running `vibe` derives its own identity from **`current_exe()`** (it *is*
  the binary, so it knows its path).</fact></item>
        <item><fact id="ENV-DEMOTED" status="spec/done">`$VIBEVM_HOME` stays only as an advisory/compat env for external tools,
  reconciled by `vibe vars` (§2.14) and a startup divergence warning.</fact></item>
        <item><fact id="V1-REVERSED" status="spec/done">This reverses v1's decision deliberately; env-as-truth was the cause of the
  reload friction.</fact></item>
      </list>
    </section>
    <section id="rationale-no-hash" title="9.2 Why not content-hash the distribution">
      <list ordered="false" p="86">
        <item><fact id="HASH-NATURAL" status="spec/done">A natural instance key is a content hash of the built distribution
  (dedup + self-describing).</fact></item>
        <item><fact id="HASH-DOESNT-SCALE" status="spec/done">It does not scale: a distribution may grow to gigabytes and ship as
  binaries (merged projects), and hashing 2 GB+ on every install would be
  prohibitive.</fact></item>
        <item><fact id="KEY-NEVER-READS" status="spec/done">So the instance key never reads the payload (§9.4), and change detection
  for diff-copy hashes only **small** files, trusting `(size, mtime)` for
  large ones (§2.15).</fact></item>
        <item><fact id="BINARY-PUBLISHER-DIGEST" status="impl/done">Fetched binary artifacts are keyed by the **publisher's** digest
  (computed once at publish), verified during download/extraction, and their
  installed manifest, binaries, retained source ZIP and extracted source are
  rechecked before reuse. The local `self
  import` path is deliberately outside this rule: it has no publisher, so it
  hashes the handed file once on entry — that digest is its identity, not a
  verification of anyone's claim.</fact></item>
      </list>
    </section>
    <section id="rationale-instances" title="9.3 Why whole-directory instances, not in-place file replace">
      <list ordered="false" p="87">
        <item><fact id="RENAME-ASIDE-IDEA" status="spec/done">The first idea for "reinstall over the running binary" was the Windows
  *rename-aside* trick (rename the running `.exe`, write the new one;
  empirically verified to work).</fact></item>
        <item><fact id="ONE-FILE-LIMIT" status="spec/done">It handles one file; a distribution is many (exe + DLLs + assets), all
  locked while running.</fact></item>
        <item><fact id="WHOLE-DIR-UNIT" status="spec/done">So the unit of install and switch became the **whole immutable instance
  directory**: each install writes a *new* dir and switching is a pointer
  flip, so **nothing in use is ever overwritten** — no lock for any file on
  any OS, and no reload.</fact></item>
        <item><fact id="RENAME-ASIDE-DROPPED" status="spec/done">rename-aside was dropped as unnecessary.</fact></item>
      </list>
    </section>
    <section id="rationale-counter" title="9.4 Why a monotonic counter for the instance key">
      <list ordered="false" p="88">
        <item><fact id="COUNTER-KEY" status="spec/done">With content-hash rejected (§9.2), the instance key is a monotonic counter:
  always unique, O(1), independent of distribution size.</fact></item>
        <item><fact id="CHANGE-VIA-MANIFEST" status="spec/done">"Did anything change" is answered cheaply by the diff-copy manifest
  (§2.15), which also yields the *dedup-skip* (no new instance when every
  file is unchanged) without hashing the payload. `--force` bypasses the
  skip.</fact></item>
      </list>
    </section>
    <section id="rationale-prune" title="9.5 Why explicit prune preserves rollback">
      <list ordered="false" p="89">
        <item><fact id="AUTO-PRUNE-BINARY-ONLY" status="impl/done">Install never silently prunes either origin: a same-label previous payload
  is the rollback evidence for mutable releases. Disk reclamation is an
  explicit `remove` / `gc --prune-others` decision.</fact></item>
        <item><fact id="SOURCE-KEEPS" status="impl/done">Pruning preserves the active and immediate previous instances. Source builds
  retain referenced checkout context; binary instances retain their exact
  source snapshot until explicitly pruned.</fact></item>
      </list>
    </section>
    <section id="rationale-sources" title="9.6 Why developer sources are referenced but release source is copied">
      <list ordered="false" p="90">
        <item><fact id="COPY-UNTENABLE" status="spec/done">Copying a checkout into the install root is untenable — a working tree's
  cargo `target/` is already tens of GB.</fact></item>
        <item><fact id="MANAGED-INCREMENTAL" status="spec/done">So managed sources are git clones VVM updates incrementally, and external
  (committer) sources are referenced by their absolute path and built **in
  place**, never touched.</fact></item>
        <item><fact id="LINKED-BONUS" status="spec/done">This also gives *linked sources* (§2.16): rebuild from a remembered
  location without being in it.</fact></item>
        <item><fact id="DIST-COPIED-NOT-SOURCE" status="impl/done">A developer working tree is never copied because its ignored build/cache
  surface may be enormous. A binary release deliberately embeds only the
  committed clean tree—normalized offline from one Git OID—because skills may
  need source and no external checkout exists. Its installed `source/` is part
  of the immutable distribution, not a development clone.</fact></item>
      </list>
    </section>
    <section id="rationale-warning" title="9.7 Why the binary, not the shim, emits the divergence warning">
      <list ordered="false" p="91">
        <item><fact id="BINARY-EMITS-WARNING" status="spec/done">When `current` and `$VIBEVM_HOME` disagree, the warning is emitted by the
  `vibe` **binary** at startup, not by the sh/cmd shim: the binary has
  `current_exe` ground truth and the `vibe vars` formatter, and keeps the
  shims trivial.</fact></item>
        <item><fact id="WARNING-SUPPRESSED" status="spec/done">The warning is suppressed outside a managed run (a dev `cargo run` has no
  managed location and should not be nagged).</fact></item>
      </list>
    </section>
  </section>
</spec>
