--- name: sync-gbrain preamble-tier: 2 version: 1.0.0 description: | Keep gbrain current with this repo's code and refresh agent search guidance in CLAUDE.md. Wraps the gstack-gbrain-sync orchestrator with state probing, native code-surface registration, capability checks, and a verdict block. Re-runnable, idempotent. Use when: "sync gbrain", "refresh gbrain", "re-index this repo", "gbrain search isn't finding things". (gstack) triggers: - sync gbrain - refresh gbrain - reindex repo - update gbrain allowed-tools: - Bash - Read - Write - Edit - Glob - Grep - AskUserQuestion --- {{PREAMBLE}} # /sync-gbrain — Keep gbrain current and teach the agent to use it You are running the canonical "keep this brain up to date" verb. /setup-gbrain installs gbrain once; /sync-gbrain runs every time the user wants the brain refreshed against this repo's current state, and refreshes the agent-side guidance in CLAUDE.md so the coding agent knows when to prefer `gbrain` search over Grep. **Architecture:** This skill uses gbrain v0.20.0+'s **native code surfaces** (`gbrain sources add`, `gbrain sync --strategy code`, `gbrain reindex-code`, `gbrain code-def/code-refs/code-callers/code-callees`). It does NOT use `gbrain import` (that path is for markdown directories). It does NOT touch `~/.gstack/` indexing (the existing `gstack-gbrain-source-wireup` owns that — never double-store). ## User-invocable When the user types `/sync-gbrain`, run this skill. Argument modes (parsed by the skill itself, not a dispatcher binary): - `/sync-gbrain` — incremental sync (default; mtime fast-path; ~50ms steady-state) - `/sync-gbrain --full` — full code reindex via `gbrain reindex-code` (~25-35 min on a big repo). Auto-builds the call graph (`gbrain dream --phase resolve_symbol_edges`) **only when it was never built**. - `/sync-gbrain --dream` — build this source's call graph (`gbrain code-callers`/`code-callees`) via `gbrain dream --source --phase resolve_symbol_edges`; ~minutes; runs lock-free after the sync stages. Always forces, even if already built. Runs only that phase, never gbrain's full maintenance cycle (about 35 minutes with LLM phases); if the installed gbrain cannot scope the phase, the dream row says so and nothing runs. Only produces a graph on a code-aware schema pack; otherwise the run reports a WARN explaining why the graph is still empty. - `/sync-gbrain --no-dream` — skip the dream cycle that `--full` would otherwise auto-run. - `/sync-gbrain --code-only` — only run the code stage; skip memory + brain-sync - `/sync-gbrain --dry-run` — preview what would sync; no writes anywhere - `/sync-gbrain --no-memory` / `--no-brain-sync` — selectively skip stages - `/sync-gbrain --sources ` — memory types to ingest (env `GSTACK_MEMORY_INGEST_SOURCES`); by default, types a federated gstack source already indexes are skipped, and transcripts are skipped unless `transcript_ingest_mode` is `recent` or `all` (a list naming `transcript` overrides that for one run) - `/sync-gbrain --quiet` — suppress per-stage output - `/sync-gbrain --refresh-cache` — force-rebuild brain-aware planning cache. Skips code + memory stages; routes to `gstack-brain-cache refresh --project `. - `/sync-gbrain --audit` — emit summary of gstack-owned pages per project + sensitive-content audit. Read-only. Pass-through args go straight to the orchestrator at `{{BIN_DIR}}/gstack-gbrain-sync.ts`. **`--refresh-cache` short-circuit:** when this flag is present, the skill runs ONLY the cache refresh (`gstack-brain-cache refresh --project ` for the current worktree's slug, plus a cross-project refresh of user-profile if `gstack/user-profile/` exists). Code + memory + brain-sync stages are skipped. Useful when the user knows the brain has new info gstack should pick up before the next planning skill. **`--audit` short-circuit:** when this flag is present, the skill runs `gstack-brain-cache list --project --json`, summarizes by page type, then scans for any cached salience entries that ended up outside the SALIENCE_DEFAULT_ALLOWLIST (leak check). Read-only; no modifications to brain or cache. --- ## Step 1: State probe Before doing anything, check that /setup-gbrain has been run on this machine. ```bash ~/.claude/skills/gstack/bin/gstack-gbrain-detect 2>/dev/null ``` **Brain trust policy gate:** If `gbrain_mcp_mode == "remote-http"` from the detect output AND the per- endpoint policy is `unset`, the policy question MUST fire here before the orchestrator runs. Local engines auto-set to `personal` silently per the per-transport default table. ```bash _HASH=$(~/.claude/skills/gstack/bin/gstack-config endpoint-hash 2>/dev/null) _POLICY=$(~/.claude/skills/gstack/bin/gstack-config get brain_trust_policy@$_HASH 2>/dev/null || echo unset) echo "BRAIN_TRUST_POLICY[$_HASH]: $_POLICY" ``` If `_POLICY == "unset"` AND `_HASH != "local"`, AskUserQuestion per the Step 9.5 wording in `/setup-gbrain` (personal vs shared, with persistence to `brain_trust_policy@` and conditional `artifacts_sync_mode=full` flip for personal). Then continue. If `_POLICY == "unset"` AND `_HASH == "local"`, auto-set personal: ```bash ~/.claude/skills/gstack/bin/gstack-config set brain_trust_policy@$_HASH personal ``` **Split-engine model.** Code stage runs locally against the per-machine gbrain engine (PGLite or whatever `gbrain config` points to), with each worktree of a repo registered as its own source. **Memory stage also runs locally** in local-stdio MCP mode — `gstack-memory-ingest` shells out to `gbrain import` against the same local engine. In remote-http MCP mode (Path 4), the memory stage instead persists staged markdown to `~/.gstack/transcripts//` and the artifacts pipeline pushes it to the brain admin's pull job. Brain-sync (the `gstack-brain-sync` push to git) is the one stage that never touches local engine and runs regardless of mode. Practically: local PGLite stays code-only on remote-http machines; the remote brain holds everything else. Local-stdio machines mix code + transcripts in one local engine. Also check the per-repo trust policy. If `gstack-gbrain-repo-policy get` for this repo returns `deny`, STOP: > "This repo's gbrain trust policy is `deny`. Run `/setup-gbrain --repo` to > change it before syncing." --- ## Step 1.5: Local engine pre-flight Read `gbrain_local_status` from the Step 1 detect output. Branch as follows BEFORE invoking the orchestrator: - **`ok`**: proceed to Step 2 normally. - **`timeout`**: proceed to Step 2 — the engine is most likely healthy but slow (cold pooler connection). Tell the user in one line: "Engine probe timed out (>15s) — proceeding; raise `GSTACK_GBRAIN_PROBE_TIMEOUT_MS` if your pooler is slow." Do NOT treat this as a broken config. - **`thin-client`**: proceed to Step 2 — this machine is a thin client of a remote-HTTP MCP brain: no local engine BY DESIGN, so the code, memory, and dream stages will SKIP with a thin-client reason (code indexing runs on the brain server; memory syncs via the remote brain's artifacts pull). Only the brain-sync push runs locally. Tell the user in one line: "Thin client of a remote brain — local stages skip by design; brain queries work via remote MCP (reachability is verified at use time, not probed here)." Do NOT route this into the broken-config remediation. - **`engine-locked`**: STOP. "The local PGLite database is busy, usually because `gbrain serve` from a live Claude session owns it. Stop that process or run `/sync-gbrain` outside the live session, then retry. This identifies the conflict but does not remove PGLite's single-process limit." - **`no-cli`**: STOP. "Local gbrain CLI not installed. Run `/setup-gbrain` first." - **`missing-config`** AND `gbrain_mcp_mode == "remote-http"`: tell the user "Your brain queries (the `mcp__gbrain__*` tools) work via remote MCP, but symbol code search needs a local PGLite. Run `/setup-gbrain` and pick 'Yes' at the local code-search prompt (Path 4, step 4d), or run `gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024` directly (drop the voyage flags if `VOYAGE_API_KEY` isn't set). Continuing without code stage." Then proceed to Step 2 — the orchestrator's `runCodeImport()` and `runMemoryIngest()` will return SKIP; only `runBrainSyncPush()` will run. Do NOT abort. - **`missing-config`** AND `gbrain_mcp_mode != "remote-http"`: STOP. "Local gbrain CLI is installed but no engine config. Run `/setup-gbrain` first." - **`db-unreachable`**: STOP. Print `gbrain_local_status_detail` from the detect JSON verbatim (for example "database host unreachable (ENOTFOUND db.example.com); your gbrain config is unchanged. Fix: check network or VPN, then re-run /sync-gbrain."). Never suggest moving the config aside. - **`broken-config`** OR **`broken-db`**: STOP with a clear message: ``` Local gbrain config at ~/.gbrain/config.json points at an unreachable engine (status: {gbrain_local_status}). Two options: 1. Re-run /setup-gbrain — Step 1.5 offers Retry / Switch to PGLite / Switch brain mode / Quit. 2. Repair manually: mv ~/.gbrain/config.json ~/.gbrain/config.json.bak && gbrain init --pglite --json --embedding-model voyage:voyage-code-3 \ --embedding-dimensions 1024 (drop voyage flags if VOYAGE_API_KEY unset) Re-run /sync-gbrain after. ``` Do NOT continue — the orchestrator would skip code+memory and only run brain-sync, which is a degraded state the user should fix explicitly. This pre-flight short-circuits the orchestrator before it spends ~80ms probing the engine again. The orchestrator independently runs the same classifier for defense-in-depth, but Step 1.5's STOP is where the user gets the actionable remediation message. --- ## Step 1.6: Transcript consent Claude Code and Codex session transcripts are ingested only when `transcript_ingest_mode` is `recent` (last 90 days), `all` (all history) or `new@` (only sessions that start after it), optionally narrowed to the repos in `transcript_repos` (the value then ends in `+repos`). Skip this step for `--code-only`, `--no-memory`, `--dry-run`, `--refresh-cache` and `--audit`. Otherwise check whether the user chose; `has` tells an absent key from the `off` default that `get` prints: ```bash _TIM=$(~/.claude/skills/gstack/bin/gstack-config get transcript_ingest_mode 2>/dev/null || true) if ~/.claude/skills/gstack/bin/gstack-config has transcript_ingest_mode; then _T='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]t[0-9][0-9]:[0-9][0-9]:[0-9][0-9]z' case "$(printf '%s' "$_TIM" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')" in recent|all|off|recent+repos|all+repos|new@$_T|new@$_T+repos) echo "TRANSCRIPT_MODE: $_TIM (repos: $(~/.claude/skills/gstack/bin/gstack-config get transcript_repos 2>/dev/null || true))" ;; *) echo "TRANSCRIPT_MODE: ask (stored value '$_TIM' is not recognized by this version)" ;; esac else echo "TRANSCRIPT_MODE: ask (not set)" fi ``` A stored value (`recent`, `all`, `off`, `new@