Skip to content

Commit 0660547

Browse files
garrytanclaude
andauthored
v1.29.0.0 feat: worktree-aware gbrain code sources via path-hash IDs and CWD pin (#1382)
* feat: worktree-aware gbrain code sources via path-hash IDs and CWD pin Conductor sibling worktrees of the same repo no longer collide on a shared gstack-code-<slug> source ID. /sync-gbrain now derives a path-hashed source ID per worktree, runs gbrain sources attach to write .gbrain-source in the worktree root, and removes the legacy unsuffixed source on first new-format sync to prevent orphan accumulation. Bug fixes surfaced by /codex during /ship: - Silent attach failure now treated as stage failure (no more ok:true while pin is missing → unqualified code-def hits wrong source). - Startup preamble checks .gbrain-source in the cwd worktree, not global state, so an unsynced worktree no longer claims "indexed" because a sibling synced. - Code stage no longer skipped on remote-MCP (Path 4); the early-exit was in the SKILL template, not the orchestrator. - Source registration routes through lib/gbrain-sources.ts only; deleted the near-duplicate ensureSourceRegisteredSync from the orchestrator. Requires gbrain v0.30.0+ (uses sources attach). Phase 0 spike report: ~/.gstack/projects/garrytan-gstack/2026-05-08-gbrain-split-engine-spike.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: bump version and changelog (v1.29.0.0) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 443bde0 commit 0660547

49 files changed

Lines changed: 928 additions & 721 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ bin/gstack-global-discover
1818
.openclaw/
1919
.hermes/
2020
.gbrain/
21+
.gbrain-source
2122
.context/
2223
extension/.auth.json
2324
# xterm assets are vendored from npm at build time; not source-of-truth.

CHANGELOG.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,81 @@
11
# Changelog
22

3+
## [1.29.0.0] - 2026-05-08
4+
5+
## **Code search beats Grep across every Conductor worktree now, not just the last one you synced.**
6+
7+
`/sync-gbrain` registers each worktree as its own gbrain source, then
8+
runs `gbrain sources attach <id>` so the worktree gets a `.gbrain-source`
9+
pin in its root. Subsequent `gbrain code-def`, `code-refs`, `code-callers`
10+
calls from anywhere under the worktree route to that source by default,
11+
no `--source` flag needed. Conductor sibling worktrees of the same repo
12+
no longer collide on a shared `gstack-code-<slug>` source ID, so the
13+
last `/sync-gbrain` run no longer silently overwrites every other
14+
worktree's index.
15+
16+
Three correctness bugs surfaced by `/codex` adversarial review during
17+
`/ship` are fixed in the same release: silent attach failure (sync
18+
succeeds but pin is missing → unqualified `code-def` hits the wrong
19+
source), preamble inconsistency (startup hint claimed "indexed" based
20+
on global state, ignoring per-worktree pins), and orphan source leak
21+
(the pre-pathhash `gstack-code-<slug>` source stayed registered
22+
forever, polluting federated cross-source search). All three fixed
23+
before merge.
24+
25+
### The numbers that matter
26+
27+
End-to-end verified via `bun test test/gstack-gbrain-sync.test.ts test/gbrain-sources.test.ts test/gen-skill-docs.test.ts`:
28+
29+
| Surface | Before | After | Δ |
30+
|---|---|---|---|
31+
| Conductor worktrees indexed independently | 1 (last-sync-wins) | N (one source per path) | branch-correct |
32+
| `gbrain code-def` from a worktree without sync | hits wrong source silently | falls back to default with notice | no silent corruption |
33+
| Orphan sources accumulated across runs | unbounded | 0 (legacy id removed on first new-format sync) | clean |
34+
| Attach-failure-to-pin behavior | stage reports `ok:true` | stage reports `ok:false` with reason | no silent correctness break |
35+
| Orchestrator registration logic | duplicated in `bin/` and `lib/` (could miss `--db` on one path) | single source of truth in `lib/gbrain-sources.ts` | DRY |
36+
| Required gbrain version | v0.20.0+ (single-brain-only) | v0.30.0+ (uses `sources attach`) | prerequisite bumped |
37+
38+
Test count went from 405 → 408 (+3 worktree-aware tests + 1 legacy-cleanup preview test).
39+
40+
### What this means for builders
41+
42+
If you use Conductor to run multiple parallel branches of the same
43+
repo, you can now run `/sync-gbrain` in each one and `gbrain code-def`
44+
from inside any of them returns hits from THAT worktree's branch state,
45+
not whichever sibling synced most recently. This was a hard requirement
46+
before semantic code search could replace Grep for refactor planning,
47+
"where is X used", "what depends on what" queries across parallel
48+
worktrees. Run `gbrain autopilot --install` once per machine for
49+
ongoing background sync; gbrain owns the daemon lifecycle.
50+
51+
### Itemized changes
52+
53+
#### Added
54+
55+
- Worktree-aware source IDs in `bin/gstack-gbrain-sync.ts:176-186`. Pattern is now `gstack-code-<slug>-<pathhash8>` where `pathhash8` is the first 8 hex chars of `sha1(absolute repo path)`. Conductor worktrees of the same origin coexist as separate sources in one gbrain DB.
56+
- `gbrain sources attach <id>` step in `runCodeImport` (`bin/gstack-gbrain-sync.ts:336-351`). Writes `.gbrain-source <id>` in the worktree root after sync succeeds; subsequent `gbrain code-def` calls from any subdirectory auto-route to that source.
57+
- Legacy source cleanup: on first new-format sync, removes the pre-pathhash `gstack-code-<slug>` orphan via `gbrain sources remove ... --confirm-destructive` (`bin/gstack-gbrain-sync.ts:298-318`).
58+
- `.gbrain-source` added to `.gitignore` so per-worktree pin doesn't leak across branches.
59+
60+
#### Changed
61+
62+
- Code stage no longer skipped on remote-MCP (Path 4) installs. The early-exit in `sync-gbrain/SKILL.md.tmpl` was bouncing users out before the orchestrator ran; the local code brain works regardless of whether artifacts use a remote MCP. Replaced with split-engine prose explaining the model.
63+
- Source registration now flows through `lib/gbrain-sources.ts:ensureSourceRegistered` exclusively. Deleted `ensureSourceRegisteredSync` from the orchestrator binary (was a near-duplicate of the lib helper at `lib/gbrain-sources.ts:100`). Removes the missed-flag risk where one path could skip `--db` or `--federated`.
64+
- Startup preamble (`scripts/resolvers/preamble/generate-brain-sync-block.ts:48-75`) now checks for `.gbrain-source` in `git rev-parse --show-toplevel`, not the global `~/.gstack/.gbrain-sync-state.json`. Opening an unsynced worktree no longer claims "indexed" based on a sibling's sync.
65+
- CLAUDE.md guidance block in the SKILL template now documents the `.gbrain-source` pin and `gbrain autopilot --install` for ongoing sync.
66+
67+
#### Fixed
68+
69+
- Silent attach failure: `gbrain sources attach` now treated as stage failure if it returns non-zero. Previously the stage reported `ok:true` while the pin was missing, so unqualified `gbrain code-def` queries silently hit the default source. Now surfaces ERR with reason in the verdict block; user knows to retry.
70+
- Wrong-layer Path 4 early-exit (`/codex` finding #2 from `/plan-eng-review`).
71+
- Orphan source accumulation: the pre-pathhash `gstack-code-<slug>` source stayed registered across `/sync-gbrain` runs even after the path-keyed format shipped, polluting federated `gbrain search` results with stale duplicates.
72+
73+
#### For contributors
74+
75+
- Phase 0 verification spike at `~/.gstack/projects/garrytan-gstack/2026-05-08-gbrain-split-engine-spike.md` documents what gbrain v0.30 actually provides (no `--db` flag, `serve --http` requires postgres, `sources attach` is the v0.30 routing primitive). The approved plan's "per-worktree PGLite + per-worktree HTTP serve" architecture was invalidated by the spike; the simpler "one brain, many sources, attach for CWD pin" model collapsed ~80% of the plan's complexity.
76+
- `/codex` adversarial review during `/ship` caught all three correctness bugs above (silent attach, preamble inconsistency, orphan leak) before merge. Find-cost: ~10 min CC. Production-bug-cost: stale code search results that "almost worked" — the worst kind to debug.
77+
- gbrain CLI minimum version is now v0.30.0 (uses `sources attach`, which doesn't exist in v0.20.x). Run `cd ~/git/gbrain && git pull && bun install && bun link` to upgrade.
78+
379
## [1.28.0.0] - 2026-05-07
480

581
## **Browse handles real-world automation now: SOCKS5 with auth, container Xvfb, browser-native downloads. Plus a single-file `llms.txt` index agents can crawl in one read.**

SKILL.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -285,30 +285,29 @@ _BRAIN_SYNC_BIN="~/.claude/skills/gstack/bin/gstack-brain-sync"
285285
_BRAIN_CONFIG_BIN="~/.claude/skills/gstack/bin/gstack-config"
286286

287287
# /sync-gbrain context-load: teach the agent to use gbrain when it's available.
288-
# Mutually exclusive variants per /plan-eng-review §4. Empty string when gbrain
289-
# is not configured (zero context cost for non-gbrain users).
288+
# Per-worktree pin: post-spike redesign uses kubectl-style `.gbrain-source` in the
289+
# git toplevel to scope queries. Look for the pin in the worktree (not a global
290+
# state file) so that opening worktree B without a pin doesn't claim "indexed"
291+
# just because worktree A was synced. Empty string when gbrain is not
292+
# configured (zero context cost for non-gbrain users).
290293
_GBRAIN_CONFIG="$HOME/.gbrain/config.json"
291294
if [ -f "$_GBRAIN_CONFIG" ] && command -v gbrain >/dev/null 2>&1; then
292295
_GBRAIN_VERSION_OK=$(gbrain --version 2>/dev/null | grep -c '^gbrain ' || echo 0)
293296
if [ "$_GBRAIN_VERSION_OK" -gt 0 ] 2>/dev/null; then
294-
_SYNC_STATE="$_GSTACK_HOME/.gbrain-sync-state.json"
295-
_CWD_PAGES=0
296-
if [ -f "$_SYNC_STATE" ]; then
297-
# Flatten newlines so the regex works against pretty-printed JSON too.
298-
_CWD_PAGES=$(tr -d '\n' < "$_SYNC_STATE" 2>/dev/null \
299-
| grep -o '"name": *"code"[^}]*"detail": *{[^}]*"page_count": *[0-9]*' \
300-
| grep -o '"page_count": *[0-9]*' | grep -o '[0-9]\+' | head -1)
301-
_CWD_PAGES=${_CWD_PAGES:-0}
297+
_GBRAIN_PIN_PATH=""
298+
_REPO_TOP=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
299+
if [ -n "$_REPO_TOP" ] && [ -f "$_REPO_TOP/.gbrain-source" ]; then
300+
_GBRAIN_PIN_PATH="$_REPO_TOP/.gbrain-source"
302301
fi
303-
if [ "$_CWD_PAGES" -gt 0 ] 2>/dev/null; then
302+
if [ -n "$_GBRAIN_PIN_PATH" ]; then
304303
echo "GBrain configured. Prefer \`gbrain search\`/\`gbrain query\` over Grep for"
305304
echo "semantic questions; use \`gbrain code-def\`/\`code-refs\`/\`code-callers\` for"
306305
echo "symbol-aware code lookup. See \"## GBrain Search Guidance\" in CLAUDE.md."
307306
echo "Run /sync-gbrain to refresh."
308307
else
309-
echo "GBrain configured but this repo isn't indexed yet. Run \`/sync-gbrain --full\`"
310-
echo "before relying on \`gbrain search\` for code questions in this repo."
311-
echo "Falls back to Grep until indexed."
308+
echo "GBrain configured but this worktree isn't pinned yet. Run \`/sync-gbrain --full\`"
309+
echo "before relying on \`gbrain search\` for code questions in this worktree."
310+
echo "Falls back to Grep until pinned."
312311
fi
313312
fi
314313
fi

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.28.0.0
1+
1.29.0.0

autoplan/SKILL.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -353,30 +353,29 @@ _BRAIN_SYNC_BIN="~/.claude/skills/gstack/bin/gstack-brain-sync"
353353
_BRAIN_CONFIG_BIN="~/.claude/skills/gstack/bin/gstack-config"
354354

355355
# /sync-gbrain context-load: teach the agent to use gbrain when it's available.
356-
# Mutually exclusive variants per /plan-eng-review §4. Empty string when gbrain
357-
# is not configured (zero context cost for non-gbrain users).
356+
# Per-worktree pin: post-spike redesign uses kubectl-style `.gbrain-source` in the
357+
# git toplevel to scope queries. Look for the pin in the worktree (not a global
358+
# state file) so that opening worktree B without a pin doesn't claim "indexed"
359+
# just because worktree A was synced. Empty string when gbrain is not
360+
# configured (zero context cost for non-gbrain users).
358361
_GBRAIN_CONFIG="$HOME/.gbrain/config.json"
359362
if [ -f "$_GBRAIN_CONFIG" ] && command -v gbrain >/dev/null 2>&1; then
360363
_GBRAIN_VERSION_OK=$(gbrain --version 2>/dev/null | grep -c '^gbrain ' || echo 0)
361364
if [ "$_GBRAIN_VERSION_OK" -gt 0 ] 2>/dev/null; then
362-
_SYNC_STATE="$_GSTACK_HOME/.gbrain-sync-state.json"
363-
_CWD_PAGES=0
364-
if [ -f "$_SYNC_STATE" ]; then
365-
# Flatten newlines so the regex works against pretty-printed JSON too.
366-
_CWD_PAGES=$(tr -d '\n' < "$_SYNC_STATE" 2>/dev/null \
367-
| grep -o '"name": *"code"[^}]*"detail": *{[^}]*"page_count": *[0-9]*' \
368-
| grep -o '"page_count": *[0-9]*' | grep -o '[0-9]\+' | head -1)
369-
_CWD_PAGES=${_CWD_PAGES:-0}
365+
_GBRAIN_PIN_PATH=""
366+
_REPO_TOP=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
367+
if [ -n "$_REPO_TOP" ] && [ -f "$_REPO_TOP/.gbrain-source" ]; then
368+
_GBRAIN_PIN_PATH="$_REPO_TOP/.gbrain-source"
370369
fi
371-
if [ "$_CWD_PAGES" -gt 0 ] 2>/dev/null; then
370+
if [ -n "$_GBRAIN_PIN_PATH" ]; then
372371
echo "GBrain configured. Prefer \`gbrain search\`/\`gbrain query\` over Grep for"
373372
echo "semantic questions; use \`gbrain code-def\`/\`code-refs\`/\`code-callers\` for"
374373
echo "symbol-aware code lookup. See \"## GBrain Search Guidance\" in CLAUDE.md."
375374
echo "Run /sync-gbrain to refresh."
376375
else
377-
echo "GBrain configured but this repo isn't indexed yet. Run \`/sync-gbrain --full\`"
378-
echo "before relying on \`gbrain search\` for code questions in this repo."
379-
echo "Falls back to Grep until indexed."
376+
echo "GBrain configured but this worktree isn't pinned yet. Run \`/sync-gbrain --full\`"
377+
echo "before relying on \`gbrain search\` for code questions in this worktree."
378+
echo "Falls back to Grep until pinned."
380379
fi
381380
fi
382381
fi

benchmark-models/SKILL.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -287,30 +287,29 @@ _BRAIN_SYNC_BIN="~/.claude/skills/gstack/bin/gstack-brain-sync"
287287
_BRAIN_CONFIG_BIN="~/.claude/skills/gstack/bin/gstack-config"
288288

289289
# /sync-gbrain context-load: teach the agent to use gbrain when it's available.
290-
# Mutually exclusive variants per /plan-eng-review §4. Empty string when gbrain
291-
# is not configured (zero context cost for non-gbrain users).
290+
# Per-worktree pin: post-spike redesign uses kubectl-style `.gbrain-source` in the
291+
# git toplevel to scope queries. Look for the pin in the worktree (not a global
292+
# state file) so that opening worktree B without a pin doesn't claim "indexed"
293+
# just because worktree A was synced. Empty string when gbrain is not
294+
# configured (zero context cost for non-gbrain users).
292295
_GBRAIN_CONFIG="$HOME/.gbrain/config.json"
293296
if [ -f "$_GBRAIN_CONFIG" ] && command -v gbrain >/dev/null 2>&1; then
294297
_GBRAIN_VERSION_OK=$(gbrain --version 2>/dev/null | grep -c '^gbrain ' || echo 0)
295298
if [ "$_GBRAIN_VERSION_OK" -gt 0 ] 2>/dev/null; then
296-
_SYNC_STATE="$_GSTACK_HOME/.gbrain-sync-state.json"
297-
_CWD_PAGES=0
298-
if [ -f "$_SYNC_STATE" ]; then
299-
# Flatten newlines so the regex works against pretty-printed JSON too.
300-
_CWD_PAGES=$(tr -d '\n' < "$_SYNC_STATE" 2>/dev/null \
301-
| grep -o '"name": *"code"[^}]*"detail": *{[^}]*"page_count": *[0-9]*' \
302-
| grep -o '"page_count": *[0-9]*' | grep -o '[0-9]\+' | head -1)
303-
_CWD_PAGES=${_CWD_PAGES:-0}
299+
_GBRAIN_PIN_PATH=""
300+
_REPO_TOP=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
301+
if [ -n "$_REPO_TOP" ] && [ -f "$_REPO_TOP/.gbrain-source" ]; then
302+
_GBRAIN_PIN_PATH="$_REPO_TOP/.gbrain-source"
304303
fi
305-
if [ "$_CWD_PAGES" -gt 0 ] 2>/dev/null; then
304+
if [ -n "$_GBRAIN_PIN_PATH" ]; then
306305
echo "GBrain configured. Prefer \`gbrain search\`/\`gbrain query\` over Grep for"
307306
echo "semantic questions; use \`gbrain code-def\`/\`code-refs\`/\`code-callers\` for"
308307
echo "symbol-aware code lookup. See \"## GBrain Search Guidance\" in CLAUDE.md."
309308
echo "Run /sync-gbrain to refresh."
310309
else
311-
echo "GBrain configured but this repo isn't indexed yet. Run \`/sync-gbrain --full\`"
312-
echo "before relying on \`gbrain search\` for code questions in this repo."
313-
echo "Falls back to Grep until indexed."
310+
echo "GBrain configured but this worktree isn't pinned yet. Run \`/sync-gbrain --full\`"
311+
echo "before relying on \`gbrain search\` for code questions in this worktree."
312+
echo "Falls back to Grep until pinned."
314313
fi
315314
fi
316315
fi

benchmark/SKILL.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -287,30 +287,29 @@ _BRAIN_SYNC_BIN="~/.claude/skills/gstack/bin/gstack-brain-sync"
287287
_BRAIN_CONFIG_BIN="~/.claude/skills/gstack/bin/gstack-config"
288288

289289
# /sync-gbrain context-load: teach the agent to use gbrain when it's available.
290-
# Mutually exclusive variants per /plan-eng-review §4. Empty string when gbrain
291-
# is not configured (zero context cost for non-gbrain users).
290+
# Per-worktree pin: post-spike redesign uses kubectl-style `.gbrain-source` in the
291+
# git toplevel to scope queries. Look for the pin in the worktree (not a global
292+
# state file) so that opening worktree B without a pin doesn't claim "indexed"
293+
# just because worktree A was synced. Empty string when gbrain is not
294+
# configured (zero context cost for non-gbrain users).
292295
_GBRAIN_CONFIG="$HOME/.gbrain/config.json"
293296
if [ -f "$_GBRAIN_CONFIG" ] && command -v gbrain >/dev/null 2>&1; then
294297
_GBRAIN_VERSION_OK=$(gbrain --version 2>/dev/null | grep -c '^gbrain ' || echo 0)
295298
if [ "$_GBRAIN_VERSION_OK" -gt 0 ] 2>/dev/null; then
296-
_SYNC_STATE="$_GSTACK_HOME/.gbrain-sync-state.json"
297-
_CWD_PAGES=0
298-
if [ -f "$_SYNC_STATE" ]; then
299-
# Flatten newlines so the regex works against pretty-printed JSON too.
300-
_CWD_PAGES=$(tr -d '\n' < "$_SYNC_STATE" 2>/dev/null \
301-
| grep -o '"name": *"code"[^}]*"detail": *{[^}]*"page_count": *[0-9]*' \
302-
| grep -o '"page_count": *[0-9]*' | grep -o '[0-9]\+' | head -1)
303-
_CWD_PAGES=${_CWD_PAGES:-0}
299+
_GBRAIN_PIN_PATH=""
300+
_REPO_TOP=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
301+
if [ -n "$_REPO_TOP" ] && [ -f "$_REPO_TOP/.gbrain-source" ]; then
302+
_GBRAIN_PIN_PATH="$_REPO_TOP/.gbrain-source"
304303
fi
305-
if [ "$_CWD_PAGES" -gt 0 ] 2>/dev/null; then
304+
if [ -n "$_GBRAIN_PIN_PATH" ]; then
306305
echo "GBrain configured. Prefer \`gbrain search\`/\`gbrain query\` over Grep for"
307306
echo "semantic questions; use \`gbrain code-def\`/\`code-refs\`/\`code-callers\` for"
308307
echo "symbol-aware code lookup. See \"## GBrain Search Guidance\" in CLAUDE.md."
309308
echo "Run /sync-gbrain to refresh."
310309
else
311-
echo "GBrain configured but this repo isn't indexed yet. Run \`/sync-gbrain --full\`"
312-
echo "before relying on \`gbrain search\` for code questions in this repo."
313-
echo "Falls back to Grep until indexed."
310+
echo "GBrain configured but this worktree isn't pinned yet. Run \`/sync-gbrain --full\`"
311+
echo "before relying on \`gbrain search\` for code questions in this worktree."
312+
echo "Falls back to Grep until pinned."
314313
fi
315314
fi
316315
fi

0 commit comments

Comments
 (0)