gstack-brain-context-load shells out to `gbrain list_pages`, which is an MCP tool name and not a CLI verb — brain context is always 0 bytes
Summary
bin/gstack-brain-context-load.ts builds its argv as ["list_pages", "--limit", ...] and calls spawnSync("gbrain", cliArgs). list_pages is the MCP tool name; the CLI verb is list. Every invocation exits non-zero with Unknown command: list_pages, the dispatch returns { ok: false, bytes: 0 }, and no brain context is ever loaded into any skill.
It fails silently: the caller degrades to Grep with no user-visible error.
Reproduction
$ gbrain list_pages --limit 3
Unknown command: list_pages
Run gbrain --help for available commands.
$ gbrain list --limit 3
<3 rows>
Measured end-to-end with the loader's own --explain:
$ gstack-brain-context-load --skill investigate --repo <slug> --explain
[brain-context-load] mode=manifest queries=3
SKIP prior-investigations kind=list bytes=0 (gbrain list_pages exited 1)
SKIP project-learnings kind=filesystem bytes=0 (no matches)
SKIP recent-eureka kind=filesystem bytes=0 (no matches)
[brain-context-load] total bytes=0
$ gstack-brain-context-load --skill review --repo <slug> --explain
[brain-context-load] mode=default queries=3
SKIP recent-transcripts kind=list bytes=0 (gbrain list_pages exited 1)
SKIP recent-curated kind=list bytes=0 (gbrain list_pages exited 1)
SKIP skill-name-events kind=list bytes=0 (gbrain list_pages exited 1)
[brain-context-load] total bytes=0
Note the second case: a skill with no gbrain: manifest falls back to Layer 1, and that fallback is also all-kind: list, so it fails identically.
Source
bin/gstack-brain-context-load.ts, line 263 on current origin/main:
const cliArgs: string[] = ["list_pages", "--limit", String(limit)];
if (q.sort) cliArgs.push("--sort", q.sort);
if (q.filter) {
for (const [k, v] of Object.entries(q.filter)) {
cliArgs.push("--filter", `${k}=${rv}`);
}
}
const result = spawnSync("gbrain", cliArgs, ...);
Impact
Every brain-backed query in the shipped skills is kind: list -- there are zero kind: vector queries -- and the Layer-1 fallback is all-list too. Net effect is 0 bytes of brain context in every skill, for every user with gbrain installed.
Verified independently on two machines
| Machine A | Machine B | |
|---|---|---|
| gbrain | 0.42.67.0 | 0.50.2.0 |
| gstack | 1.84.1.0 | 1.84.x |
gbrain list_pages |
Unknown command | Unknown command |
loader --explain |
total bytes=0 | total bytes=0 |
So it is not a gbrain-version artifact. On both machines the local gstack copy was unmodified (git diff origin/main -- bin/gstack-brain-context-load.ts empty) and within a few commits of origin/main.
Likely root cause, and a docs half
gbrain list --help itself says "use list_pages with sort=updated_desc" -- gbrain's CLI help refers to its own MCP tool name. Anyone writing a CLI caller from that help text lands exactly here. Worth fixing on the gbrain side too, or having the CLI accept list_pages as an alias.
Secondary mismatches (CONFIRMED on 0.50.2.0)
Initially filed as unverified leads; both are now confirmed directly:
$ gbrain list --limit 5 --filter type=timeline
gbrain list: unknown flag --filter for 'gbrain list'
Run: gbrain list --help
--filterdoes not exist. The real flags are--typeand--tag. The string--filterappears nowhere in gbrain 0.50'ssrc/cli.ts.--sortvalue is wrong. The loader sendsupdated_at_desc; the CLI acceptsupdated_desc,updated_asc,created_desc,slug.
So fixing the verb alone is not sufficient -- the sort value and the filter flags need translating too.
Worth noting for anyone fixing this: on gbrain <=0.42 unknown flags were silently ignored, and 0.50 made them a hard error (a deliberate upstream break, per its release notes). A verb-only fix would therefore surface these two immediately on 0.50+ rather than degrading quietly.
Related but distinct
#1687 covers filter: blocks being dropped by parseSkillManifest, so kind: list queries ran unfiltered (reported fixed in #2264). That bug assumes the query executes. This one is upstream of it: with the wrong verb the command never runs at all, so correct filter parsing still yields 0 bytes.
Source: garrytan/gstack