<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="root">PROP-025 — vibe-native binary delivery</title>
  <status stage="impl" state="done" comment="B0 2026-07-24: v1 IMPLEMENTED SS2-5 (deferrals-closeout campaign); S6-S7 specified v2 surface; fact grain 2026-07-24"/>
  <p p="1"><fact id="status-line" status="impl/done">**Status: v1 IMPLEMENTED (§§2–5; the deferrals-closeout campaign). §6–§7 are
specified v2 surface.** Module: `vibe-workspace` / `vibe-install` / `vibe-cli`.</fact></p>
  <section id="problem" title="1. Problem">
    <p p="2"><fact id="req-problem" status="impl/done">`req r1`</fact></p>
    <list ordered="false" p="3">
      <item><fact id="packages-ship-tools" status="impl/done">Code-bearing packages (PROP-024) ship runnable tools — the discipline
  stacks alone ship the umbrella + gate binaries (`rust-ai-native`,
  `rust-ai-native-conform`, `rust-ai-native-specmap`, `typescript-ai-native`,
  `typescript-ai-native-conform`, `typescript-ai-native-specmap`) — but
  `vibe install` stops at materialising source
  into `vibedeps/`.</fact></item>
      <item><fact id="manual-path-step" status="impl/done">Getting from a slot to a tool on PATH is a manual,
  documented step (`cargo install --path vibedeps/&lt;slot&gt;/crates/&lt;cli&gt;`,
  GUIDE §13), repeated per machine and re-repeated per version bump: n
  stacks × m tools of PATH management that vibe already knows how to do for
  itself (PROP-019).</fact></item>
      <item><fact id="BINARY-MUST" status="impl/done">MUST: a package declares its binaries; vibe builds and
  dispatches them.</fact></item>
    </list>
  </section>
  <section id="manifest" title="2. Manifest surface">
    <p p="4"><fact id="req-manifest" status="impl/done">`req r1`</fact></p>
    <p p="5"><fact id="BINARY-TABLE" status="impl/done" action="continue" actionstage="doc" audience="author">A code-bearing package declares each shipped tool in its `vibe.toml`:</fact></p>
    <fence lang="toml" p="6">[[binary]]
name = "rust-ai-native"             # the PATH-facing name (the family
                                    # prefix keeps it collision-safe)
