Summary
On installs that run via bun run src/cli.ts (symlink from ~/.bun/bin/gbrain
pointing at the source file, not the compiled binary), the v0.13.0 migration
orchestrator always finishes as partial because frontmatter_backfill never
succeeds.
Root cause: src/commands/migrations/v0_13_0.ts:44 has
const GBRAIN = process.execPath;
Under compiled-binary installs (bun build --compile --outfile bin/gbrain),
process.execPath is the gbrain binary, so this works. Under bun-runtime
installs, process.execPath is /Users/.../.bun/bin/bun. Phase B then
executes:
execSync(`${GBRAIN} extract links --source db --include-frontmatter`)
// → bun extract links --source db --include-frontmatter
Bun interprets this as bun <script> and tries to resolve extract as an
npm script or local binary. It fails with error: Script not found "extract",
and as a side effect, it runs bun init in the worktree, polluting
package.json with "private": true + a typescript peerDep and creating a
.cursor/rules/ directory.
Reproduction
- Install gbrain via
git clone + bun link (the docs/INSTALL_FOR_AGENTS.md
path), so which gbrain → ~/.bun/bin/gbrain symlink → src/cli.ts.
- Pre-existing brain at schema_version <= 11 (so v0.13.0 is pending, not skipped).
- Run
gbrain apply-migrations --yes --non-interactive.
Expected: v0.13.0 completes.
Observed: v0.13.0 → partial with frontmatter_backfill failure. gbrain doctor permanently reports MINIONS HALF-INSTALLED (partial migration: 0.13.0). package.json silently gains bogus fields. Re-running keeps
re-triggering the same failure.
Workaround
Run gbrain extract links --source db --include-frontmatter manually and
restore package.json / delete .cursor/ afterward. Doctor still reports
the partial state because the orchestrator ledger in
~/.gbrain/migrations/completed.jsonl doesn't pick up the out-of-band run.
Suggested fix
Resolve GBRAIN by preference order (most specific → least):
import { dirname, join } from 'node:path';
import { existsSync } from 'node:fs';
// 1. Compiled-binary case: process.execPath IS the gbrain binary.
// 2. bun-runtime case: argv[1] is src/cli.ts under the project; use
// `gbrain` from PATH so the public CLI contract is honored.
function resolveGbrain(): string {
// If argv[1] is a script path (compiled binaries don't have this), fall
// back to the `gbrain` symlink on PATH.
const isScript = process.argv[1]?.endsWith('.ts') || process.argv[1]?.endsWith('.js');
if (isScript) return 'gbrain';
return process.execPath;
}
const GBRAIN = resolveGbrain();
Or, more conservatively, always use gbrain from PATH in
src/commands/migrations/*.ts — matches user expectation that the installed
CLI is callable by name, and sidesteps both cases.
The same pattern appears in other migration orchestrators (grep for
process.execPath under src/commands/migrations/); worth a sweep.
Environment
- gbrain 0.15.1 (synced today from upstream master)
- Install path:
~/.bun/bin/gbrain symlink → src/cli.ts
- Runtime: Bun v1.3.10
- macOS 25.3.0 (Darwin arm64)
- Engine: pglite
Summary
On installs that run via
bun run src/cli.ts(symlink from~/.bun/bin/gbrainpointing at the source file, not the compiled binary), the v0.13.0 migration
orchestrator always finishes as
partialbecausefrontmatter_backfillneversucceeds.
Root cause:
src/commands/migrations/v0_13_0.ts:44hasUnder compiled-binary installs (
bun build --compile --outfile bin/gbrain),process.execPathis the gbrain binary, so this works. Under bun-runtimeinstalls,
process.execPathis/Users/.../.bun/bin/bun. Phase B thenexecutes:
Bun interprets this as
bun <script>and tries to resolveextractas annpm script or local binary. It fails with
error: Script not found "extract",and as a side effect, it runs
bun initin the worktree, pollutingpackage.jsonwith"private": true+ a typescript peerDep and creating a.cursor/rules/directory.Reproduction
git clone + bun link(thedocs/INSTALL_FOR_AGENTS.mdpath), so
which gbrain→~/.bun/bin/gbrainsymlink →src/cli.ts.gbrain apply-migrations --yes --non-interactive.Expected: v0.13.0 completes.
Observed: v0.13.0 →
partialwithfrontmatter_backfillfailure.gbrain doctorpermanently reportsMINIONS HALF-INSTALLED (partial migration: 0.13.0). package.json silently gains bogus fields. Re-running keepsre-triggering the same failure.
Workaround
Run
gbrain extract links --source db --include-frontmattermanually andrestore
package.json/ delete.cursor/afterward. Doctor still reportsthe partial state because the orchestrator ledger in
~/.gbrain/migrations/completed.jsonldoesn't pick up the out-of-band run.Suggested fix
Resolve GBRAIN by preference order (most specific → least):
Or, more conservatively, always use
gbrainfrom PATH insrc/commands/migrations/*.ts— matches user expectation that the installedCLI is callable by name, and sidesteps both cases.
The same pattern appears in other migration orchestrators (grep for
process.execPathundersrc/commands/migrations/); worth a sweep.Environment
~/.bun/bin/gbrainsymlink →src/cli.ts