Files
gstack/docs/gbrain-sync.md
T
Garry Tan 9dbaf906cf feat(v1.9.0.0): gbrain-sync — cross-machine gstack memory (#1151)
* feat(gbrain-sync): queue primitives + writer shims

Adds bin/gstack-brain-enqueue (atomic append to sync queue) and
bin/gstack-jsonl-merge (git merge driver, ts-sort with SHA-256 fallback).
Wires one backgrounded enqueue call into learnings-log, timeline-log,
review-log, and developer-profile --migrate. question-log and
question-preferences stay local per Codex v2 decision.

gstack-config gains gbrain_sync_mode (off/artifacts-only/full) and
gbrain_sync_mode_prompted keys, plus GSTACK_HOME env alignment so
tests don't leak into real ~/.gstack/config.yaml.

* feat(gbrain-sync): --once drain + secret scan + push

bin/gstack-brain-sync is the core sync binary. Subcommands: --once
(drain queue, allowlist-filter, privacy-class-filter, secret-scan
staged diff, commit with template, push with fetch+merge retry),
--status, --skip-file <path>, --drop-queue --yes, --discover-new
(cursor-based detection of artifact writes that skip the shim).

Secret regex families: AWS keys, GitHub tokens (ghp_/gho_/ghu_/ghs_/
ghr_/github_pat_), OpenAI sk-, PEM blocks, JWTs, bearer-token-in-JSON.
On hit: unstage, preserve queue, print remediation hint (--skip-file
or edit), exit clean. No daemon — invoked by preamble at skill
boundaries.

* feat(gbrain-sync): init, restore, uninstall, consumer registry

bin/gstack-brain-init: idempotent first-run. git init ~/.gstack/,
.gitignore=*, canonical .brain-allowlist + .brain-privacy-map.json,
pre-commit secret-scan hook (defense-in-depth), merge driver registration
via git config, gh repo create --private OR arbitrary --remote <url>,
initial push, ~/.gstack-brain-remote.txt for new-machine discovery,
GBrain consumer registration via HTTP POST.

bin/gstack-brain-restore: safe new-machine bootstrap. Refuses clobber
of existing allowlisted files, clones to staging, rsync-copies tracked
files, re-registers merge drivers (required — not cloned from remote),
rehydrates consumers.json, prompts for per-consumer tokens.

bin/gstack-brain-uninstall: clean off-ramp. Removes .git + .brain-*
files + consumers.json + config keys. Preserves user data (learnings,
plans, retros, profile). Optional --delete-remote for GitHub repos.

bin/gstack-brain-consumer + bin/gstack-brain-reader (symlink alias):
registry management. Internal 'consumer' term; user-facing 'reader'
per DX review decision.

* feat(gbrain-sync): preamble block — privacy gate + boundary sync

scripts/resolvers/preamble/generate-brain-sync-block.ts emits bash that
runs at every skill invocation:
- Detects ~/.gstack-brain-remote.txt on machines without local .git
  and surfaces a restore-available hint (does NOT auto-run restore).
- Runs gstack-brain-sync --once at skill start to drain any pending
  writes (and at skill end via prose instruction).
- Once-per-day auto-pull (cached via .brain-last-pull) for append-only
  JSONL files.
- Emits BRAIN_SYNC: status line every skill run.

Also emits prose for the host LLM to fire the one-time privacy
stop-gate (full / artifacts-only / off) when gbrain is detected and
gbrain_sync_mode_prompted is false. Wired into preamble.ts composition.

* test(gbrain-sync): 27-test consolidated suite

test/brain-sync.test.ts covers:
- Config: validation, defaults, GSTACK_HOME env isolation
- Enqueue: no-op gates, skip list, concurrent atomicity, JSON escape
- JSONL merge driver: 3-way + ts-sort + SHA-256 fallback
- Init + sync: canonical file creation, merge driver registration,
  push-reject + fetch+merge retry path
- Init refuses different remote (idempotency)
- Cross-machine restore round-trip (machine A write → machine B sees)
- Secret scan across all 6 regex families (AWS, GH, OpenAI, PEM, JWT,
  bearer-JSON). --skip-file unblock remediation
- Uninstall removes sync config, preserves user data
- --discover-new idempotence via mtime+size cursor

Behaviors verified via integration smokes during implementation. Known
follow-up: bun-test 5s default timeout needs 30s wrapper for
spawnSync-heavy tests.

* docs(gbrain-sync): user guide + error lookup + README section

docs/gbrain-sync.md: setup walkthrough, privacy modes, cross-machine
workflow, secret protection, two-machine conflict handling, uninstall,
troubleshooting reference.

docs/gbrain-sync-errors.md: problem/cause/fix index for every
user-visible error. Patterned on Rust's error docs + Stripe's API
error reference.

README.md: 'Cross-machine memory with GBrain sync' section near the
top (discovery moment), plus docs-table entry.

* chore: bump version and changelog (v1.7.0.0)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: regenerate SKILL.md files for gbrain-sync preamble block

Re-runs bun run gen:skill-docs after adding generateBrainSyncBlock
to scripts/resolvers/preamble.ts in a2aa8a07. CI check-freshness
caught the drift. All 36 SKILL.md files regenerated with the new
skill-start bash block + privacy-gate prose + skill-end sync
instructions baked in.

* fix(test): session-awareness reads AskUserQuestion Format from a Tier 2+ SKILL.md

The test was reading ROOT/SKILL.md (browse skill, Tier 1) which never
contained '## AskUserQuestion Format' — that section is only emitted
for Tier 2+ skills by scripts/resolvers/preamble.ts. As a result the
agent was prompted with an empty format guide and only emitted
'RECOMMENDATION' intermittently, making the test flaky.

Pre-existing on main (same ROOT/SKILL.md shape there) — surfaced now
because the agent run didn't hit the RECOMMENDATION/recommend/option a
fallback strings in this particular attempt.

Fix: read from office-hours/SKILL.md (Tier 3, always has the section)
with a fallback that scans for the first top-level skill dir whose
SKILL.md contains the header. Future template moves won't break this
test again.

* chore: bump to v1.9.0.0 for gbrain-sync landing

Changes just the VERSION + package.json + CHANGELOG header (1.7.0.0 → 1.9.0.0
and date 2026-04-22 → 2026-04-23). No code changes. User call: land gbrain-sync
as a bigger-signal release above main's 1.6.4.0, skipping 1.8.0.0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 17:54:54 -07:00

6.5 KiB
Raw Blame History

Cross-machine memory with GBrain sync

gstack writes a lot of useful state to ~/.gstack/ — learnings, retros, CEO plans, design docs, developer profile. By default, all of that dies when you switch laptops. GBrain sync pushes a curated subset to a private git repo so your memory follows you across machines and becomes indexable by GBrain.

What you get

  • Work on machine A, pick up seamlessly on machine B.
  • Your learnings, plans, and designs are visible in GBrain (if you use it).
  • A clean off-ramp (gstack-brain-uninstall) that never touches your data.
  • No daemon, no system service, no background process.

What does NOT leave your machine

By design, these stay local even when sync is on:

  • Credentials: .auth.json, auth-token.json, sidebar-sessions/, security/device-salt, consumer tokens in config.yaml
  • Machine-specific state: Chromium profiles, ONNX model weights, caches, eval-cache, CDP-profile, one-time prompt markers (.welcome-seen, .telemetry-prompted, .vendoring-warned-*, etc.)
  • Question-preferences: per-machine UX preferences (question-preferences.json, question-log.jsonl, question-events.jsonl).

The exact allowlist lives in ~/.gstack/.brain-allowlist. The CLI manages it; you can append your own entries below the marker line.

First-run setup (3090 seconds)

gstack-brain-init

The command:

  1. Turns ~/.gstack/ into a git repo.
  2. Asks for a remote URL (default: gh repo create --private gstack-brain-$USER). Any git remote works — GitHub, GitLab, Gitea, self-hosted.
  3. Pushes an initial commit with just the config.
  4. Writes ~/.gstack-brain-remote.txt (URL-only, no secrets — safe to copy to another machine).
  5. Registers GBrain as a reader if GBRAIN_URL + GBRAIN_TOKEN are configured. Otherwise you can add readers later with gstack-brain-reader add <name> --ingest-url <url> --token <token>.

After init, the next skill you run will ask you ONE question about privacy mode:

  • Everything allowlisted (recommended): learnings, reviews, plans, designs, retros, timelines, and developer profile all sync.
  • Only artifacts: plans, designs, retros, learnings — skip behavioral data (timelines, developer profile).
  • Decline: keep everything local. You can turn sync on later with gstack-config set gbrain_sync_mode full.

Your answer is persisted. You won't be asked again.

Cross-machine workflow

On machine A: run gstack-brain-init once. That's it — every skill invocation now drains the sync queue at its start and end boundaries (~200800 ms network pause per skill).

On machine B:

  1. Copy ~/.gstack-brain-remote.txt from machine A to machine B (password manager, dotfile repo, USB stick — your call).
  2. Run any gstack skill. The preamble sees the URL file and prints:
    BRAIN_SYNC: brain repo detected: <url>
    BRAIN_SYNC: run 'gstack-brain-restore' to pull your cross-machine memory
    
  3. Run gstack-brain-restore. That clones the repo, rehydrates your learnings/plans/retros, and re-registers the git merge drivers.
  4. Re-enter consumer tokens (they're machine-local and NOT synced — gstack-config set gbrain_token <your-token>).
  5. Next skill: your yesterday-on-machine-A learning surfaces. That's the magical moment.

Status, health, and queue depth

gstack-brain-sync --status

Shows: last successful push, pending queue depth, any sync blocks, and the current privacy mode.

Every skill run prints a BRAIN_SYNC: line near the top of the preamble output. Scan it for problems.

Privacy modes in detail

Mode What syncs
off Nothing (default).
artifacts-only Plans, designs, retros, learnings, reviews. Skips timelines + developer-profile.
full Everything in the allowlist, including behavioral state.

Change anytime with:

gstack-config set gbrain_sync_mode full
gstack-config set gbrain_sync_mode off

Secret protection

Every commit is scanned for credential-shaped content before it leaves your machine. Blocked patterns include:

  • AWS access keys (AKIA…)
  • GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_, github_pat_)
  • OpenAI keys (sk-…)
  • PEM blocks (-----BEGIN …-----)
  • JWTs (eyJ…)
  • Bearer tokens in JSON ("authorization": "…", "api_key": "…", etc.)

If a scan hits, sync stops, the queue is preserved, and your preamble prints:

BRAIN_SYNC: blocked: <pattern-family>:<snippet>

To remediate:

  1. Review the offending file.
  2. If the match is a false positive on content you explicitly want to sync, run gstack-brain-sync --skip-file <path> to permanently exclude that path.
  3. Otherwise, edit the file to remove the secret and re-run any skill.

There's a defense-in-depth hook at ~/.gstack/.git/hooks/pre-commit that runs the same scan if you manually git commit against the repo.

Two-machine conflicts

If you write on machine A and machine B the same day, both will push append commits. Git's default would conflict at the file tail, but the .jsonl and markdown files are registered with custom merge drivers:

  • JSONL files use a sort-and-dedup driver that orders appends by ISO timestamp (falls back to SHA-256 hash of each line for determinism).
  • Markdown artifacts (retros, plans, designs) use a union merge driver that concatenates both sides.

You shouldn't see conflict prompts. If you do (a real semantic conflict, like two machines editing the same plan), git will stop and prompt.

Cross-machine pull cadence

The preamble runs git fetch + git merge --ff-only once per 24 hours (cached via ~/.gstack/.brain-last-pull). You don't need to think about this — it happens automatically at the first skill invocation each day.

Uninstall

gstack-brain-uninstall

This:

  • Removes ~/.gstack/.git/ and all .brain-* config files.
  • Clears gbrain_sync_mode in gstack-config.
  • Does NOT touch your learnings, plans, retros, or developer profile.

Add --delete-remote to also delete the private GitHub repo (GitHub only, uses gh repo delete).

Re-init anytime with gstack-brain-init.

Troubleshooting

See gbrain-sync-errors.md for an index of every error message gstack-brain may print, with problem / cause / fix for each.

Under the hood

For the architectural decisions behind this feature (allowlist vs denylist, daemon vs preamble-boundary sync, JSONL merge driver, privacy stop-gate), see the approved plan in the gstack plans directory.