[object Object]

← back to Cli Printing Press

fix(cli): scope HTTP cache to <api>/http so invalidateCache spares siblings (#1166)

36976106f4357fb3dba3242a85ba66b9eb677bc3 · 2026-05-12 00:41:46 -0700 · Trevin Chow

invalidateCache() runs os.RemoveAll(cacheDir) after every mutation. With
cacheDir set to ~/.cache/<api>/, any sibling state (SQLite mirrors, FTS5
stores, watchlists) an agent or future scaffold places in the same
parent directory gets nuked alongside the HTTP cache.

Point cacheDir at ~/.cache/<api>/http/ so the wipe stays scoped to HTTP
responses. Add a guard test asserting the http subdir shape; refresh the
three affected golden client.go fixtures.

Fixes #1126

Files touched

Diff

commit 36976106f4357fb3dba3242a85ba66b9eb677bc3
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Tue May 12 00:41:46 2026 -0700

    fix(cli): scope HTTP cache to <api>/http so invalidateCache spares siblings (#1166)
    
    invalidateCache() runs os.RemoveAll(cacheDir) after every mutation. With
    cacheDir set to ~/.cache/<api>/, any sibling state (SQLite mirrors, FTS5
    stores, watchlists) an agent or future scaffold places in the same
    parent directory gets nuked alongside the HTTP cache.
    
    Point cacheDir at ~/.cache/<api>/http/ so the wipe stays scoped to HTTP
    responses. Add a guard test asserting the http subdir shape; refresh the
    three affected golden client.go fixtures.
    
    Fixes #1126
---
 internal/generator/client_invalidate_cache_test.go | 28 ++++++++++++++++++++++
 internal/generator/templates/client.go.tmpl        |  2 +-
 .../internal/client/client.go                      |  2 +-
 .../internal/client/client.go                      |  2 +-
 .../tier-routing-golden/internal/client/client.go  |  2 +-
 5 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/internal/generator/client_invalidate_cache_test.go b/internal/generator/client_invalidate_cache_test.go
index f855b640..9caffa03 100644
--- a/internal/generator/client_invalidate_cache_test.go
+++ b/internal/generator/client_invalidate_cache_test.go
@@ -61,3 +61,31 @@ func TestGenerateEmitsInvalidateCacheSymmetry(t *testing.T) {
 	assert.Contains(t, clientGo, "func (c *Client) writeCache(",
 		"client.go must still define writeCache; symmetry presupposes both")
 }
+
+// TestGenerateCacheDirIsHTTPSubdir guards #1126: cacheDir must point at
+// ~/.cache/<api>/http (not ~/.cache/<api>) so that invalidateCache's
+// os.RemoveAll only wipes the HTTP cache and leaves sibling state files
+// (SQLite mirrors, FTS5 stores, watchlists) intact.
+func TestGenerateCacheDirIsHTTPSubdir(t *testing.T) {
+	t.Parallel()
+
+	apiSpec, err := spec.Parse(filepath.Join("..", "..", "testdata", "stytch.yaml"))
+	require.NoError(t, err)
+
+	outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+	gen := New(apiSpec, outputDir)
+	require.NoError(t, gen.Generate())
+
+	clientGoBytes, err := os.ReadFile(filepath.Join(outputDir, "internal", "client", "client.go"))
+	require.NoError(t, err)
+	clientGo := string(clientGoBytes)
+
+	cliName := naming.CLI(apiSpec.Name)
+	wantSubdir := `filepath.Join(homeDir, ".cache", "` + cliName + `", "http")`
+	wantOldShape := `filepath.Join(homeDir, ".cache", "` + cliName + `")`
+
+	assert.Contains(t, clientGo, wantSubdir,
+		"client.go must place cacheDir under <api>/http so invalidateCache spares siblings (#1126)")
+	assert.NotContains(t, clientGo, wantOldShape,
+		"client.go must not point cacheDir at the bare ~/.cache/<api>/ root (#1126)")
+}
diff --git a/internal/generator/templates/client.go.tmpl b/internal/generator/templates/client.go.tmpl
index 78d632f3..6aa2c7d8 100644
--- a/internal/generator/templates/client.go.tmpl
+++ b/internal/generator/templates/client.go.tmpl
@@ -300,7 +300,7 @@ func newHTTPClient(timeout time.Duration, jar http.CookieJar) *http.Client {
 
 func New(cfg *config.Config, timeout time.Duration, rateLimit float64) *Client {
 	homeDir, _ := os.UserHomeDir()
-	cacheDir := filepath.Join(homeDir, ".cache", "{{.Name}}-pp-cli")
+	cacheDir := filepath.Join(homeDir, ".cache", "{{.Name}}-pp-cli", "http")
 {{- if eq .Auth.Type "session_handshake"}}
 	sess := newSessionManager(timeout)
 	httpClient := newHTTPClient(timeout, sess.CookieJar())
diff --git a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
index 496b28e3..4bdda627 100644
--- a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
+++ b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
@@ -55,7 +55,7 @@ func newHTTPClient(timeout time.Duration, jar http.CookieJar) *http.Client {
 
 func New(cfg *config.Config, timeout time.Duration, rateLimit float64) *Client {
 	homeDir, _ := os.UserHomeDir()
-	cacheDir := filepath.Join(homeDir, ".cache", "printing-press-oauth2-pp-cli")
+	cacheDir := filepath.Join(homeDir, ".cache", "printing-press-oauth2-pp-cli", "http")
 	httpClient := newHTTPClient(timeout, nil)
 	return &Client{
 		BaseURL:    strings.TrimRight(cfg.BaseURL, "/"),
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
index dffda2dc..d3c44d73 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
@@ -49,7 +49,7 @@ func newHTTPClient(timeout time.Duration, jar http.CookieJar) *http.Client {
 
 func New(cfg *config.Config, timeout time.Duration, rateLimit float64) *Client {
 	homeDir, _ := os.UserHomeDir()
-	cacheDir := filepath.Join(homeDir, ".cache", "printing-press-golden-pp-cli")
+	cacheDir := filepath.Join(homeDir, ".cache", "printing-press-golden-pp-cli", "http")
 	httpClient := newHTTPClient(timeout, nil)
 	return &Client{
 		BaseURL:    strings.TrimRight(cfg.BaseURL, "/"),
diff --git a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
index c855a5d1..ad913d8a 100644
--- a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
+++ b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
@@ -143,7 +143,7 @@ func newHTTPClient(timeout time.Duration, jar http.CookieJar) *http.Client {
 
 func New(cfg *config.Config, timeout time.Duration, rateLimit float64) *Client {
 	homeDir, _ := os.UserHomeDir()
-	cacheDir := filepath.Join(homeDir, ".cache", "tier-routing-golden-pp-cli")
+	cacheDir := filepath.Join(homeDir, ".cache", "tier-routing-golden-pp-cli", "http")
 	httpClient := newHTTPClient(timeout, nil)
 	return &Client{
 		BaseURL:    strings.TrimRight(cfg.BaseURL, "/"),

← 12964486 chore(main): release 4.5.0 (#1134)  ·  back to Cli Printing Press  ·  fix(cli): preserve novel-command keys in --compact partial-s f88a10b3 →