mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-26 19:49:57 +02:00
feat(memory): optional gbrain --semantic recall for decision search
Adds gstack-decision-search --semantic (with --query): appends a 'Related from memory' block from gbrain semantic search, scoped to the curated-memory source. Pure enhancement, reliability-first: a new lib/gstack-decision-semantic.ts is the ONLY decision module that touches gbrain and is imported lazily only on --semantic, so the reliable file path never loads gbrain code. Every path degrades to the reliable file results when gbrain is off, unconfigured, empty, or errors (never throws, 10s timeout). Built against the verified gbrain 0.42.x surface (text output [score] slug -- snippet, NOT JSON; curated-memory source resolved by worktree path, not a gstack-brain-<user> id). Deterministic-contract tests only: parser units, degrade-to-null when gbrain absent, and a fake-gbrain shim proving scope+search end-to-end. find-contradictions deferred (no verifiable CLI surface yet + curated memory not indexed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,3 +102,60 @@ describe("gstack-decision-search", () => {
|
||||
expect(search()).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("gstack-decision-search --semantic (optional gbrain enhancement)", () => {
|
||||
function shimDir(gbrainBody: string): string {
|
||||
const d = fs.mkdtempSync(path.join(os.tmpdir(), "gbrain-shim-"));
|
||||
const p = path.join(d, "gbrain");
|
||||
fs.writeFileSync(p, gbrainBody, { mode: 0o755 });
|
||||
fs.chmodSync(p, 0o755);
|
||||
return d;
|
||||
}
|
||||
function searchWithPath(args: string, pathPrefix?: string): string {
|
||||
const env = { ...process.env, GSTACK_HOME: tmpDir } as NodeJS.ProcessEnv;
|
||||
if (pathPrefix) env.PATH = `${pathPrefix}:${process.env.PATH}`;
|
||||
try {
|
||||
return execSync(`${SEARCH} ${args}`, { cwd: ROOT, env, encoding: "utf-8", timeout: 20000 }).trim();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
test("--semantic without --query behaves like a normal search (no gbrain spawn)", () => {
|
||||
log('{"decision":"reliable-alpha","scope":"repo","source":"user"}');
|
||||
const out = searchWithPath("--semantic");
|
||||
expect(out).toContain("reliable-alpha");
|
||||
expect(out).not.toContain("Related from memory");
|
||||
});
|
||||
|
||||
test("--semantic --query appends a related-memory block when gbrain returns hits", () => {
|
||||
log('{"decision":"reliable-alpha","scope":"repo","source":"user"}');
|
||||
const dir = shimDir(
|
||||
`#!/usr/bin/env bash
|
||||
if [ "$1" = "sources" ]; then echo '{"sources":[{"id":"default","local_path":"/u/.gstack-brain-worktree"}]}'; exit 0; fi
|
||||
if [ "$1" = "search" ]; then echo "[0.88] decisions/related -- a semantically related past call"; exit 0; fi
|
||||
exit 1
|
||||
`,
|
||||
);
|
||||
try {
|
||||
const out = searchWithPath("--query alpha --semantic", dir);
|
||||
expect(out).toContain("reliable-alpha"); // reliable results still shown
|
||||
expect(out).toContain("Related from memory");
|
||||
expect(out).toContain("decisions/related");
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("--semantic degrades silently when gbrain errors (reliable results stand)", () => {
|
||||
log('{"decision":"reliable-alpha","scope":"repo","source":"user"}');
|
||||
const dir = shimDir(`#!/usr/bin/env bash\nexit 1\n`);
|
||||
try {
|
||||
const out = searchWithPath("--query alpha --semantic", dir);
|
||||
expect(out).toContain("reliable-alpha");
|
||||
expect(out).not.toContain("Related from memory");
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user