mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-02 03:35:09 +02:00
9dbaf906cf
* 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>
189 lines
6.5 KiB
Markdown
189 lines
6.5 KiB
Markdown
# 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 (30–90 seconds)
|
||
|
||
```bash
|
||
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
|
||
(~200–800 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
|
||
|
||
```bash
|
||
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:
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
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](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](../system-instruction-you-are-working-jaunty-kahn.md) in
|
||
the gstack plans directory.
|