mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
A5: brain-sync stage fails loud when the artifacts source indexed zero pages
The stage reported "curated artifacts pushed" whenever gstack-brain-sync exited 0, but a git push is not gbrain indexing. After a successful push the stage now reads the maintained artifacts source's page count (GSTACK_BRAIN_SOURCE_ID, else the source whose path is the artifacts worktree or state root): zero pages is ERR with the fix command; a missing source or an unavailable count says so instead of claiming indexing. Co-authored-by: Jimmy Zeisweiss <4261258+jzeisweiss@users.noreply.github.com>
This commit is contained in:
1 parent
d02c8effb7
commit
16aed1ced5
3 files changed
+90
-10
No files matched your search
+41
-10
@@ -214,6 +214,13 @@ export function resolveMemoryIngestSources(
|
||||
* state root itself), if any. Its pages already cover FEDERATED_CURATED_TYPES.
|
||||
*/
|
||||
export function federatedCuratedSourceId(env?: NodeJS.ProcessEnv): string | null {
|
||||
const rows = parseSourcesList(execGbrainJson(["sources", "list", "--json"], { baseEnv: env, timeout: 10_000 }));
|
||||
const row = rows.find((r) => (r as { federated?: boolean }).federated === true && isArtifactsSourceRow(r, env));
|
||||
return row?.id ?? null;
|
||||
}
|
||||
|
||||
/** A source this installation maintains for its curated artifacts (the worktree or the state root). */
|
||||
function isArtifactsSourceRow(r: { local_path?: string }, env?: NodeJS.ProcessEnv): boolean {
|
||||
const realOrSelf = (p: string): string => {
|
||||
try {
|
||||
return realpathSync(p);
|
||||
@@ -226,9 +233,36 @@ export function federatedCuratedSourceId(env?: NodeJS.ProcessEnv): string | null
|
||||
realOrSelf((env ?? process.env).GSTACK_BRAIN_WORKTREE || join(home, ".gstack-brain-worktree")),
|
||||
realOrSelf(GSTACK_HOME),
|
||||
]);
|
||||
const rows = parseSourcesList(execGbrainJson(["sources", "list", "--json"], { baseEnv: env, timeout: 10_000 }));
|
||||
const row = rows.find((r) => (r as { federated?: boolean }).federated === true && r.local_path && owned.has(realOrSelf(r.local_path)));
|
||||
return row?.id ?? null;
|
||||
return !!r.local_path && owned.has(realOrSelf(r.local_path));
|
||||
}
|
||||
|
||||
/**
|
||||
* A5 (#2670): after a successful artifacts push, report what gbrain actually
|
||||
* holds. A git push is not indexing; a maintained artifacts source with zero
|
||||
* pages is the one provably broken state and fails the stage.
|
||||
*/
|
||||
export function brainSyncIndexVerdict(env: NodeJS.ProcessEnv = process.env): { ok: boolean; summary: string } {
|
||||
const raw = execGbrainJson(["sources", "list", "--json"], { baseEnv: env, timeout: 10_000 });
|
||||
if (raw === null) {
|
||||
return { ok: true, summary: "curated artifacts pushed to git; gbrain page count unavailable (gbrain sources list failed)" };
|
||||
}
|
||||
const explicit = env.GSTACK_BRAIN_SOURCE_ID;
|
||||
const row = parseSourcesList(raw).find((r) => (explicit ? r.id === explicit : isArtifactsSourceRow(r, env)));
|
||||
if (!row?.id) {
|
||||
return { ok: true, summary: "curated artifacts pushed to git (no gbrain artifacts source on this machine, so indexing is not checked here)" };
|
||||
}
|
||||
if (typeof row.page_count !== "number") {
|
||||
return { ok: true, summary: `curated artifacts pushed to git; gbrain source ${row.id} did not report a page count` };
|
||||
}
|
||||
if (row.page_count === 0) {
|
||||
return {
|
||||
ok: false,
|
||||
summary:
|
||||
`curated artifacts pushed to git, but gbrain source ${row.id} has 0 indexed pages. ` +
|
||||
`Fix: gbrain sync --source ${row.id}, then re-run /sync-gbrain`,
|
||||
};
|
||||
}
|
||||
return { ok: true, summary: `curated artifacts pushed; gbrain source ${row.id} has ${row.page_count} pages` };
|
||||
}
|
||||
|
||||
export interface MemorySourceSelection {
|
||||
@@ -1598,13 +1632,10 @@ function runBrainSyncPush(args: CliArgs): StageResult {
|
||||
spawnSync(discover.cmd, discover.argv, { stdio, timeout: 60 * 1000, shell: discover.shell });
|
||||
const result = spawnSync(once.cmd, once.argv, { stdio, timeout: 60 * 1000, shell: once.shell });
|
||||
|
||||
return {
|
||||
name: "brain-sync",
|
||||
ran: true,
|
||||
ok: result.status === 0,
|
||||
duration_ms: Date.now() - t0,
|
||||
summary: result.status === 0 ? "curated artifacts pushed" : `gstack-brain-sync exited ${result.status}`,
|
||||
};
|
||||
if (result.status !== 0) {
|
||||
return { name: "brain-sync", ran: true, ok: false, duration_ms: Date.now() - t0, summary: `gstack-brain-sync exited ${result.status}` };
|
||||
}
|
||||
return { name: "brain-sync", ran: true, duration_ms: Date.now() - t0, ...brainSyncIndexVerdict() };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -408,6 +408,25 @@ gstack-gbrain-install # upgrade gbrain
|
||||
|
||||
---
|
||||
|
||||
## `curated artifacts pushed to git, but gbrain source <id> has 0 indexed pages. Fix: gbrain sync --source <id>, then re-run /sync-gbrain`
|
||||
|
||||
**Problem.** `/sync-gbrain`'s brain-sync stage is `ERR` even though the git
|
||||
push worked.
|
||||
|
||||
**Cause.** The push only moves your curated artifacts to git. gbrain indexes
|
||||
them separately, and the artifacts source this machine maintains reports zero
|
||||
pages, so searches return nothing from it. Before this release the stage said
|
||||
"curated artifacts pushed" and passed.
|
||||
|
||||
**Fix.**
|
||||
```bash
|
||||
gbrain sync --source <id>
|
||||
/sync-gbrain
|
||||
```
|
||||
If it stays at zero, re-register the source with `gstack-gbrain-source-wireup`.
|
||||
|
||||
---
|
||||
|
||||
## Nothing is syncing but I expect it to
|
||||
|
||||
**Not an error, but a common gotcha.** Check in order:
|
||||
|
||||
@@ -544,6 +544,36 @@ esac
|
||||
rmSync(home, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
// A5 (#2670): a successful git push is not gbrain indexing.
|
||||
for (const [pageCount, ok, expected] of [
|
||||
[0, false, "gbrain source fixture-artifacts has 0 indexed pages. Fix: gbrain sync --source fixture-artifacts"],
|
||||
[7, true, "curated artifacts pushed; gbrain source fixture-artifacts has 7 pages"],
|
||||
] as const) {
|
||||
it(`brain-sync stage reports the artifacts source's ${pageCount} indexed pages (ok=${ok})`, () => {
|
||||
const home = makeTestHome();
|
||||
const gstackHome = join(home, ".gstack");
|
||||
const fakeBin = join(home, "fake-bin");
|
||||
mkdirSync(gstackHome, { recursive: true });
|
||||
mkdirSync(fakeBin, { recursive: true });
|
||||
writeFileSync(join(fakeBin, "gbrain"), `#!/bin/sh
|
||||
case "$*" in
|
||||
"sources list --json") printf '%s\\n' '{"sources":[{"id":"fixture-artifacts","local_path":"${gstackHome}","page_count":${pageCount}}]}' ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
`);
|
||||
chmodSync(join(fakeBin, "gbrain"), 0o755);
|
||||
const r = runScript(["--incremental", "--no-code", "--no-memory", "--quiet"], {
|
||||
HOME: home, GSTACK_HOME: gstackHome, PATH: `${fakeBin}:${process.env.PATH || ""}`,
|
||||
});
|
||||
const state = JSON.parse(readFileSync(join(gstackHome, ".gbrain-sync-state.json"), "utf-8"));
|
||||
const stage = state.last_stages.find((entry: { name: string }) => entry.name === "brain-sync");
|
||||
expect(stage.summary).toContain(expected);
|
||||
expect(stage.ok).toBe(ok);
|
||||
expect(r.exitCode).toBe(ok ? 0 : 1);
|
||||
rmSync(home, { recursive: true, force: true });
|
||||
});
|
||||
}
|
||||
|
||||
it("brain-sync stage resolves the sibling binary, not a HOME-rooted path", () => {
|
||||
// Regression for Codex M9: pre-fix the orchestrator looked up
|
||||
// ~/.claude/skills/gstack/bin/gstack-brain-sync, which silently no-op'd
|
||||
|
||||
Reference in new issue
Block a user