From d46efb4790bda1e518499cbd420a0ea01450c182 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Tue, 28 Apr 2026 20:21:41 -0400 Subject: [PATCH] fix(#2784): clear shared ~/.cache/gsd/ update-check cache in update workflow (#2813) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(#2784): clear shared ~/.cache/gsd/ cache in update workflow The SessionStart hook (hooks/gsd-check-update.js) writes update-check results to $HOME/.cache/gsd/gsd-update-check.json (shared, tool-agnostic). The update.md run_update step only cleared per-runtime paths like ~/.claude/cache/gsd-update-check.json, so the statusline kept showing the stale upgrade indicator after a successful update. Fix: add rm -f "$HOME/.cache/gsd/gsd-update-check.json" to the cache-clear block in the run_update step. Regression test: tests/bug-2784-update-cache-clear-path.test.cjs * fix(#2784): address CodeRabbit review — four edge-cases count, bash fence, structured test assertions --- CHANGELOG.md | 2 +- docs/RELEASE-v1.39.0-rc.5.md | 2 +- get-shit-done/workflows/update.md | 5 ++ .../bug-2784-update-cache-clear-path.test.cjs | 86 +++++++++++++++++++ 4 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tests/bug-2784-update-cache-clear-path.test.cjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a25f9cb3..309b15476 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). values, and unsupported value types. Both pre-write helper failures and write-time failures restore the pre-install snapshot and abort with a clear error rather than warn-and-continue. (#2760) -- **Codex hooks migrator correctness hardening** — five edge-cases in the +- **Codex hooks migrator correctness hardening** — four edge-cases in the `[[hooks.]]` → `[[hooks..hooks]]` migration path fixed: (1) the TOML key parser in hook-body classification now uses `parseTomlKey()` instead of a bare regex, so hyphenated keys (e.g. `status-message`) and quoted keys are no longer diff --git a/docs/RELEASE-v1.39.0-rc.5.md b/docs/RELEASE-v1.39.0-rc.5.md index 5b07c5a44..96d945ff1 100644 --- a/docs/RELEASE-v1.39.0-rc.5.md +++ b/docs/RELEASE-v1.39.0-rc.5.md @@ -2,7 +2,7 @@ Pre-release candidate. Published to npm under the `next` tag. -``` +```bash npx get-shit-done-cc@next ``` diff --git a/get-shit-done/workflows/update.md b/get-shit-done/workflows/update.md index d4712d3fc..bf2c685d3 100644 --- a/get-shit-done/workflows/update.md +++ b/get-shit-done/workflows/update.md @@ -539,6 +539,11 @@ for dir in .claude .config/opencode .opencode .gemini .config/kilo .kilo .codex; rm -f "./$dir/cache/gsd-update-check.json" rm -f "$HOME/$dir/cache/gsd-update-check.json" done + +# Clear the shared tool-agnostic cache written by gsd-check-update.js hook (#2784). +# The hook uses ~/.cache/gsd/gsd-update-check.json regardless of runtime; clear it +# so the statusline stops showing the stale "⬆ /gsd-update" indicator after update. +rm -f "$HOME/.cache/gsd/gsd-update-check.json" ``` The SessionStart hook (`gsd-check-update.js`) writes to the detected runtime's cache directory, so preferred/env-derived paths and default paths must all be cleared to prevent stale update indicators. diff --git a/tests/bug-2784-update-cache-clear-path.test.cjs b/tests/bug-2784-update-cache-clear-path.test.cjs new file mode 100644 index 000000000..ded16c5f4 --- /dev/null +++ b/tests/bug-2784-update-cache-clear-path.test.cjs @@ -0,0 +1,86 @@ +/** + * Regression test for bug #2784 + * + * /gsd-update cache-clear step only cleared per-runtime cache paths + * (e.g. ~/.claude/cache/gsd-update-check.json) but the SessionStart hook + * (hooks/gsd-check-update.js) writes to the shared tool-agnostic path + * ~/.cache/gsd/gsd-update-check.json. After a successful update, the statusline + * kept showing the stale "⬆ /gsd-update" indicator because the actual cache + * file was never deleted. + * + * Fix: add `rm -f "$HOME/.cache/gsd/gsd-update-check.json"` to the + * run_update step's cache-clear block in get-shit-done/workflows/update.md. + */ + +'use strict'; + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const REPO_ROOT = path.join(__dirname, '..'); +const UPDATE_WORKFLOW = path.join( + REPO_ROOT, + 'get-shit-done', + 'workflows', + 'update.md' +); +const CHECK_UPDATE_HOOK = path.join(REPO_ROOT, 'hooks', 'gsd-check-update.js'); + +describe('bug-2784: update.md cache-clear covers shared cache path', () => { + test('gsd-check-update.js hook constructs cache dir from .cache and gsd path segments', () => { + const hookContent = fs.readFileSync(CHECK_UPDATE_HOOK, 'utf-8'); + // Parse the path.join() call structurally rather than text-grepping. + const m = hookContent.match(/const cacheDir\s*=\s*path\.join\(([^)]+)\)/); + assert.ok( + m !== null, + 'hook must assign cacheDir via path.join() with explicit path segments' + ); + const segments = m[1].split(',').map((a) => a.trim().replace(/^['"]|['"]$/g, '')); + assert.ok( + segments.includes('.cache'), + `hook cacheDir path.join() must include '.cache' segment; got: ${JSON.stringify(segments)}` + ); + assert.ok( + segments.includes('gsd'), + `hook cacheDir path.join() must include 'gsd' segment; got: ${JSON.stringify(segments)}` + ); + }); + + test('update.md run_update bash commands include rm for shared gsd cache file', () => { + const workflowContent = fs.readFileSync(UPDATE_WORKFLOW, 'utf-8'); + // Parse the step block structurally, then extract only bash fenced code lines. + const stepMatch = workflowContent.match(/[\s\S]*?<\/step>/); + assert.ok(stepMatch, 'update.md must have a block'); + const stepContent = stepMatch[0]; + + const bashLines = []; + const fenceRe = /```(?:bash|sh)\n([\s\S]*?)```/g; + let m; + while ((m = fenceRe.exec(stepContent)) !== null) { + for (const line of m[1].split('\n')) { + const trimmed = line.trim(); + if (trimmed) bashLines.push(trimmed); + } + } + + const sharedCacheClearCmds = bashLines.filter( + (line) => /^rm\b/.test(line) && line.includes('.cache/gsd/gsd-update-check.json') + ); + assert.ok( + sharedCacheClearCmds.length > 0, + [ + 'run_update step bash blocks must include an `rm` command targeting .cache/gsd/gsd-update-check.json.', + `Bash lines found: ${JSON.stringify(bashLines)}`, + ].join('\n') + ); + const hasHomeExpansion = sharedCacheClearCmds.some( + (line) => line.includes('$HOME') || line.includes('~/') + ); + assert.ok( + hasHomeExpansion, + `shared cache rm command must use $HOME or ~/ expansion; found: ${JSON.stringify(sharedCacheClearCmds)}` + ); + }); +});