crate = "crates/rust-ai-native-cli" # package-relative crate directory</fence>
    <list ordered="false" p="7">
      <item><fact id="NAME-CONSTRAINTS" status="impl/done" action="continue" actionstage="doc" audience="author">Constraints: `name` MUST be unique within the package and SHOULD be
  globally collision-safe (the family-prefix convention, PROP-028 §2.4).</fact></item>
      <item><fact id="CRATE-CONSTRAINT" status="impl/done" action="continue" actionstage="doc" audience="author">`crate` MUST name a directory inside the shippable tree carrying a
  `[[bin]]`-bearing (or default-bin) Cargo package whose bin name equals
  `name`.</fact></item>
      <item><fact id="LINTER-VALIDATES" status="impl/done">The linter (`vibe check`) validates both.</fact></item>
      <item><fact id="OPT-IN" status="impl/done">Absent `[[binary]]`
  tables mean the package ships no tools — every field of this PROP is
  opt-in.</fact></item>
      <item><fact id="BINARY-MECHANISM-LOWERING" status="impl/done">The lifecycle successor lowers each `[[binary]]` compatibly into the shared artifact DAG and exact `build:cargo` mechanism selection; it does not create a second Cargo runner. Cargo metadata and compiler-artifact JSON messages select the executable, and the ordinary artifact record binds the declared binary, provider, config and output digest. Evidence: `a22da2a3`.</fact></item>
    </list>
  </section>
  <section id="build" title="3. Install-time build">
    <p p="8"><fact id="req-build" status="impl/done">`req r1`</fact></p>
    <list ordered="false" p="9">
      <item><fact id="BUILD-TIMING" status="impl/done" action="continue" actionstage="doc" audience="user">After materialising a slot whose manifest declares `[[binary]]` entries,
  `vibe install` MAY build them (v1: on `vibe bin sync`, see §4 — the
  install itself only RECORDS the declarations; an install-time
  `--build-bins` opt-in flag is v2 surface).</fact></item>
      <item><fact id="BUILD-CONSENT" status="impl/done" action="continue" actionstage="doc" audience="user">**Successor to the historical first-build prompt.** Building executes package build scripts and proc-macros, so installation plus explicit target/route selection is the authorisation and the engine narrates the exact provider and target before execution. Lifecycle build does not add an allow-list, first-run prompt or `--allow-hooks` analogue; provider identity, artifact records and outcomes supply the audit trail. Existing direct `vibe bin` compatibility remains routed through its declared package rather than silently choosing ambient code.</fact></item>
    </list>
    <list ordered="false" p="10">
      <item><fact id="SLOT-RESIDENT" status="impl/done">Artifacts are **provider-root resident**: the builtin Cargo mechanism runs under the exact materialised dependency slot or authored package root and uses that workspace's gitignored `target/`. The shared artifact record, not a guessed `target/release/&lt;name&gt;`, names the selected output.</fact></item>
      <item><fact id="HASHES-STABLE" status="impl/done">Build output sits outside the shippable tree (PROP-024 §2.2), so content
  hashes never move.</fact></item>
      <item><fact id="REFRESH-INVALIDATES" status="impl/done">A same-slot record-aware refresh preserves unrecorded `target/` output and unchanged source mtimes, so Cargo invalidates only affected fingerprints. A version change selects a different current slot, and artifact-record/provider/source/config mismatch refuses or rebuilds rather than trusting an old output.</fact></item>
    </list>
  </section>
  <section id="dispatch" title="4. The `vibe bin` family and dispatch">
    <p p="11"><fact id="req-dispatch" status="impl/done">`req r1`</fact></p>
    <list ordered="false" p="12">
      <item><fact id="BIN-LIST" status="impl/done">`vibe bin list` — every `[[binary]]` declared by the project's
  installed packages, with build state (built / not built) and the
  artifact path.</fact></item>
      <item><fact id="BIN-BUILD" status="impl/done" action="continue" actionstage="doc" audience="user">`vibe bin build [&lt;name&gt;…]` — release build of the named tools (default: all declared) through their exact installed package and Cargo declaration; installation is the consent and the selected work is narrated.</fact></item>
      <item><fact id="BIN-PATH" status="impl/done">`vibe bin path &lt;name&gt;` — the artifact path (non-zero when not built);
  scripts compose with it.</fact></item>
      <item><fact id="BIN-EXEC" status="impl/done" action="continue" actionstage="doc" audience="user">`vibe bin exec &lt;name&gt; [--] &lt;args…&gt;` — resolve `name` through the
  CURRENT project's lockfile → its slot → the slot-resident artifact
  (building the declared package target if absent), then execute with the exit
  code passed through. This is the rustup dispatch model: the project's
  pinned version is what runs, always.</fact></item>
    </list>
    <list ordered="false" p="13">
      <item><fact id="SHIMS-SPEC" status="spec/done">Shims (`vibe bin sync`) are v1.5 surface, specified here: a per-user bin
  dir (reconciled with PROP-019's shim dir — one PATH entry, not two) of
  dumb launchers, each `exec`-ing `vibe bin exec &lt;name&gt; -- %*` / `"$@"`
  (Windows `.cmd` + POSIX sh pair; the `cmd /c` spawn lesson of PROP-015
  applies).</fact></item>
      <item><fact id="LAUNCHER-VERSION-FREE" status="spec/done">A launcher never encodes a version — dispatch stays per-CWD
  through the lockfile walk, so two projects pinning different stack
  versions get different binaries from the same PATH entry.</fact></item>
    </list>
  </section>
  <section id="staleness" title="5. Staleness and offline honesty">
    <p p="14"><fact id="req-staleness" status="impl/done">`req r1`</fact></p>
    <list ordered="false" p="15">
      <item><fact id="TRUST-CURRENT-SLOT" status="impl/done">Current-slot existence alone is not trust. The selected artifact must belong to the current resolved provider root and pass the shared record's exact source/config/platform/provider/output-path/digest revalidation. A same-slot refresh preserves unrecorded build output; a version change moves current-root identity and cannot reuse the old slot's record.</fact></item>
      <item><fact id="WARM-NOOP" status="impl/done">`vibe bin build` on a warm artifact is a cargo
  no-op (~seconds).</fact></item>
      <item><fact id="OFFLINE-HONESTY" status="impl/done" action="continue" actionstage="doc" audience="user">Cargo needs crates.io for third-party deps unless the
  local cargo cache is warm: offline boxes get the same honest failure
  cargo gives, plus the hint that `cargo install --path &lt;slot&gt;/crates/…`
  (the documented degraded path, which stays valid indefinitely) has the
  same network shape — there is no offline shortcut to a first build.</fact></item>
    </list>
  </section>
  <section id="cross-package" title="6. Cross-package path dependencies (v2, specified only)">
    <p p="16"><fact id="req-cross-package" status="spec/done">`req r1`</fact></p>
    <list ordered="false" p="17">
      <item><fact id="CROSS-DEP-IMPOSSIBLE" status="impl/done">A stack crate cannot Cargo-`path`-dep on a core-ai-native crate across
  slots: the authored layout (`packages/org.vibevm/&lt;name&gt;/v&lt;ver&gt;/`) and the
  materialised layout (`vibedeps/&lt;group&gt;.&lt;name&gt;/&lt;ver&gt;/`) disagree on both
  directory naming and version prefix, and each slot must stay a
  self-buildable workspace (PROP-024 §2.4).</fact></item>
      <item><fact id="V1-VENDOR-SYNC" status="impl/done">v1 answers this with
  vendor-sync (byte-identical copies under `crates/vendor/`, gated by
  `sync-engines --check`).</fact></item>
      <item><fact id="V2-REWRITE-ALTERNATIVE" status="spec/done">The v2 alternative — vibe REWRITING declared
  cross-package path-deps at materialise time (a `[binary.cross_paths]`
  manifest table mapping dep names to `&lt;pkgref&gt;:&lt;crate-path&gt;`) — interacts
  with shippable-tree hashing (the rewritten manifest must be excluded from
  the hash exactly like build output, or identity breaks) and with
  presence-trust.</fact></item>
      <item><fact id="V2-TRADE-VISIBLE" status="spec/done">It lands only with its own campaign and only if
  vendor-sync's duplication cost ever exceeds the rewrite machinery's
  complexity cost. Recorded so the trade stays visible.</fact></item>
    </list>
  </section>
  <section id="gc" title="7. Uninstall and GC (v2, specified only)">
    <p p="18"><fact id="req-gc" status="spec/done">`req r1`</fact></p>
    <list ordered="false" p="19">
      <item><fact id="UNINSTALL-REMOVES" status="impl/done">`vibe uninstall` of a package removes its slot and therefore its
  artifacts (nothing else to clean in v1 — artifacts are slot-resident and
  launchers are version-free).</fact></item>
      <item><fact id="V2-SHIM-GC" status="spec/done">v2 shim GC: `vibe bin sync` removes
  launchers whose names no installed package declares.</fact></item>
      <item><fact id="V2-VARS-ROW" status="spec/done">`vibe vars` learns
  the bin-dir row when shims land.</fact></item>
    </list>
  </section>
  <section id="security" title="8. Security posture">
    <p p="20"><fact id="req-security" status="impl/done">`req r1`</fact></p>
    <list ordered="false" p="21">
      <item><fact id="CONSENT-MODEL" status="impl/done">An install-time / exec-time build runs third-party build scripts. The current lifecycle successor treats package installation plus explicit target/route selection as authorisation, with exact provider/target narration and durable artifact evidence; the former group allow-list and first-build prompt are historical, not a second current gate.</fact></item>
      <item><fact id="NO-PATH-HIJACK" status="impl/done">`vibe bin exec` never searches PATH for the tool it
  dispatches (no hijack surface): resolution is lockfile → slot → artifact,
  all project-local.</fact></item>
      <item><fact id="SCOPE-UNCHANGED" status="impl/done">Scope discipline (PROP-002 §2.10) applies unchanged.</fact></item>
    </list>
  </section>
  <section id="v1-cut" title="9. The v1 cut">
    <p p="22"><fact id="req-v1-cut" status="impl/done">`req r1`</fact></p>
    <list ordered="false" p="23">
      <item><fact id="V1-IMPLEMENTED" status="impl/done">Implemented in the original campaign: §2 manifest parsing + linting, §3 slot builds, §4 `vibe bin list/build/path/exec`, and §5 slot-root dispatch. The lifecycle successor adds exact mechanism selection, Cargo-message artifact discovery and record revalidation without removing those compatibility verbs.</fact></item>
      <item><fact id="V1-DEFERRED" status="impl/done">Deferred with names: §4 shims (`vibe bin sync` — lands
  with a PROP-019 shim-dir reconciliation pass), §6 cross-package path
  rewriting, §7 GC + `vibe vars` row.</fact></item>
    </list>
  </section>
  <section id="history" title="History">
    <list ordered="false" p="24">
      <item><fact id="HIST-AUTHORED" status="impl/done">2026-07-07 — authored and v1-implemented in the deferrals-closeout
  campaign; supersedes the "future PROP" note the Self-Sufficiency
  campaign's §10 recorded.</fact></item>
    </list>
  </section>
</spec>
