From b6b3fbc5e3186e02674ddf74ca1235a01b68d5ca Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Wed, 27 May 2026 08:34:15 -0700 Subject: [PATCH] refactor(brain): compress GBRAIN_* resolvers, move template prose to docs/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit generateGBrainContextLoad: 80 -> 115 tokens with explicit skip-header. generateGBrainSaveResults: 500-700 -> 161 tokens per skill with the skill metadata extracted into a typed skillSaveMap (slugPrefix + title + tag). Verbose prose (heredoc body, entity-stub instructions, throttle handling, backlink protocol) moved into a new doc: docs/gbrain-write-surfaces.md (Sections: §Context Load, §Save Template). The agent reads the doc on-demand only when actually saving — one Read call, cached by Claude's context. Net per-planning-skill overhead under un-suppression drops from ~1000 tokens (naive un-suppression) to ~275 tokens (compressed). Combined with the setup-time detection from prior commits, users WITHOUT gbrain pay zero overhead (block suppressed at gen-time) and users WITH gbrain pay ~275 tokens. The /investigate special-case (data-research routing in CONTEXT_LOAD) stays inline since it's skill-specific. docs/gbrain-write-surfaces.md also serves as the manual-probe reference for humans verifying live persistence + a topology summary covering trust-policy + .gbrain-source reads-only semantics. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/gbrain-write-surfaces.md | 208 ++++++++++++++++++++++++++++++++++ scripts/resolvers/gbrain.ts | 103 ++++++++++------- 2 files changed, 271 insertions(+), 40 deletions(-) create mode 100644 docs/gbrain-write-surfaces.md diff --git a/docs/gbrain-write-surfaces.md b/docs/gbrain-write-surfaces.md new file mode 100644 index 000000000..7d84734b1 --- /dev/null +++ b/docs/gbrain-write-surfaces.md @@ -0,0 +1,208 @@ +# gbrain write surfaces — what lands where, and how to verify + +This doc serves two audiences: + +1. **Agents**: when a planning skill renders the compact `## Brain Context + Load` or `## Save Results to Brain` blocks, those blocks reference this + doc. Read §Context Load or §Save Template here on-demand when you're + actually using gbrain. Skip entirely if `gbrain` is not on PATH. +2. **Humans**: after running a planning skill against a real brain, use + the manual-probe sections to confirm the page actually landed. + +## What lands where + +| Host + detection state | What renders in the planning-skill SKILL.md | +|---|---| +| Any host + `gstack-config gbrain-refresh` reports `gbrain_local_status: "ok"` | Compressed brain-aware blocks render. Agent reads this doc on-demand when it actually saves. ~250 token overhead per planning skill. | +| Any host + gbrain not detected | Blocks suppressed at gen-time. Zero token overhead. Calibration takes still render (separate resolver, host-agnostic). | +| GBrain or Hermes host | Blocks always render regardless of detection — these hosts ship gbrain integration as a first-class concern. | + +`.gbrain-source` pins **reads** only — writes go to the default engine +configured in `~/.gbrain/config.json`. Documented at +`bin/gstack-gbrain-sync.ts` for code-lookup resolvers; gstack treats the +same contract as load-bearing for artifact `put` semantics. If a user +reports writes landing in the wrong source, look here first. + +Trust policy (`personal` vs `shared`, per endpoint hash) gates auto-push +and writeback. Set via `gstack-config set +brain_trust_policy@ personal`. Local PGLite installs +auto-default to `personal`; remote-MCP installs prompt during +`/setup-gbrain` step 9.5. + +## §Context Load (agent reads this when running a planning skill) + +Before starting, search the brain for relevant context: + +1. **Extract 2-4 keywords** from the user's request. Pick nouns, error + names, file paths, technical terms — NOT verbs or adjectives. + Example: for "the login page is broken after deploy", search for + `login broken deploy`. +2. **Search**: `gbrain search ""`. Returns lines like + `[slug] Title (score: 0.85) - first line of content...`. +3. **If few results** (under 3): broaden to the single most specific + keyword and search again. If still few, proceed without brain context. +4. **Read top 3 results**: `gbrain get_page ""` for each. Stop + after 3 — diminishing returns past that. +5. **Use the context** to inform your analysis. Cite specific slugs in + your output when a brain page changed your thinking. + +If `gbrain search` returns any non-zero exit (gbrain not on PATH, network +flake, throttle), treat as transient: proceed without brain context. Do +not retry inline — the user can re-run the skill later. + +## §Save Template (agent reads this when actually saving) + +After completing the skill, save the output. The compact resolver block +already shows the slug prefix + title + tag for your specific skill (e.g. +`gbrain put "ceo-plans/" ...`). The full template: + +```bash +gbrain put "/" --content "$(cat <<'EOF' +--- +title: ": <feature name>" +tags: [<tag>, <feature-slug>] +--- +<skill output in markdown — the actual deliverable, not a summary> +EOF +)" +``` + +**Slug guidance**: `<feature-slug>` should be kebab-case, lowercase, and +unique within the prefix. Prefer concrete project/feature names over +abstract labels. Example: `auth-rate-limit` not `security-fix`. + +**Title guidance**: the constant prefix (e.g. "CEO Plan", "Eng Review") +is fixed; the suffix is the human-readable name of the feature/topic. + +**Tag guidance**: the first tag is the constant `<tag>` from the skill's +metadata (e.g. `ceo-plan`, `eng-review`). The second tag is the +`<feature-slug>` so cross-page traversal works. Add more tags if obvious +relationships exist (e.g. `[ceo-plan, auth-rate-limit, security]`). + +### Entity-stub enrichment + +After saving the main page, extract person and organization names +mentioned in the output. For each one: + +```bash +# Check if a page exists first +gbrain search "<entity name>" + +# If no match, create a stub +gbrain put "entities/<entity-slug>" --content "$(cat <<'EOF' +--- +title: "<Person or Company Name>" +tags: [entity, person] +--- +Stub page. Mentioned in <skill name> output. Replace with real bio when relevant. +EOF +)" +``` + +**Only extract real names** — actual person names (e.g. "Garry Tan") and +company/organization names (e.g. "Y Combinator"). Skip product names, +feature names, section headings, technical terms (CSS class names, +function names), and file paths. When in doubt, skip. + +`tags: [entity, person]` for people, `tags: [entity, organization]` for +companies/teams. + +### Error handling + +- **Throttle**: exit code 1 with stderr containing `throttle`, `rate + limit`, `capacity`, or `busy`. Defer the save and move on — the brain + is busy; the content isn't lost, just not persisted this run. +- **Any other non-zero exit**: treat as transient failure. Do not retry + inline — the user can re-run the skill or run + `gstack-config gbrain-refresh` if they suspect gbrain itself is + misconfigured. +- **`gbrain: command not found`**: gbrain isn't on PATH. The compact + resolver block told you to skip — you shouldn't reach this code. If + you somehow did, silently skip and continue. + +### Backlinks + +If your save output mentions another brain page by name or topic, add a +backlink line at the bottom of the markdown body: + +``` +Related: [[other-page-slug]], [[another-slug]] +``` + +gbrain auto-resolves `[[slug]]` syntax into a clickable link in the +rendered page. Add backlinks only when the relationship is concrete +(e.g. "this CEO plan depends on the eng review at +`eng-reviews/auth-rate-limit`"). Don't fabricate connections. + +### Completion summary + +In your final skill output, note brain utilization in one line: +"Brain: read 3 pages, saved 1 page, enriched 2 entity stubs, 0 throttles." +This helps the user see brain coverage growing over time. + +## Persistence verification (automated) + +The matched-pair "is the data we hope to save actually being saved?" +question is covered by `test/skill-e2e-gbrain-roundtrip-local.test.ts`: +real `gbrain init --pglite` + `gbrain put` + `gbrain get` round-trip +against an isolated temp HOME. Periodic-tier. Skips when +`VOYAGE_API_KEY` is unset or gbrain CLI is missing from PATH. + +Run it before opening a PR that touches the resolver: + +```bash +EVALS=1 EVALS_TIER=periodic VOYAGE_API_KEY=$VOYAGE_API_KEY \ + bun test test/skill-e2e-gbrain-roundtrip-local.test.ts +``` + +If you do want to spot-check by hand against your own brain after a +real planning-skill run (debugging a specific page that the agent +should have saved): + +```bash +gbrain get "<prefix>/<slug>" # expect markdown + frontmatter +gbrain search "<slug fragment>" # expect slug in top results +gbrain sources list # confirm gstack-brain-<user> source +gbrain get "entities/<person>" # expect stub per named person +``` + +## Remote / Supabase / thin-client-MCP routing + +The resolver emits a single CLI shape — `gbrain put "<slug>" --content +"..."` — that works against every engine gbrain supports. The CLI +internally routes to local PGLite, remote Supabase, or a remote MCP +endpoint depending on the user's `~/.gbrain/config.json`. **gstack +doesn't test that routing**: the storage layer is gbrain's contract to +honor, and the same CLI invocation we test against local PGLite is the +one that fires against any other engine. + +If you're on Supabase or thin-client MCP and writes aren't landing: + +1. `gbrain doctor --fast --json` — engine health check. If anything + reports `error`, fix that first. +2. `gstack-config get brain_trust_policy@<endpoint-hash>` must be + `personal` for auto-write. Run `gstack-config endpoint-hash` to get + the active hash. If `shared`, the agent prompts before writes — if + you declined, re-run the skill. +3. If trust policy is `personal` and `gbrain doctor` is clean but the + page still isn't there, file an issue against gbrain — gstack's + CLI call shape is the same as what T11 (`gbrain-roundtrip-local`) + exercises. + +## What's NOT verified by automation + +- **Calibration takes (`takes_add`)**: today these fall back to + fence-block writes inside a `gbrain put` because + `BRAIN_CALIBRATION_WRITEBACK` is FALSE pending gbrain v0.42+ shipping + the `takes_add` MCP op. When the flag flips, re-run the probe in this + doc against `/office-hours` and confirm `gbrain takes_list` surfaces a + `kind=bet` entry with the expected weight (0.9 for office-hours, per + `scripts/brain-cache-spec.ts:151-157`). +- **Per-skill E2E for the other 4 planning skills**: only `/office-hours` + has fake-CLI E2E coverage (`test/skill-e2e-office-hours-brain-writeback.test.ts`). + The resolver unit test (`test/resolvers-gbrain-save-results.test.ts`) + covers wiring for all 5. Per-skill E2E expansion is tracked in TODOS.md. +- **`.gbrain-source` write semantics**: gstack treats the documented + reads-only contract as load-bearing, but doesn't independently verify + that gbrain CLI never re-routes writes based on the pin. If you find a + case where it does, that's a gbrain bug to file upstream. diff --git a/scripts/resolvers/gbrain.ts b/scripts/resolvers/gbrain.ts index 78055bb70..6c6b66d64 100644 --- a/scripts/resolvers/gbrain.ts +++ b/scripts/resolvers/gbrain.ts @@ -26,72 +26,95 @@ import { getInvalidationTargets, } from '../brain-cache-spec'; +// Per-skill slug + title + tag metadata for SAVE_RESULTS. The full save +// template (heredoc body, entity-stub instructions, throttle handling, +// backlinks) lives in docs/gbrain-write-surfaces.md §Save Template and is +// read on-demand by the agent. Compressing the inline prose keeps the +// token footprint at ~150 tokens per skill (down from ~500), so users with +// gbrain installed pay a small overhead and users without it (whose hosts +// have GBRAIN_SAVE_RESULTS suppressed at gen-time) pay nothing. +interface SkillSaveMeta { + slugPrefix: string; + title: string; + tag: string; +} + +const skillSaveMap: Record<string, SkillSaveMeta> = { + 'office-hours': { slugPrefix: 'office-hours', title: 'Office Hours', tag: 'design-doc' }, + 'investigate': { slugPrefix: 'investigations', title: 'Investigation', tag: 'investigation' }, + 'plan-ceo-review': { slugPrefix: 'ceo-plans', title: 'CEO Plan', tag: 'ceo-plan' }, + 'plan-eng-review': { slugPrefix: 'eng-reviews', title: 'Eng Review', tag: 'eng-review' }, + 'plan-design-review': { slugPrefix: 'design-reviews', title: 'Design Review', tag: 'design-review' }, + 'plan-devex-review': { slugPrefix: 'devex-reviews', title: 'Devex Review', tag: 'devex-review' }, + 'retro': { slugPrefix: 'retros', title: 'Retro', tag: 'retro' }, + 'ship': { slugPrefix: 'releases', title: 'Release', tag: 'release' }, + 'cso': { slugPrefix: 'security-audits', title: 'Security Audit', tag: 'security-audit' }, + 'design-consultation': { slugPrefix: 'design-systems', title: 'Design System', tag: 'design-system' }, +}; + export function generateGBrainContextLoad(ctx: TemplateContext): string { let base = `## Brain Context Load -Before starting this skill, search your brain for relevant context: +**Skip this entire section if \`gbrain\` is not on PATH.** -1. Extract 2-4 keywords from the user's request (nouns, error names, file paths, technical terms). - Search GBrain: \`gbrain search "keyword1 keyword2"\` - Example: for "the login page is broken after deploy", search \`gbrain search "login broken deploy"\` - Search returns lines like: \`[slug] Title (score: 0.85) - first line of content...\` -2. If few results, broaden to the single most specific keyword and search again. -3. For each result page, read it: \`gbrain get_page "<page_slug>"\` - Read the top 3 pages for context. -4. Use this brain context to inform your analysis. +Extract 2-4 keywords from the user's request. Search the brain: +\`gbrain search "<keywords>"\`. Read the top 3 results with +\`gbrain get_page "<slug>"\`. Use that context to inform your analysis. -If GBrain is not available or returns no results, proceed without brain context. -Any non-zero exit code from gbrain commands should be treated as a transient failure.`; +If \`gbrain search\` returns no results or any non-zero exit, proceed +without brain context. Full search/read protocol + examples: +see \`docs/gbrain-write-surfaces.md\` §Context Load.`; if (ctx.skillName === 'investigate') { - base += `\n\nIf the user's request is about tracking, extracting, or researching structured data (e.g., "track this data", "extract from emails", "build a tracker"), route to GBrain's data-research skill instead: \`gbrain call data-research\`. This skill has a 7-phase pipeline optimized for structured data extraction.`; + base += `\n\nFor structured-data extraction requests ("track this", "extract from emails", "build a tracker"), route to GBrain's data-research skill instead: \`gbrain call data-research\`.`; } return base; } export function generateGBrainSaveResults(ctx: TemplateContext): string { - // gbrain v0.18+ renamed `put_page` → `put <slug>` and moved --title/--tags - // into YAML frontmatter inside --content. These templates render into - // SKILL.md files as user-facing instructions; using the old subcommand - // ships broken copy-paste to every gstack user. - const skillSaveMap: Record<string, string> = { - 'office-hours': 'Save the design document as a brain page:\n```bash\ngbrain put "office-hours/<project-slug>" --content "$(cat <<\'EOF\'\n---\ntitle: "Office Hours: <project name>"\ntags: [design-doc, <project-slug>]\n---\n<design doc content in markdown>\nEOF\n)"\n```', - 'investigate': 'Save the root cause analysis as a brain page:\n```bash\ngbrain put "investigations/<issue-slug>" --content "$(cat <<\'EOF\'\n---\ntitle: "Investigation: <issue summary>"\ntags: [investigation, <affected-files>]\n---\n<investigation findings in markdown>\nEOF\n)"\n```', - 'plan-ceo-review': 'Save the CEO plan as a brain page:\n```bash\ngbrain put "ceo-plans/<feature-slug>" --content "$(cat <<\'EOF\'\n---\ntitle: "CEO Plan: <feature name>"\ntags: [ceo-plan, <feature-slug>]\n---\n<scope decisions and vision in markdown>\nEOF\n)"\n```', - 'retro': 'Save the retrospective as a brain page:\n```bash\ngbrain put "retros/<date>" --content "$(cat <<\'EOF\'\n---\ntitle: "Retro: <date range>"\ntags: [retro, <date>]\n---\n<retro output in markdown>\nEOF\n)"\n```', - 'plan-eng-review': 'Save the architecture decisions as a brain page:\n```bash\ngbrain put "eng-reviews/<feature-slug>" --content "$(cat <<\'EOF\'\n---\ntitle: "Eng Review: <feature name>"\ntags: [eng-review, <feature-slug>]\n---\n<review findings and decisions in markdown>\nEOF\n)"\n```', - 'ship': 'Save the release notes as a brain page:\n```bash\ngbrain put "releases/<version>" --content "$(cat <<\'EOF\'\n---\ntitle: "Release: <version>"\ntags: [release, <version>]\n---\n<changelog entry and deploy details in markdown>\nEOF\n)"\n```', - 'cso': 'Save the security audit as a brain page:\n```bash\ngbrain put "security-audits/<date>" --content "$(cat <<\'EOF\'\n---\ntitle: "Security Audit: <date>"\ntags: [security-audit, <date>]\n---\n<findings and remediation status in markdown>\nEOF\n)"\n```', - 'design-consultation': 'Save the design system as a brain page:\n```bash\ngbrain put "design-systems/<project-slug>" --content "$(cat <<\'EOF\'\n---\ntitle: "Design System: <project name>"\ntags: [design-system, <project-slug>]\n---\n<design decisions in markdown>\nEOF\n)"\n```', - }; + // gbrain v0.18+ uses `gbrain put <slug>` (NOT the deprecated `put_page` + // MCP op). Compressed in v1.50.0.0: the inline heredoc + entity-stub + + // throttle + backlink prose moved to docs/gbrain-write-surfaces.md + // §Save Template, which the agent reads on demand when it actually + // saves. The compact pointer keeps non-gbrain users' token overhead + // near zero when their host's static suppression is overridden by + // detection. + const meta = skillSaveMap[ctx.skillName]; - const saveInstruction = skillSaveMap[ctx.skillName] || 'Save the skill output as a brain page if the results are worth preserving:\n```bash\ngbrain put "<slug>" --content "$(cat <<\'EOF\'\n---\ntitle: "<descriptive title>"\ntags: [<relevant>, <tags>]\n---\n<content in markdown>\nEOF\n)"\n```'; + if (!meta) { + return `## Save Results to Brain + +**Skip this entire section if \`gbrain\` is not on PATH.** + +If the skill output is worth preserving, save it via +\`gbrain put "<slug>" --content "<frontmatter + markdown>"\`. Full template +(heredoc body, frontmatter shape, entity-stub instructions, throttle +handling): see \`docs/gbrain-write-surfaces.md\` §Save Template.`; + } return `## Save Results to Brain -After completing this skill, persist the results to your brain for future reference: +**Skip this entire section if \`gbrain\` is not on PATH.** -${saveInstruction} +After completing this skill, save the output: -After saving the page, extract and enrich mentioned entities: for each actual person name or company/organization name found in the output, \`gbrain search "<entity name>"\` to check if a page exists. If not, create a stub page: \`\`\`bash -gbrain put "entities/<entity-slug>" --content "$(cat <<'EOF' +gbrain put "${meta.slugPrefix}/<feature-slug>" --content "$(cat <<'EOF' --- -title: "<Person or Company Name>" -tags: [entity, person] +title: "${meta.title}: <feature name>" +tags: [${meta.tag}, <feature-slug>] --- -Stub page. Mentioned in <skill name> output. +<skill output in markdown> EOF )" \`\`\` -Only extract actual person names and company/organization names. Skip product names, section headings, technical terms, and file paths. -Throttle errors appear as: exit code 1 with stderr containing "throttle", "rate limit", "capacity", or "busy". If GBrain returns a throttle or rate-limit error on any save operation, defer the save and move on. The brain is busy — the content is not lost, just not persisted this run. Any other non-zero exit code should also be treated as a transient failure. - -Add backlinks to related brain pages if they exist. If GBrain is not available, skip this step. - -After brain operations complete, note in your completion output: how many pages were found in the initial search, how many entities were enriched, and whether any operations were throttled. This helps the user see brain utilization over time.`; +Then extract person/org entities and create stub pages for each one. +Throttle errors (exit 1 with "throttle"/"rate limit"/"busy") and any +other non-zero exit are transient — don't retry inline. Full entity-stub +template, throttle handling, and backlink protocol: +see \`docs/gbrain-write-surfaces.md\` §Save Template.`; } // ────────────────────────────────────────────────────────────────────