From b04cc35832183a89ed71eed628de15a6f315a1a8 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Thu, 22 Oct 2026 22:47:18 -0700 Subject: [PATCH] v1.91.182.0 fix(capy-install): verify Bun 1.4.4 archives so a 1.4.4 project pin installs instead of RUNTIME_PIN_CONFLICT (#3345) * fix(capy-install): verify Bun 1.4.4 archives so a 1.4.4 project pin installs instead of RUNTIME_PIN_CONFLICT Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * v1.91.182.0 stamp: fix(capy-install): verify Bun 1.4.4 archives (#3345) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- CHANGELOG.md | 8 ++++++++ VERSION | 2 +- agents-digest/gstack-AGENTS.md | 2 +- bin/gstack-bun-version.sh | 8 +++++++- docs/capy-install.md | 2 +- package.json | 2 +- test/bun-version-drift.test.ts | 7 +++++++ test/gstack-capy-install.test.ts | 17 +++++++++++++++-- test/gstack-doctor.test.ts | 2 +- 9 files changed, 42 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37ae50b89..e74a6d62b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.91.182.0] - 2026-10-23 + +**gstack installs on a project that pins Bun 1.4.4 again.** gbrain master moved its CI to Bun 1.4.4 (gbrain #7374, the Brotli CVE-2026-111497 fix), and gstack's Capy installer then stopped every gbrain session with `RUNTIME_PIN_CONFLICT` ("verified archives: 1.4.2 1.4.3 … pins exactly 1.4.4"), because it installs only Bun archives whose SHA-256 it carries. It now carries 1.4.4, so the same run installs the pinned Bun and ends `gstack: ok`. + +### Fixed +- **`bin/gstack-bun-version.sh`**: `GSTACK_BUN_VERIFIED` is now 1.4.2 1.4.3 1.4.4, with 1.4.4's checksums for linux-x64, linux-x64-baseline and linux-aarch64, the targets 1.4.3 covers. They come from the release's `SHASUMS256.txt.asc`, whose clearsigned signature verifies against Bun's release key F3DCC08A8572C0749B3E18888EAB4D40A7B22B59 (Robobun, the key oven-sh/bun's own Dockerfiles assert), and each archive was downloaded and hashed to the same value. The 1.4.2 and 1.4.3 entries are unchanged and verify the same way. gstack's own minimum (1.4.2), install default (1.4.3) and CI pins do not move. +- **`test/gstack-capy-install.test.ts`**: a gbrain-like repo whose CI pins only 1.4.4 refuses `~/.bun` 1.4.3 and installs the 1.4.4 archive. **`test/bun-version-drift.test.ts`**: every verified version must carry a checksum for each installer target. The existing "verified archives" text assertions list 1.4.4. + ## [1.91.181.0] - 2026-10-23 **/cso can run its contained runtime checks.** Before this release the runtime catalog qualified nothing, so /cso always fell back to static assessment. It now qualifies ten pinned images: Node 24.4.0, Bun 1.3.10, Python 3.13.4 with uv 0.8.0, Rails on Ruby 3.4.4, and PostgreSQL 17.2, each for linux/amd64 and linux/arm64. With Docker installed, comprehensive /cso boots the target app inside the matching image and tests findings and repairs against it. The images passed the private evaluator's release qualification (run-5 statements, re-delivered under Garry's approval). The ingress then checked them in qualify run 39674065531 at 514fbfd. diff --git a/VERSION b/VERSION index 1662717f2..1f6481848 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.91.181.0 +1.91.182.0 diff --git a/agents-digest/gstack-AGENTS.md b/agents-digest/gstack-AGENTS.md index 14e8a9dc3..275434383 100644 --- a/agents-digest/gstack-AGENTS.md +++ b/agents-digest/gstack-AGENTS.md @@ -1,4 +1,4 @@ -# gstack digest v1.91.181.0 — regenerate/re-copy after upgrading gstack +# gstack digest v1.91.182.0 — regenerate/re-copy after upgrading gstack Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed for agent hosts without a full skill install. The full skills add workflows, diff --git a/bin/gstack-bun-version.sh b/bin/gstack-bun-version.sh index e4a6d28fd..9dda4a073 100644 --- a/bin/gstack-bun-version.sh +++ b/bin/gstack-bun-version.sh @@ -28,7 +28,7 @@ GSTACK_BUN_DEFAULT="1.4.3" # test/bun-version-drift.test.ts keeps the $GSTACK_BUN_TESTED rows equal). A # newer version is listed so the installer can follow a project's exact pin # above the minimum. Prints nothing and returns 1 without a verified archive. -GSTACK_BUN_VERIFIED="1.4.2 1.4.3" +GSTACK_BUN_VERIFIED="1.4.2 1.4.3 1.4.4" gstack_bun_archive_sha256() { case "${2:-$GSTACK_BUN_TESTED}" in 1.4.2) case "$1" in @@ -43,6 +43,12 @@ gstack_bun_archive_sha256() { linux-aarch64) echo efa9813da5ed72423bf847f916e8d2c47c0d776add972354026a75e10da9aa21 ;; *) return 1 ;; esac ;; + 1.4.4) case "$1" in + linux-x64) echo bf8ccbfb2b0b80159e0f9a6d15c55317add8077534dbc6130a3edb52fccb395c ;; + linux-x64-baseline) echo e0446eb55995772eb9894bebae9a6be368d985a8b9271ee124486f9946eedd76 ;; + linux-aarch64) echo baa6bb36f51d6e58561e02bbea825038a9df454f673d6d776e8bbf86f8c7850e ;; + *) return 1 ;; + esac ;; *) return 1 ;; esac } diff --git a/docs/capy-install.md b/docs/capy-install.md index fb9e865cb..404f85b2f 100644 --- a/docs/capy-install.md +++ b/docs/capy-install.md @@ -158,7 +158,7 @@ session holds no lease) and a reclaim keeps the lease. `bin/gstack-lease list` s repaired without a window (its files are renamed over their predecessors), and `~/.bun/bin` is never written. The archive is the project's non-CI exact pin when gstack carries a verified archive for it - (`GSTACK_BUN_VERIFIED`: 1.4.2, 1.4.3); with only CI exact pins, the install + (`GSTACK_BUN_VERIFIED`: 1.4.2, 1.4.3, 1.4.4); with only CI exact pins, the install default when it is one of them, else the highest verified one; else the install default `GSTACK_BUN_DEFAULT` (1.4.3, above the 1.4.2 minimum; one CI job runs the quick free tier on it). diff --git a/package.json b/package.json index bdf731bc3..86dbd66d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gstack", - "version": "1.91.181", + "version": "1.91.182", "description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.", "license": "MIT", "type": "module", diff --git a/test/bun-version-drift.test.ts b/test/bun-version-drift.test.ts index 724bc337e..1d856e09a 100644 --- a/test/bun-version-drift.test.ts +++ b/test/bun-version-drift.test.ts @@ -124,6 +124,13 @@ describe('install default Bun (A2, A-g)', () => { expect(verified).toContain(tested); expect(Bun.semver.order(installDefault, tested)).toBeGreaterThanOrEqual(0); }); + test('every verified version has a SHA-256 for each target the Capy installer downloads', () => { + for (const version of verified) { + const r = spawnSync('bash', ['-c', `. "${path.join(ROOT, 'bin', 'gstack-bun-version.sh')}" && for t in linux-x64 linux-x64-baseline linux-aarch64; do gstack_bun_archive_sha256 "$t" "$1" || exit 1; done`, 'bash', version], { encoding: 'utf-8', timeout: 10_000 }); + expect(r.status, `Bun ${version}: ${r.stderr}`).toBe(0); + expect(r.stdout.trim().split('\n').filter(line => /^[0-9a-f]{64}$/.test(line)), `Bun ${version}`).toHaveLength(3); + } + }); test('one CI job runs the quick free tier on GSTACK_BUN_DEFAULT, and the free-tests aggregate requires it', () => { const onDefault = collectPins().filter(p => p.job === DEFAULT_BUN_JOB); expect(onDefault.map(p => p.version)).toEqual([installDefault]); diff --git a/test/gstack-capy-install.test.ts b/test/gstack-capy-install.test.ts index d83f68076..d851ec135 100644 --- a/test/gstack-capy-install.test.ts +++ b/test/gstack-capy-install.test.ts @@ -202,7 +202,7 @@ describe('gstack-capy-install', () => { const unverified = run(g, ['--project', project]); expect(unverified.status).toBe(3); expect(unverified.stderr).toContain(`${project}/.bun-version pins exactly 1.4.9`); - expect(unverified.stderr).toContain('verified archives: 1.4.2 1.4.3'); + expect(unverified.stderr).toContain('verified archives: 1.4.2 1.4.3 1.4.4'); expect(unverified.stderr).toContain('(RUNTIME_PIN_CONFLICT; '); expect(g.calls().some(l => l.startsWith('curl ') || l.startsWith('setup '))).toBe(false); }); @@ -512,6 +512,19 @@ describe.skipIf(process.platform !== 'linux')('gstack-capy-install: one resoluti expect(gr.stdout).toContain(`bun 1.4.3 at ${store(g, '1.4.3')}/bun (the project's exact pin`); }); + test('GSTA-64: a gbrain-like repo whose CI pins only 1.4.4 refuses ~/.bun 1.4.3 and installs the verified 1.4.4 archive instead of stopping with RUNTIME_PIN_CONFLICT', () => { + const f = fixture({ bun: '1.3.14' }); + withPins(f); + realBun(path.join(f.home, '.bun/bin/bun'), '1.4.3'); + const project = ciProject(f, 'gbrain', '1.4.4'); + const r = run(f, ['--for', 'review'], { STUB_CURL_ZIP: zipFor(f, '1.4.4') }, project); + expect(r.status, r.stdout + r.stderr).toBe(0); + expect(r.stderr).not.toContain('RUNTIME_PIN_CONFLICT'); + expect(r.stdout).toContain(`judged: bun 1.4.3 at ${f.home}/.bun/bin/bun (BUN_INSTALL)`); + expect(f.calls().filter(l => l.startsWith('curl '))).toEqual([expect.stringContaining('https://github.com/oven-sh/bun/releases/download/bun-v1.4.4/bun-linux')]); + expect(r.stdout).toContain(`bun 1.4.4 at ${store(f, '1.4.4')}/bun (the project's exact pin`); + }); + test('no installed Bun satisfies the pins: the line names the Bun actually judged, the exact pin is published in the store, and the next --check agrees', () => { const f = fixture({ bun: '1.3.14' }); withPins(f); @@ -604,7 +617,7 @@ describe.skipIf(process.platform !== 'linux')('gstack-capy-install: one resoluti const f = fixture({ bun: '1.3.14' }); const bad = run(f, [], { GSTACK_BUN_INSTALL_VERSION: '1.4.9' }); expect(bad.status).toBe(3); - expect(bad.stderr).toContain('GSTACK_BUN_INSTALL_VERSION=1.4.9 is not a verified Bun at or above gstack\'s supported minimum 1.4.2 (verified archives: 1.4.2 1.4.3)'); + expect(bad.stderr).toContain('GSTACK_BUN_INSTALL_VERSION=1.4.9 is not a verified Bun at or above gstack\'s supported minimum 1.4.2 (verified archives: 1.4.2 1.4.3 1.4.4)'); expect(bad.stderr).toContain('(BUN_INSTALL_VERSION_INVALID; https://github.com/garrytan/gstack/blob/main/docs/troubleshooting.md#bun-install-version-invalid)'); 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') }); diff --git a/test/gstack-doctor.test.ts b/test/gstack-doctor.test.ts index 42b3fc328..ca7d3ae73 100644 --- a/test/gstack-doctor.test.ts +++ b/test/gstack-doctor.test.ts @@ -630,7 +630,7 @@ describe('gstack-doctor --check', () => { const wrapper = '/drive/skills/gstack/scripts/install.sh'; const capy = row(f, ['--project', project], { STUB_BUN_VERSION: '1.3.14', GSTACK_CAPY_WRAPPER_PATH: wrapper }); expect(capy.rows.pins.status).toBe('FAIL'); - expect(capy.rows.pins.rest).toContain(`fix: bash ${wrapper} (installs the project's pinned Bun when it is a verified version: 1.4.2 1.4.3; otherwise it names the conflict), or change the pin in ${project}`); + expect(capy.rows.pins.rest).toContain(`fix: bash ${wrapper} (installs the project's pinned Bun when it is a verified version: 1.4.2 1.4.3 1.4.4; otherwise it names the conflict), or change the pin in ${project}`); expect(capy.rows.pins.rest).toContain('(PROJECT_PIN_MISMATCH; '); const forSkill = row(f, ['--project', project, '--for', 'review'], { STUB_BUN_VERSION: '1.3.14', GSTACK_CAPY_WRAPPER_PATH: wrapper }); expect(forSkill.rows.pins.rest).toContain(`fix: bash ${wrapper} --for review (installs`);