Skip to content

gstack-memory-ingest broken on gbrain >=0.18: still calls renamed put_page subcommand #1346

Closed

Description

@simonkhc

Environment

  • gstack 1.26.4.0 (~/.claude/skills/gstack/VERSION)
  • gbrain 0.18.2
  • Linux, bun runtime

Reproducer

$ gbrain --version
gbrain 0.18.2
$ gbrain --help | grep -A 1 PAGES
PAGES
  get <slug>                         Read a page
  put <slug> [< file.md]             Write/update a page
$ echo "hi" | gbrain put_page --slug test --type note
Unknown command: put_page
Run gbrain --help for available commands.

Result: every transcript ingest fails. In a fresh setup with 292 transcripts queued, all 292 hit [put-error] ... Unknown command: put_page and nothing lands in the brain.

Expected

gstack-memory-ingest should use the canonical gbrain page-write subcommand and successfully ingest pages.

Actual

bin/gstack-memory-ingest.ts:768 builds the args array with the literal "put_page":

const args = [
  "put_page",
  "--slug", page.slug,
  "--title", page.title,
  "--type", page.type,
  "--tags", page.tags.join(","),
];
execFileSync("gbrain", args, { ... });

put_page is the gbrain <=0.17 name. gbrain >=0.18 renamed it to put (same flags, same stdin contract for the body).

Suggested fix

One-line change at bin/gstack-memory-ingest.ts:768: "put_page" -> "put". The rest of the call site (flags, stdin body, timeouts) is unchanged. PR to follow.

Activity

  1. s1dd4rth commented on May 7, 2026

    @s1dd4rth

    +1 — same failure on macOS with gbrain v0.22.8 + gstack v1.26.4.0. Adding a correction: the suggested one-line rename is incomplete.

    Environment

    • gstack 1.26.4.0
    • gbrain 0.22.8 (also reproduced on a v0.27.0 checkout before reverting)
    • macOS (Darwin 25.4.0), bun runtime

    The simple put_page -> put rename silently drops metadata

    gbrain put only accepts slug (positional) + content (stdin). The --title, --type, and --tags flags are silently consumed:

    $ echo "test body" | gbrain put gstack-test-flags --type test --tags foo,bar
    $ gbrain get gstack-test-flags
    ---
    type: concept
    title: Gstack Test Flags
    ---
    test body
    

    Type came back as auto-derived concept, tags are empty. So a literal put_page -> put swap makes calls succeed but loses all the metadata gstack built (repo:<slug>, agent:<name>, content type, etc.).

    Looking at gbrain/src/core/operations.ts:367:

    cliHints: { name: 'put', positional: ['slug'], stdin: 'content' }

    The put_page operation only takes slug and content over the CLI surface. Title/type/tags have to come from YAML frontmatter inside the body. (The put_page name itself is the operation/MCP name — it was, as far as I can tell, never a CLI subcommand. cliHints.name does the rename to put.)

    Working patch

    Rewrote gbrainPutPage() at bin/gstack-memory-ingest.ts:762 to inject metadata into the body's frontmatter (merging with existing frontmatter when present, since transcript pages already have an agent:/session_id: head):

    function gbrainPutPage(page: PageRecord): { ok: boolean; error?: string } {
      if (!gbrainAvailable()) {
        return { ok: false, error: "gbrain CLI not in PATH" };
      }
      try {
        const yamlEscape = (s: string) => JSON.stringify(s);
        const yamlList = (xs: string[]) => \`[\${xs.map(yamlEscape).join(", ")}]\`;
        const metaLines = [
          \`title: \${yamlEscape(page.title)}\`,
          \`type: \${page.type}\`,
          \`tags: \${yamlList(page.tags)}\`,
        ];
        let body: string;
        if (page.body.startsWith("---\n")) {
          const closeIdx = page.body.indexOf("\n---", 4);
          if (closeIdx > 0) {
            const existingHead = page.body.slice(4, closeIdx);
            const rest = page.body.slice(closeIdx + 4);
            body = ["---", ...metaLines, existingHead.trimEnd(), "---", rest.replace(/^\n/, "")].join("\n");
          } else {
            body = ["---", ...metaLines, "---", "", page.body].join("\n");
          }
        } else {
          body = ["---", ...metaLines, "---", "", page.body].join("\n");
        }
        execFileSync("gbrain", ["put", page.slug], {
          input: body,
          encoding: "utf-8",
          timeout: 30000,
          stdio: ["pipe", "pipe", "pipe"],
        });
        return { ok: true };
      } catch (err) {
        return { ok: false, error: err instanceof Error ? err.message : String(err) };
      }
    }

    After patch: 15 curated artifacts (CEO plans, design docs, learnings, timelines) ingested cleanly with correct types/tags preserved. gbrain list -n shows the right titles and types; semantic search ranks them as expected. 0 failed in a --bulk --sources eureka,learning,timeline,ceo-plan,design-doc,retro,builder-profile-entry run.

    Happy to send a PR if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions