diff --git a/bin/gstack-capy-install b/bin/gstack-capy-install
index 28b30a0c1..83e21e8c2 100755
--- a/bin/gstack-capy-install
+++ b/bin/gstack-capy-install
@@ -233,7 +233,7 @@ else
[ "$PINS_MODE" = parser ] || read_pins "$STORE/$WANT/bun"
resolve || conflict "bun $WANT in $STORE does not satisfy $PROJECT's pins $(pins_text) (judged: ${_gb_judged:-none})"
fi
- [ -n "${GSTACK_BUN_INSTALL_VERSION:-}" ] && say "install version $INSTALL_VERSION (GSTACK_BUN_INSTALL_VERSION; default $GSTACK_BUN_DEFAULT)"
+ [ -n "${GSTACK_BUN_INSTALL_VERSION:-}" ] && say "install version $INSTALL_VERSION from GSTACK_BUN_INSTALL_VERSION (default $GSTACK_BUN_DEFAULT)"
say "bun $RES_VER at $RES_BIN ($(gstack_bun_rule_text "$RES_RULE"); satisfies gstack's supported minimum $GSTACK_BUN_TESTED${PIN_EXACT:+ and the project pin $PIN_EXACT}$([ -z "$PIN_EXACT" ] && [ -n "$PIN" ] && printf ' and the project pin %s' "$PIN")$([ -z "$PIN_EXACT" ] && [ "$PINS_MODE" = parser ] && printf ' and the project pins'))"
fi
if [ -n "$RES_BIN" ] && [ "$(gstack_realpath "$(command -v bun 2>/dev/null || echo /nonexistent)" 2>/dev/null)" != "$RES_BIN" ]; then
diff --git a/bin/gstack-doctor-check.sh b/bin/gstack-doctor-check.sh
index b22e477f7..140cae2ea 100644
--- a/bin/gstack-doctor-check.sh
+++ b/bin/gstack-doctor-check.sh
@@ -274,7 +274,7 @@ _dc_check_runtime() {
found=$(bun --version /dev/null | clamp 'A-Za-z0-9.+-' 40)
_dc_bun="$found"
extra="$(_dc_shim_note)"
- [ -n "${GSTACK_BUN_INSTALL_VERSION:-}" ] && extra="$extra; install version $GSTACK_BUN_INSTALL_VERSION (GSTACK_BUN_INSTALL_VERSION)"
+ [ -n "${GSTACK_BUN_INSTALL_VERSION:-}" ] && extra="$extra; install version $GSTACK_BUN_INSTALL_VERSION from GSTACK_BUN_INSTALL_VERSION"
case "$(gstack_bun_status "$found")" in
ok) _dc_row PASS runtime "bun $found at $where (floor $GSTACK_BUN_FLOOR, supported minimum $GSTACK_BUN_TESTED)$extra" ;;
untested) _dc_row FAIL runtime "bun $found at $where is below the supported minimum $GSTACK_BUN_TESTED (above the security floor $GSTACK_BUN_FLOOR)$_dc_ln_note$extra" "$fix" RUNTIME_BELOW_MINIMUM ;;
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index f5073c73f..f89a8cd34 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -1243,15 +1243,60 @@ revision at or above the minimum. An interrupted install after the
fast-forward leaves the trailer `fail`; the next install run completes it.
-### `gstack-capy-skill: owner action — Drive copy is wrapper , minimum ; copy /capy/skill/gstack/ (CAPY_WRAPPER_STALE)`
+### `FAIL wrapper — ... (CAPY_WRAPPER_STALE)` / `WARN wrapper — ...; owner action: ...` / `gstack-capy-skill: owner action — Drive copy is wrapper , minimum ; ...`
-**Meaning.** The Capy skill folder that ran (the bootstrapper's
-`GSTACK_CAPY_WRAPPER`) is older than the checkout's `GSTACK_CAPY_MIN_WRAPPER`.
-The run continues through the checkout's own wrapper (`bin/gstack-capy-session`);
-only a checkout with no such wrapper refuses (`gstack: fail wrapper`).
+**Meaning.** The Capy skill folder is older than the checkout's
+`GSTACK_CAPY_MIN_WRAPPER` (`capy/support.env`). The doctor's `wrapper` row
+decides by the active wrapper's provenance (`GSTACK_CAPY_WRAPPER_REV` and
+`GSTACK_CAPY_WRAPPER_PATH`, exported by the bootstrapper): a current active
+wrapper passes, and a stale Drive copy elsewhere is a `WARN` row, an owner
+action that never fails the run. Without provenance (a direct
+`gstack-doctor --check`, or a pre-bootstrapper copy with no
+`GSTACK_CAPY_WRAPPER=` line, which reads as wrapper 0) it reads the Drive
+copies (`~/.capy/drive/*/skills/gstack/scripts/install.sh`, or only
+`GSTACK_CAPY_WRAPPER_PATH` when set) and fails only when every copy is stale,
+listing each with its revision. A stale active wrapper also fails.
-**Fix.** The owner copies `/capy/skill/gstack/` over the skill folder
-(on Capy, the user-scope Drive `skills/gstack/`). Agents never write it.
+**Fix.** This session: run the checkout's own copy,
+`bash /capy/skill/gstack/scripts/install.sh --check --for `;
+it carries current provenance, so it certifies with the stale Drive copy left
+unchanged. Owner: copy `/capy/skill/gstack/` over the Drive skill
+folder (on Capy, the user-scope Drive `skills/gstack/`), or have Capy re-sync
+it. The message recurs every session until the Drive copy changes. Agents never
+write the skill folder.
+
+
+### `FAIL runtime — bun resolution drifted: recorded bun at (), now ; changed: ; fix: ... (BUN_RESOLUTION_DRIFT)`
+
+**Meaning.** Install and `--check` resolve Bun through one contract
+(`gstack_bun_resolve` in `bin/gstack-bun-version.sh`, twinned by `resolveBun`
+in `lib/pregate/runtime.ts`): the project's exact pin and gstack's install
+default in the runtime store (`/runtimes/bun//bun`),
+`$BUN_INSTALL/bin`, `~/.bun/bin`, then PATH; the first Bun that satisfies
+gstack's minimum and every project pin wins. Install records the pick in the
+install verdict for this checkout and project (`bun_path`, `bun_version`,
+`bun_rule`). `--check` resolved a different Bun: the recorded binary was
+deleted or replaced, `BUN_INSTALL` or PATH changed, or a new candidate (a store
+Bun) now resolves first. The same code refuses a workflow start
+(`INSTALL: fail runtime — bun at is running, but the install
+verdict recorded ...`) when the Bun running is not the recorded one.
+
+**Fix.** Run the row's fix. When the current pick satisfies the pins it is the
+installer (it records the new resolution); otherwise it names the variable to
+restore (`export BUN_INSTALL=`), the directory to put back on PATH,
+or the installer that re-downloads the recorded version into the store. The
+next `--check` passes.
+
+
+### `gstack-capy-install: GSTACK_BUN_INSTALL_VERSION= is not a verified Bun at or above gstack's supported minimum ...; fix: ... (BUN_INSTALL_VERSION_INVALID)`
+
+**Meaning.** `GSTACK_BUN_INSTALL_VERSION` overrides the Bun the installer
+downloads when no exact project pin decides (`GSTACK_BUN_DEFAULT`, 1.4.3). It
+must be listed in `GSTACK_BUN_VERIFIED` (a SHA-256-verified archive) and sit at
+or above the supported minimum (`GSTACK_BUN_TESTED`). Nothing was installed.
+
+**Fix.** Unset the variable, or set it to one of the verified versions the
+message lists.
### `INSTALL: fail verdict — ; fix: ... (INSTALL_UNVERIFIED)`
diff --git a/test/gstack-capy-install.test.ts b/test/gstack-capy-install.test.ts
index 1cb5de374..f0386b656 100644
--- a/test/gstack-capy-install.test.ts
+++ b/test/gstack-capy-install.test.ts
@@ -481,7 +481,7 @@ describe.skipIf(process.platform !== 'linux')('gstack-capy-install: one resoluti
expect(f.calls().some(l => l.startsWith('curl ') || l.startsWith('setup '))).toBe(false);
const r = run(f, ['--for', 'autoplan'], { GSTACK_BUN_INSTALL_VERSION: '1.4.2', STUB_CURL_ZIP: zipFor(f, '1.4.2') });
expect(r.status, r.stdout + r.stderr).toBe(0);
- expect(r.stdout).toContain('install version 1.4.2 (GSTACK_BUN_INSTALL_VERSION; default 1.4.3)');
+ expect(r.stdout).toContain('install version 1.4.2 from GSTACK_BUN_INSTALL_VERSION (default 1.4.3)');
expect(r.stdout).toContain(`bun 1.4.2 at ${store(f, '1.4.2')}/bun (gstack's install default, from gstack's runtime store;`);
});