From 8d1b6dc470b6d21c92e7ccc151c897c4eebdc3f4 Mon Sep 17 00:00:00 2001 From: garrytan Date: Sat, 3 Oct 2026 21:41:25 +0000 Subject: [PATCH] A2: treat db-unreachable like timeout in gbrain-refresh The memory lane's new db-unreachable status (network or DNS failure, config intact) is transient, so gbrain-refresh keeps rendering brain-aware blocks for it exactly as it does for timeout instead of suppressing them. --- bin/gstack-config | 6 ++++-- test/gstack-config-gbrain-refresh.test.ts | 26 +++++++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/bin/gstack-config b/bin/gstack-config index be3b29512..cd1603444 100755 --- a/bin/gstack-config +++ b/bin/gstack-config @@ -707,8 +707,10 @@ case "${1:-}" in fi case "$STATUS" in - ok|timeout|thin-client|engine-locked) - # "timeout" = slow-but-healthy engine (#1964); "thin-client" = + ok|timeout|db-unreachable|thin-client|engine-locked) + # "timeout" = slow-but-healthy engine (#1964); "db-unreachable" = + # network/DNS failure with an intact config, transient like timeout + # (A2); "thin-client" = # remote-HTTP MCP brain, no local engine by design (#2051); # "engine-locked" = same class (#2456): PGLite is single-writer, so a # live `gbrain serve` (typically an MCP server) holds the embedded DB. diff --git a/test/gstack-config-gbrain-refresh.test.ts b/test/gstack-config-gbrain-refresh.test.ts index 84b5e8332..a0b8bae67 100644 --- a/test/gstack-config-gbrain-refresh.test.ts +++ b/test/gstack-config-gbrain-refresh.test.ts @@ -15,6 +15,12 @@ const ROOT = path.resolve(import.meta.dir, '..'); let tmp: string; let binDir: string; let home: string; +const writeDetect = (status: string) => fs.writeFileSync(path.join(binDir, 'gstack-gbrain-detect'), + `#!/usr/bin/env bash\nprintf '%s\\n' '{"gbrain_on_path":true,"gbrain_local_status":"${status}","gbrain_version":"0.35.8.0"}'\n`, { mode: 0o755 }); +const refresh = () => spawnSync('bash', [path.join(binDir, 'gstack-config'), 'gbrain-refresh'], { + encoding: 'utf8', timeout: 30_000, + env: { PATH: `${binDir}:${process.env.PATH ?? '/usr/bin:/bin'}`, HOME: home, GSTACK_HOME: home, GSTACK_STATE_ROOT: home }, +}); beforeEach(() => { tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gbrain-refresh-')); @@ -26,8 +32,7 @@ beforeEach(() => { fs.copyFileSync(path.join(ROOT, 'bin', f), path.join(binDir, f)); fs.chmodSync(path.join(binDir, f), 0o755); } - fs.writeFileSync(path.join(binDir, 'gstack-gbrain-detect'), - `#!/usr/bin/env bash\nprintf '%s\\n' '{"gbrain_on_path":true,"gbrain_local_status":"no-config","gbrain_version":"0.35.8.0"}'\n`, { mode: 0o755 }); + writeDetect('no-config'); // A Python that cannot open() the state path but reads stdin, like native // Windows Python handed an MSYS path. Bun stands in for its JSON parse. fs.writeFileSync(path.join(binDir, 'python3'), `#!/usr/bin/env bash @@ -39,12 +44,21 @@ afterEach(() => { fs.rmSync(tmp, { recursive: true, force: true }); }); describe('gstack-config gbrain-refresh (E6)', () => { test('reads the detection file through stdin, so the status is not masked as unknown', () => { - const r = spawnSync('bash', [path.join(binDir, 'gstack-config'), 'gbrain-refresh'], { - encoding: 'utf8', timeout: 30_000, - env: { PATH: `${binDir}:${process.env.PATH ?? '/usr/bin:/bin'}`, HOME: home, GSTACK_HOME: home, GSTACK_STATE_ROOT: home }, - }); + const r = refresh(); expect(r.status, r.stderr).toBe(0); expect(r.stdout).toContain('local-status: no-config'); expect(r.stdout).not.toContain('local-status: unknown'); }); + + // A2: a network/DNS failure with an intact config is transient, like + // timeout, so brain-aware blocks are kept rather than stripped. + test('db-unreachable counts as a configured gbrain, like timeout', () => { + for (const status of ['timeout', 'db-unreachable']) { + writeDetect(status); + const r = refresh(); + expect(r.status, r.stderr).toBe(0); + expect(r.stdout).toContain(`Detected gbrain v0.35.8.0 (local-status: ${status}).`); + expect(r.stdout).not.toContain('brain-aware blocks will be suppressed'); + } + }); });