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;`); });