mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-28 14:59:47 +00:00
* feat(skillopt): wire held-out gate, honest receipts, ENFORCE + ablation opts Wire the F11 held-out gate into the orchestrator at checkpoint acceptance (runHeldOutGate was dead code); parse + thread --held-out through CLI, batch, fleet, background job, and the run_skillopt MCP op. Populate the real receipt.baseline_sel_score (was hardcoded 0) and add a final-test eval (test_score + baseline_test_score) via a shared scoreSkillOnTasks primitive. Fix the --no-mutate proposed.md write (was a stub) and enforce maxRuntimeMin. D16 ENFORCE in core mutation policy (assertBundledMutationHeldOut): mutating a bundled skill in place requires a non-empty (>=5), benchmark-disjoint held-out set or hard-refuses. Add three eval-internal ablation opts (reflectMode, disableValidationGate, optimizerMode='one-shot-rewrite') recorded in the receipt + audit; ROLLOUT_SUCCESS_THRESHOLD named constant. Security: run_skillopt MCP op validates skill_name (kebab-only) and confines caller-supplied benchmark/held-out paths to the skills dir for remote callers. * test(skillopt): held-out gate, ENFORCE, one-shot rewrite, runtime + receipt honesty New test/skillopt/rollout.test.ts (rollout had zero coverage). Held-out ENFORCE unit cases + one-shot-rewrite fence handling (whole-response unwrap, embedded-fence preserved, error path). E2E: F11 held-out BLOCKS/ALLOWS, bundled no-mutate write, reflectMode/disableValidationGate/optimizerMode, maxRuntimeMin abort, receipt baseline/test-score honesty, held-out/benchmark disjointness, D2 no-DB-pollution. * chore: bump version and changelog (v0.42.9.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: document skillopt held-out gate + bundled mutation requirement for v0.42.9.0 Wire --held-out into the skill-optimizer SKILL.md, guide flags/safety tables, and the tutorial's bundled-skill step: mutating a bundled skill in place now requires --allow-mutate-bundled AND --held-out (>=5 benchmark-disjoint tasks) or it hard-refuses. Add the --held-out flag row + F11 held-out gate to the guide; update the receipt contract to the honest baseline/test-score fields. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gateway): AI SDK v6 toolLoop compat — multi-turn tool calls work again The ai@6.x bump tightened ModelMessage + tool-schema validation, which silently broke every multi-turn tool loop. Both `gbrain skillopt` rollouts and production background `subagent` jobs route through `chat()`/`toolLoop` and crashed the moment the model called a tool ("messages do not match the ModelMessage[] schema" / "schema is not a function"). Surfaced end-to-end by the SkillOpt real-LLM eval. Three fixes: - chat(): wrap tool defs with the SDK's `jsonSchema()` helper instead of a bare `{jsonSchema}` object (v6 asSchema() treated the bare object as a thunk and threw). - chat(): new exported pure `toModelMessages()` converts gbrain's provider-neutral ChatMessage[] into v6 ModelMessage[] — tool results ride a dedicated `role:'tool'` message with structured `{type,value}` output; null output preserved as json null. Load-bearing for the production subagent path, not just skillopt. - rollout.ts: replace the inline params→schema mapper (dropped `items` on array params) with the shared `paramDefToSchema` single source of truth. Pinned by test/gateway-model-messages.test.ts (8 cases). Folds into the open v0.42.9.0 PR (#1759) — these complete the eval-readiness wave by making skillopt actually run against a live model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(skillopt): budget no-pricing for Haiku silently scored every rollout 0 Surfaced by the SkillOpt real-LLM eval (Track B). Two coupled bugs that made a budget-capped Haiku run report a vacuous "0/N" measurement in ~2ms with zero LLM calls — indistinguishable from a real deficient-skill score: 1. Claude Haiku 4.5's canonical dateless id (`claude-haiku-4-5`) was missing from anthropic-pricing.ts (only the dated `-20251001` was present). With `--max-cost` set, BudgetTracker.reserve() threw no_pricing on the FIRST chat() of every rollout. Added the dateless entry (sonnet already had its dateless form). 2. runValidationGate swallowed that BUDGET_EXHAUSTED error — runWithLimit settled it as {ok:false}, which the gate turned into median:0. A pricing/cap crash became a fake score. The gate now scans settled results for isMustAbortError() and re-throws so the caller aborts loudly; ordinary (non-abort) rollout errors still fail-open to 0 (judge-hiccup posture kept). Pinned by test/skillopt/validate-gate-abort.test.ts (3 cases). Folds into the open v0.42.9.0 PR (#1759). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): llms-full.txt over size budget — drop what-schemas-unlock from full bundle The toolLoop + budget bug-fix annotations grew CLAUDE.md, pushing llms-full.txt to 756KB over the 750KB FULL_SIZE_BUDGET (the `build-llms > size budget` test failed, failing the `test` CI job). CLAUDE.md stays inlined by design (it's the point of the one-fetch bundle), so per the budget comment's own guidance ("ship with includeInFull=false exclusions") this excludes docs/what-schemas-unlock.md (15.4KB value-explainer, not load-bearing operational reference) from llms-full.txt; it stays linked in llms.txt. Bundle now 740KB with ~9KB headroom. No budget bump — 750KB is near the ~190k-token-context fit ceiling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(ci): re-admit policy docs into ci-cache-hash before doc relocation docs/**/*.md is deny-listed from the CI cache hash (test-irrelevant). The CLAUDE.md restructure moves test/release POLICY into docs/TESTING.md + docs/RELEASING.md, which DO carry contracts the test suite reads. Without re-admitting them, a policy-only edit would produce the same cache hash and skip the test shard that runs the build-llms + doc-history guards (false-pass). Adds an ALLOW_PATTERNS re-admit step after the deny, scoped to the named policy docs (not a blanket docs un-deny). Lands FIRST, before any doc moves. Pinned by 3 new cases in test/scripts/ci-cache-hash.test.ts: TESTING.md + RELEASING.md edits MUST change the hash; docs/guide.md still must not. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(docs): relocate Key files / thin-client / Testing out of CLAUDE.md (verbatim) CLAUDE.md had grown to 592KB / ~147k tokens auto-loaded every session (~77% of the llms-full.txt single-fetch bundle). The per-file index was append-only by mandate. This is the exact thin-dispatcher-vs-fat-blob anti-pattern gbrain exists to fix, so CLAUDE.md becomes a thin orientation + resolver that points at on-demand docs. This commit is the VERBATIM move (content-preserving — the next commit compresses): - docs/architecture/KEY_FILES.md <- ## Key files + the calibration key-files cluster + Schema Cathedral v3 impl detail - docs/architecture/thin-client.md <- ## Thin-client routing - docs/TESTING.md <- ## Testing - ## Commands DROPPED (18 'added in vX.Y' history blocks; current surface is gbrain 0.41.38.0 -- personal knowledge brain USAGE gbrain <command> [options] SETUP init [--pglite|--supabase|--url] Create brain (PGLite default, no server) migrate --to <supabase|pglite> Transfer brain between engines upgrade Self-update check-update [--json] Check for new versions doctor [--json] [--fast] Health check (resolver, skills, pgvector, RLS, embeddings) integrations [subcommand] Manage integration recipes (senses + reflexes) PAGES get <slug> Read a page put <slug> [< file.md] Write/update a page delete <slug> Delete a page list [--type T] [--tag T] [-n N] List pages SEARCH search <query> Keyword search (tsvector) query <question> [--no-expand] Hybrid search (RRF + expansion) ask <question> [--no-expand] Alias for query IMPORT/EXPORT import <dir> [--no-embed] Import markdown directory sync [--repo <path>] [flags] Git-to-brain incremental sync sync --watch [--interval N] Continuous sync (loops until stopped) sync --install-cron Install persistent sync daemon export [--dir ./out/] Export to markdown export --restore-only [--repo <p>] Restore missing supabase-only files [--type T] [--slug-prefix S] With optional filters FILES files list [slug] List stored files files upload <file> --page <slug> Upload file to storage files upload-raw <file> --page <s> Smart upload (size routing + .redirect.yaml) files signed-url <path> Generate signed URL (1-hour) files sync <dir> Bulk upload directory files verify Verify all uploads EMBEDDINGS embed [<slug>|--all|--stale] Generate/refresh embeddings LINKS link <from> <to> [--type T] Create typed link unlink <from> <to> Remove link backlinks <slug> Incoming links graph <slug> [--depth N] Traverse link graph (returns nodes) graph-query <slug> [--type T] Edge-based traversal with type/direction filters [--depth N] [--direction in|out|both] TAGS tags <slug> List tags tag <slug> <tag> Add tag untag <slug> <tag> Remove tag TIMELINE timeline [<slug>] View timeline timeline-add <slug> <date> <text> Add timeline entry TOOLS extract <links|timeline|all> Extract links/timeline (idempotent) [--source fs|db] fs (default) walks .md files; db iterates engine pages [--dir <brain>] brain dir for fs source [--type T] [--since DATE] filters (db source) [--dry-run] [--json] publish <page.md> [--password] Shareable HTML (strips private data, optional AES-256) check-backlinks <check|fix> [dir] Find/fix missing back-links across brain lint <dir|file> [--fix] Catch LLM artifacts, placeholder dates, bad frontmatter orphans [--json] [--count] Find pages with no inbound wikilinks salience [--days N] [--kind P] v0.29: pages ranked by emotional + activity salience anomalies [--since D] [--sigma N] v0.29: cohort-based statistical anomalies (tag, type) transcripts recent [--days N] v0.29: recent raw .txt transcripts (local-only) dream [--dry-run] [--json] Run the overnight maintenance cycle once (cron-friendly). See also: autopilot --install (continuous daemon). check-resolvable [--json] [--fix] Validate skill tree (reachability/MECE/DRY) report --type <name> --content ... Save timestamped report to brain/reports/ BRAIN (capture / ideate / explore — v0.37/v0.38) capture [content] [--file PATH] Single entrypoint for getting content into the brain [--stdin] [--slug s] [--type t] Inline content / file / stdin; writes to inbox/ by default [--source ID] [--quiet|--json] Multi-source brains: route to a non-default source brainstorm <question> [--json] Bisociation idea generator (hybrid search + far-set + judge) [--save|--no-save] [--limit N] lsd <question> [--json] Lateral Synaptic Drift: inverted-judge brainstorm [--save|--no-save] [--limit N] rewarding far-from-obvious + axiomatic inversions SOURCES (multi-repo / multi-brain) sources list Show registered sources sources add <id> --path <p> Register a source (id = short name, e.g. 'wiki') sources remove <id> Remove a source + its pages sync --all Sync all sources with a local_path sync --source <id> Sync one specific source repos ... DEPRECATED alias for 'sources' (v0.19.0) CODE INDEXING (v0.19.0 / v0.20.0 Cathedral II) code-def <symbol> [--lang l] Find the definition of a symbol across code pages code-refs <symbol> [--lang l] Find all references to a symbol (JSON-first) code-callers <symbol> Who calls this symbol? (v0.20.0 A1) code-callees <symbol> What does this symbol call? (v0.20.0 A1) query <q> --lang <l> Filter hybrid search to one language (v0.20.0) query <q> --symbol-kind <k> Filter to symbol type (function|class|method|...) (v0.20.0) reconcile-links [--dry-run] Batch-recompute doc↔impl edges (v0.20.0) reindex-code [--source id] [--yes] Explicit code-page reindex (v0.20.0) sync --strategy code Sync code files into the brain JOBS (Minions) jobs submit <name> [--params JSON] Submit background job [--follow] [--dry-run] jobs list [--status S] [--limit N] List jobs jobs get <id> Job details + history jobs cancel <id> Cancel job jobs retry <id> Re-queue failed/dead job jobs prune [--older-than 30d] Clean old jobs jobs stats Job health dashboard jobs work [--queue Q] Start worker daemon (Postgres only) ADMIN stats Brain statistics health Brain health dashboard history <slug> Page version history revert <slug> <version-id> Revert to version features [--json] [--auto-fix] Scan usage + recommend unused features autopilot [--repo] [--interval N] Self-maintaining brain daemon config [show|get|set] <key> [val] Brain config storage status [--repo <path>] Storage tier status and health [--json] (git-tracked vs supabase-only) serve MCP server (stdio) serve --http [--port N] HTTP MCP server with OAuth 2.1 --token-ttl N Access token TTL in seconds (default: 3600) --enable-dcr Enable Dynamic Client Registration --public-url URL Public issuer URL (required behind proxy/tunnel) call <tool> '<json>' Raw tool invocation version Version info --tools-json Tool discovery (JSON) Run gbrain <command> --help for command-specific help. + the per-command KEY_FILES entries; content stays in git) CLAUDE.md gains: a Reference map (resolver), a Maintaining section (the anti-disease rule), and a Cross-cutting invariants subsection under Architecture so the must-never-violate rules (trust fail-closed, sourceScopeOpts isolation, JSONB trap, engine parity, contract-first, migrations, multi-source) still auto-load after the index moved out. Result: CLAUDE.md 592KB -> 61KB; llms-full.txt 740KB -> 210KB (new docs link-only until compressed). build-llms drift + budget test green; verify 29/29 green. The pre-move content is recoverable at git show <this^>:CLAUDE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(docs): compress relocated docs to current-state + add recurrence guard Compresses the verbatim-relocated reference docs from append-only release-history to current-state-only (the disease cure), then makes recurrence structurally impossible via a CI guard. Compression (fan-out subagents + adversarial verify, audited mechanically): - KEY_FILES.md 453KB -> 356KB; TESTING.md 42KB -> 38KB; thin-client.md already clean. - 393/393 entries preserved; every src/test/scripts path from the verbatim original survives (mechanical comm-check); zero bolded **v0. markers remain. - Conservative ratio (~22%) because the content is invariant-dense — correctness over brevity. Dropped: **vX.Y.Z (#NNN):** clauses, codex/review tags, contributor credits, PR-numbers-as-ids, pre-fix/then/was-now history deltas. Kept: every exported symbol, invariant, and Pinned-by reference. Verbatim original recoverable at git show <relocation-commit>:docs/architecture/KEY_FILES.md. Recurrence guard (scripts/check-key-files-current-state.sh, wired into verify + check:all): - HARD: bans the bolded **v0.<digit> marker in the reference docs (scoped — plain 'as of pgvector 0.7' prose is fine, no false positives). - HARD: CLAUDE.md size cap (90KB; currently 61KB) — the structural backstop. - Pinned by test/scripts/check-key-files-current-state.test.ts (7 cases). Content contracts (test/build-llms.test.ts, +5 cases per codex outside-voice): CLAUDE.md keeps inline ship IRON RULES (version format, document-release, never-hand-roll); AGENTS.md keeps its boot order; llms indexes the new docs; KEY_FILES stays link-only (not inlined). Privacy: scrubbed the relocated 'wintermute/chat/' source-boost examples + the literal harvest-lint regex to generic placeholders (legitimate in allowlisted CLAUDE.md; genericized for the new public docs per the privacy rule). Reverts the284c50a4band-aid: re-inlines docs/what-schemas-unlock.md now that the restructure freed ~530KB of bundle headroom (llms-full.txt 740KB -> 225KB). verify 30/30 green (incl. new check:doc-history). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(docs): relocate verbose release process to docs/RELEASING.md The highest-/ship-risk commit (isolated so it can revert alone). Moves the verbose release + contributor procedure out of CLAUDE.md, keeping every ship-critical IRON RULE inline so /ship + /document-release (which read CLAUDE.md) cannot regress. Moved to docs/RELEASING.md: pre-ship test requirements; the CHANGELOG-branch-scoped + CHANGELOG voice + release-summary template; the 'To take advantage of vX' block spec; version migrations + migration-is-canonical; schema state tracking; GitHub Actions SHA maintenance; PR-descriptions-cover-the-branch; community-PR-wave; checking-out-PRs-from-garrytan-agents. Kept INLINE in CLAUDE.md (ship-critical IRON RULES — do NOT move): - the Version-locations table (5-file sync) + the 3-line consistency audit - Conductor branch=workspace - Post-ship /document-release (MANDATORY) - Privacy + Responsible-disclosure rules (Privacy also anchors the check-privacy allowlist — the only place allowed to name the fork) - PR-title-version-first - never-hand-roll-ship (Skill routing) Plus a new ## Releasing pointer ('Before any ship, read docs/RELEASING.md in full') and a resolver row. CLAUDE.md 61KB -> 39KB (592KB -> 39KB overall, 93% cut; ~9k tokens auto-loaded vs ~147k). CLAUDE.md size-gate tightened 90KB -> 60KB. The content-contract tests pin that the inline IRON RULES (MAJOR.MINOR.PATCH.MICRO, document-release, hand-roll ship) did NOT move out. The moved ranges carry no banned fork name, so RELEASING.md needs no privacy allowlist entry. verify 30/30; bundle 225KB -> 204KB. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(changelog): note CLAUDE.md restructure in v0.42.9.0 The CLAUDE.md thin-resolver restructure (592KB → 39KB) rides in this release; record it under the existing v0.42.9.0 For-contributors section. No version bump — v0.42.9.0 is unreleased and already allocated to this PR. * fix(ci): ci-cache-hash re-admit matched a literal \t, a no-op on GNU grep The policy-doc re-admit (75992b77) put `\t` inline in the ALLOW patterns passed to `grep -E`. BSD grep (macOS local) treats `\t` as a tab so it worked locally; GNU grep (Ubuntu CI) treats it as literal `t`, so nothing re-admitted and docs/TESTING.md / docs/RELEASING.md stayed deny-listed — the two policy-doc tests failed on CI shard 6 (1097 pass / 2 fail). Build ALLOW_RE with `printf '\t(%s)'` so the tab is a real byte, identical in construction to DENY_RE (line 117), which the CI log shows matches correctly on GNU grep. End-to-end: editing docs/TESTING.md now flips the hash; a normal docs/*.md add still does not (deny stays scoped). * fix(skillopt): feed the scorer's success criteria to the optimizer Surfaced by the SkillOpt real-LLM eval (Track B). The reflect step was shown only a pass/fail score and the agent transcript — never WHAT the benchmark judge rewards. On a skill judged by structure (e.g. "must include a Confidence: line") the optimizer proposed plausible-but-off edits ("close with a synthesis") that never satisfied the literal check; every candidate scored 0 on D_sel, the validation gate rejected them all, and the skill text never changed (optimized === baseline === 0). Fix: render each benchmark Judge (rule checks / llm rubric / qrels) into plain-English criteria via new exported describeJudge / describeJudges, and thread them into the reflect prompt (a SUCCESS CRITERIA block) for both the loop reflect calls and the one-shot-rewrite path. The orchestrator computes the distinct criteria across train+sel+test once. The optimizer system prompt now instructs it to satisfy the criteria through genuine content, never empty keywords — reward-hacking stays defended by the independent held-out gate (cat32 confirms the gate catches a keyword-stuffing hack). End-to-end this took a deficient skill from 0.00 to 1.00 on a held-out set it never trained on. Pinned by test/skillopt/reflect.test.ts (describeJudge per kind, describeJudges dedup, criteria present/absent in the prompt). Folds into the open v0.42.9.0 PR (#1759). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
434 lines
22 KiB
Markdown
434 lines
22 KiB
Markdown
# Releasing & contributing (gbrain)
|
|
|
|
The full release + contributor process. CLAUDE.md keeps the ship-critical IRON RULES
|
|
inline (the Version-locations table, branch=workspace, post-ship `/document-release`,
|
|
the Privacy + Responsible-disclosure rules, PR-title-version-first, never-hand-roll-ship)
|
|
and points here for everything else. **Before any ship, read this in full. Use `/ship` —
|
|
never hand-roll a release.**
|
|
|
|
## Pre-ship requirements
|
|
|
|
Before shipping (/ship) or reviewing (/review), always run the full test suite.
|
|
Two equivalent paths:
|
|
|
|
**Path A — local CI gate (recommended, v0.23.1+):**
|
|
- `bun run ci:local` runs the entire stack inside Docker: gitleaks (host), unit
|
|
tests with `DATABASE_URL` unset, and all 29 E2E files sequentially against a
|
|
fresh pgvector container. Stronger than PR CI's 2-file Tier 1 set; closer to
|
|
what nightly Tier 1 catches. Spins up + tears down postgres automatically via
|
|
`docker-compose.ci.yml`. Override the host port with
|
|
`GBRAIN_CI_PG_PORT=5435 bun run ci:local` if 5434 collides.
|
|
- `bun run ci:local:diff` runs only the E2E files matched by the diff selector
|
|
(`scripts/select-e2e.ts`), falling back to all 29 on unmapped src/ paths or
|
|
schema/skills/package.json changes. Fast iteration during a focused branch.
|
|
|
|
**Path B — manual lifecycle (still supported):**
|
|
- `bun test` — unit tests (no database required)
|
|
- Follow the "E2E test DB lifecycle" steps above to spin up the test DB,
|
|
run `bun run test:e2e`, then tear it down.
|
|
|
|
Both must pass. Do not ship with failing E2E tests. Do not skip E2E tests.
|
|
|
|
**Always run typecheck before pushing.** `bun test` (the bun runner)
|
|
skips TypeScript type checking — it only enforces runtime behavior.
|
|
Three ways to actually gate on types:
|
|
|
|
1. `bun run test` (npm script in `package.json`) — includes `bun run typecheck`
|
|
plus the four shell pre-checks (`check-jsonb-pattern.sh`,
|
|
`check-progress-to-stdout.sh`, `check-trailing-newline.sh`,
|
|
`check-wasm-embedded.sh`) before the runner. Use this mid-branch.
|
|
2. `bun run typecheck` — `tsc --noEmit` standalone. Fast (~5s on this repo).
|
|
3. `bun run ci:local` — the full local CI gate from Path A.
|
|
|
|
The trap is: writing a new test, running `bun test test/foo.test.ts`,
|
|
seeing it pass, pushing — and CI's separate typecheck stage rejects an
|
|
invalid type literal that the runner accepted. Caught one of these
|
|
shipping the v0.23.2 round-trip E2E (`type: 'reflection'` is not a
|
|
member of `PageType`). Run `bun run typecheck` once before push, even
|
|
when only test files changed.
|
|
|
|
|
|
## CHANGELOG + VERSION are branch-scoped
|
|
|
|
**VERSION and CHANGELOG describe what THIS branch adds vs master, not how we got
|
|
here.** Every feature branch that ships gets its own version bump and CHANGELOG
|
|
entry. The entry is product release notes for users; it is not a log of internal
|
|
decisions, review rounds, or codex findings.
|
|
|
|
**Write the CHANGELOG entry at /ship time, not during development.** Mid-branch
|
|
iterations, review rounds (CEO/Eng/Codex/DX), and implementation detours belong
|
|
in the plan file at `~/.claude/plans/`, not in the CHANGELOG. One unified entry
|
|
per branch, covering what the branch added vs the base branch.
|
|
|
|
**Never edit a CHANGELOG entry that already landed on master.** If master has
|
|
v0.18.2 and your branch adds features, bump to the next version (v0.19.0, not
|
|
editing master's v0.18.2). When merging master into your branch, master may
|
|
bring new CHANGELOG entries above yours — push your entry above master's
|
|
latest and verify:
|
|
|
|
- Does CHANGELOG have your branch's own entry separate from master's entries?
|
|
- Is VERSION higher than master's VERSION?
|
|
- Is your entry the topmost `## [X.Y.Z]` entry?
|
|
- `grep "^## \[" CHANGELOG.md` shows a contiguous version sequence?
|
|
|
|
If any answer is no, fix it before continuing.
|
|
|
|
**CHANGELOG is for users, not contributors.** Write like product release notes:
|
|
|
|
- Lead with what the user can now **do** that they couldn't before. Sell the capability.
|
|
- Plain language, not implementation details. "You can now..." not "Refactored the..."
|
|
- **Never mention internal artifacts**: plan file IDs, decision tags (D-CX-#, F-ENG-#),
|
|
review rounds, codex findings, subcontractor credits. These are invisible to users.
|
|
- Put contributor-facing changes in a separate `### For contributors` section at the bottom.
|
|
- Every entry should make someone think "oh nice, I want to try that."
|
|
|
|
**What to omit:**
|
|
- "Codex caught X that the CEO review missed" — private process detail.
|
|
- "D-CX-3 split errors/warnings" — tag is meaningless to users; name the feature instead.
|
|
- "Fix-wave PR #N supersedes #M" — supersede chains belong in PR bodies, not release notes.
|
|
- "215 new cases, 3 decisions applied, 7 reviews cleared" — these are planning-mode metrics.
|
|
|
|
**What to keep:**
|
|
- The user-facing change: what commands exist now, what flag was added, what behavior fixed.
|
|
- Numbers that mean something to the user: TTHW, commands that timed out before, detection counts.
|
|
- Upgrade instructions: `gbrain upgrade` + any manual step if needed.
|
|
- Credit to external contributors when a community PR was incorporated.
|
|
|
|
## CHANGELOG voice + release-summary format
|
|
|
|
**IRON RULE: the CHANGELOG describes what the user gets, not how the work
|
|
happened.** Nobody reading release notes cares that codex caught a bug, that
|
|
the plan went through CEO + eng review, that the migration was originally
|
|
numbered v68 and renumbered to v79 during master merge, or that two
|
|
review rounds caught architectural mistakes. The reader cares what
|
|
`gbrain brainstorm` does and how to use it. If a fact only exists because
|
|
of the development process, it does NOT belong in the CHANGELOG.
|
|
|
|
**Specifically forbidden in CHANGELOG entries:**
|
|
|
|
- Any mention of review processes (CEO review, eng review, codex review,
|
|
plan-eng-review, outside voice, adversarial review, autoplan, /review).
|
|
- "What we caught and fixed before merging" sections. Bugs found pre-merge
|
|
are not changes — they're things that didn't ship.
|
|
- Plan file references, plan IDs, plan decision tags (D1, D14, D-CDX-3).
|
|
- Migration version drama ("originally v68", "renumbered to v77", "claimed
|
|
by parallel waves") — just say "Migration v79 adds X." If the user
|
|
cares about migration ordering, they read the diff.
|
|
- Round counts, finding counts, decision counts ("25 findings across 2
|
|
rounds", "8 architectural decisions", "5/6 expansions accepted").
|
|
- Names of internal collaborators ("codex caught", "the reviewer flagged",
|
|
"Claude noticed").
|
|
- "Plan + reviews" summary bullets. The plan lives in `~/.claude/plans/`;
|
|
if a future reader wants the backstory they can grep there.
|
|
- Any wording that frames a shipped feature as a *recovery* from a planning
|
|
mistake ("the first plan was wrong", "we corrected the approach", "the
|
|
shipped version supersedes the original design").
|
|
|
|
**Smell test:** read the entry as a stranger who has never touched gbrain.
|
|
If any sentence makes them think "why are you telling me this?", cut it.
|
|
Every sentence in the release-summary AND in the itemized changes must
|
|
answer one of three questions: *What can I now do? How do I use it? What
|
|
should I watch for after I upgrade?*
|
|
|
|
Every version entry in `CHANGELOG.md` MUST start with a release-summary section in
|
|
the GStack/Garry voice — one viewport's worth of prose + tables that lands like a
|
|
verdict, not marketing. The itemized changelog (subsections, bullets, files) goes
|
|
BELOW that summary, separated by a `### Itemized changes` header.
|
|
|
|
The release-summary section gets read by humans, by the auto-update agent, and by
|
|
anyone deciding whether to upgrade. The itemized list is for agents that need to
|
|
know exactly what changed.
|
|
|
|
### Release-summary template
|
|
|
|
**Iron rule: lead ELI10, get precise after.** The first ~150 words of every entry
|
|
must be readable by someone who does NOT know gbrain's internals. No file paths,
|
|
no function names, no internal constants, no acronyms (no "RRF", no "knobsHash",
|
|
no "MODE_BUNDLES", no "CDX-4"), no jargon that requires reading the codebase to
|
|
parse. Lead with the user-visible behavior change, in everyday English, like
|
|
you're explaining it to a smart engineer who has never opened the repo.
|
|
|
|
THEN, once the reader knows what shipped and why they'd care, drill into the
|
|
precise details: real file paths, real function names, real config keys, real
|
|
numbers. The precision part is required (the entry is also the technical record
|
|
of what changed), but it lives AFTER the plain-English lead, never before it.
|
|
|
|
The shape:
|
|
|
|
1. **One-line bold headline.** What changed for the user, in human English. No
|
|
jargon. No internal terms. Example good: "Your search stops boosting weak
|
|
pages just because they have a lot of links pointing at them." Example bad:
|
|
"PostFusionOpts gains floorRatio; KNOBS_HASH_VERSION bumped 2→3."
|
|
2. **Plain-English opener** (~3-5 sentences). Describe the problem this fixes in
|
|
everyday terms. Pretend the reader has a brain full of meeting notes and
|
|
people pages and wants to know if this release helps them. Concrete example
|
|
beats abstract description.
|
|
3. **A "How to turn it on" or "How to use it" section** with paste-ready
|
|
commands. Real flags, real config keys. This is where precision starts.
|
|
4. **A "What you'd see in a concrete example" or "The X numbers that matter"
|
|
section** with a table. Use everyday-language column headers ("Page",
|
|
"Match quality", "Has many backlinks?") even when the underlying mechanism
|
|
is technical. The table teaches what the feature does without requiring the
|
|
reader to understand how.
|
|
5. **A "What's safe to know about" or "Things to watch" section** for caveats,
|
|
side effects, cache invalidation, mid-deploy notes. Still in plain language.
|
|
6. **A "What we caught and fixed before merging" section** if the work went
|
|
through review (CEO/eng/codex/outside-voice). Translate review findings into
|
|
plain English. "We caught a stale-cache bug" beats "knobsHash() did not
|
|
include floorRatio in the v=2 hash input."
|
|
7. **`### Itemized changes`** (precision lives here). File paths, function
|
|
names, types, constants, line numbers. This section is for engineers who
|
|
need to know exactly what moved.
|
|
|
|
Voice rules (apply throughout):
|
|
- No em dashes (use commas, periods, "...").
|
|
- No AI vocabulary (delve, robust, comprehensive, nuanced, fundamental, etc.) or
|
|
banned phrases ("here's the kicker", "the bottom line", etc.).
|
|
- Real numbers, real file names, real commands AFTER the ELI10 lead. Not "fast"
|
|
but "~30s on 30K pages." In the ELI10 lead, "fast enough that you won't
|
|
notice" or "~30 seconds even on a big brain."
|
|
- Short paragraphs, mix one-sentence punches with 2-3 sentence runs.
|
|
- Connect to user outcomes: "the agent does ~3x less reading" beats "improved
|
|
precision."
|
|
- Be direct about quality. "Well-designed" or "this is a mess." No dancing.
|
|
|
|
**The smell test:** if someone who has never opened gbrain reads the first 150
|
|
words and walks away knowing what shipped and whether they care, the entry
|
|
passes. If they need to grep the codebase to follow along, rewrite the lead.
|
|
|
|
**Canonical examples in this CHANGELOG:** v0.35.6.0 (floor-ratio gate, written
|
|
ELI10-lead-first), v0.34.4.0 (embed stale fix wave). Use those shapes when in
|
|
doubt. Avoid the shape of entries that lead with internal constants or release
|
|
mechanics; those exist in older history but should not be the model for new
|
|
work.
|
|
|
|
Source material to pull from:
|
|
- CHANGELOG.md previous entry for prior context
|
|
- Latest `gbrain-evals/docs/benchmarks/[latest].md` for headline numbers (sibling repo)
|
|
- Recent commits (`git log <prev-version>..HEAD --oneline`) for what shipped
|
|
- Don't make up numbers. If a metric isn't in a benchmark or production data, don't
|
|
include it. Say "no measurement yet" if asked.
|
|
|
|
Target length: ~250-350 words for the summary. Should render as one viewport.
|
|
|
|
### "To take advantage of v[version]" block (required, v0.13+)
|
|
|
|
After the release-summary and BEFORE `### Itemized changes`, every `## [X.Y.Z]`
|
|
entry MUST include a human-readable self-repair block under the heading
|
|
`## To take advantage of v[version]`.
|
|
|
|
Why: `gbrain upgrade` runs `gbrain post-upgrade` which runs `gbrain apply-migrations`.
|
|
This chain has a known weak link — `upgrade.ts` catches post-upgrade failures as
|
|
best-effort (so the binary still works). When that chain silently fails, users end
|
|
up with half-upgraded brains. The self-repair block gives them a paste-ready
|
|
recovery path; the v0.13+ `~/.gbrain/upgrade-errors.jsonl` trail + `gbrain doctor`
|
|
integration close the loop.
|
|
|
|
Template (adapt the verify commands per release):
|
|
|
|
```markdown
|
|
## To take advantage of v[version]
|
|
|
|
`gbrain upgrade` should do this automatically. If it didn't, or if `gbrain doctor`
|
|
warns about a partial migration:
|
|
|
|
1. **Run the orchestrator manually:**
|
|
```bash
|
|
gbrain apply-migrations --yes
|
|
```
|
|
2. **Your agent reads `skills/migrations/v[version].md` the next time you interact with it.**
|
|
[One sentence on whether headless agents need manual action, or whether the
|
|
orchestrator already handled the mechanical side.]
|
|
3. **Verify the outcome:**
|
|
```bash
|
|
[release-specific verify commands, e.g. `gbrain graph ... --depth 2`]
|
|
gbrain stats
|
|
```
|
|
4. **If any step fails or the numbers look wrong,** please file an issue:
|
|
https://github.com/garrytan/gbrain/issues with:
|
|
- output of `gbrain doctor`
|
|
- contents of `~/.gbrain/upgrade-errors.jsonl` if it exists
|
|
- which step broke
|
|
|
|
This feedback loop is how the gbrain maintainers find fragile upgrade paths. Thank you.
|
|
```
|
|
|
|
**Skip this block** for patches that are pure bug fixes with zero user-facing action
|
|
(rare). If the release has a schema migration, data backfill, or new feature the
|
|
user needs to verify, the block is required.
|
|
|
|
The v0.13.0 entry in CHANGELOG.md is the canonical example.
|
|
|
|
### Itemized changes (the existing rules)
|
|
|
|
Below the release summary, write `### Itemized changes` and continue with the
|
|
detailed subsections (Knowledge Graph Layer, Schema migrations, Security hardening,
|
|
Tests, etc.). Same rules as before:
|
|
|
|
- Lead with what the user can now DO that they couldn't before
|
|
- Frame as benefits and capabilities, not files changed or code written
|
|
- Make the user think "hell yeah, I want that"
|
|
- Bad: "Added GBRAIN_VERIFY.md installation verification runbook"
|
|
- Good: "Your agent now verifies the entire GBrain installation end-to-end, catching
|
|
silent sync failures and stale embeddings before they bite you"
|
|
- Bad: "Setup skill Phase H and Phase I added"
|
|
- Good: "New installs automatically set up live sync so your brain never falls behind"
|
|
- **Always credit community contributions.** When a CHANGELOG entry includes work from
|
|
a community PR, name the contributor with `Contributed by @username`. Contributors
|
|
did real work. Thank them publicly every time, no exceptions.
|
|
|
|
### Reference: v0.12.0 entry as canonical example
|
|
|
|
The v0.12.0 entry in CHANGELOG.md is the canonical example of the format. Match its
|
|
structure for every future version: bold headline, lead paragraph, "numbers that
|
|
matter" with BrainBench-style before/after table, "what this means" closer, then
|
|
`### Itemized changes` with the detailed sections below.
|
|
|
|
## Version migrations
|
|
|
|
Create a migration file at `skills/migrations/v[version].md` when a release
|
|
includes changes that existing users need to act on. The auto-update agent
|
|
reads these files post-upgrade (Section 17, Step 4) and executes them.
|
|
|
|
**You need a migration file when:**
|
|
- New setup step that existing installs don't have (e.g., v0.5.0 added live sync,
|
|
existing users need to set it up, not just new installs)
|
|
- New SKILLPACK section with a MUST ADD setup requirement
|
|
- Schema changes that require `gbrain init` or manual SQL
|
|
- Changed defaults that affect existing behavior
|
|
- Deprecated commands or flags that need replacement
|
|
- New verification steps that should run on existing installs
|
|
- New cron jobs or background processes that should be registered
|
|
|
|
**You do NOT need a migration file when:**
|
|
- Bug fixes with no behavior changes
|
|
- Documentation-only improvements (the agent re-reads docs automatically)
|
|
- New optional features that don't affect existing setups
|
|
- Performance improvements that are transparent
|
|
|
|
**The key test:** if an existing user upgrades and does nothing else, will their
|
|
brain work worse than before? If yes, migration file. If no, skip it.
|
|
|
|
Write migration files as agent instructions, not technical notes. Tell the agent
|
|
what to do, step by step, with exact commands. See `skills/migrations/v0.5.0.md`
|
|
for the pattern.
|
|
|
|
## Migration is canonical, not advisory
|
|
|
|
GBrain's job is to deliver a canonical, working setup to every user on upgrade.
|
|
Anything that looks like a "host-repo change" — AGENTS.md, cron manifests,
|
|
launchctl units, config files outside `~/.gbrain/` — is a GBrain migration
|
|
step, not a nudge we leave for the host-repo maintainer. Migrations edit host
|
|
files (with backups) to make the canonical setup real. Exceptions: changes
|
|
that require human judgment (content edits, renames that break semantics,
|
|
host-specific handler registration where shell-exec would be an RCE surface).
|
|
Everything mechanical ships in the migration.
|
|
|
|
**Test:** if shipping a feature requires a sentence that starts with "in
|
|
your AGENTS.md, add…" or "in your cron/jobs.json, rewrite…", the migration
|
|
orchestrator should be doing that edit, not the user.
|
|
|
|
**The exception is host-specific code.** For custom Minion handlers
|
|
(host-specific integrations like inbox sweeps or third-party API scanners), shipping them as a
|
|
data file the worker would exec is an RCE surface. Those get registered in
|
|
the host's own repo via the plugin contract (`docs/guides/plugin-handlers.md`);
|
|
the migration orchestrator emits a structured TODO to
|
|
`~/.gbrain/migrations/pending-host-work.jsonl` + the host agent walks the
|
|
TODOs using `skills/migrations/v0.11.0.md` — stays host-agnostic, still
|
|
canonical.
|
|
|
|
|
|
## Schema state tracking
|
|
|
|
`~/.gbrain/update-state.json` tracks which recommended schema directories the user
|
|
adopted, declined, or added custom. The auto-update agent (SKILLPACK Section 17)
|
|
reads this during upgrades to suggest new schema additions without re-suggesting
|
|
things the user already declined. The setup skill writes the initial state during
|
|
Phase C/E. Never modify a user's custom directories or re-suggest declined ones.
|
|
|
|
## GitHub Actions SHA maintenance
|
|
|
|
All GitHub Actions in `.github/workflows/` are pinned to commit SHAs. Before shipping
|
|
(`/ship`) or reviewing (`/review`), check for stale pins and update them:
|
|
|
|
```bash
|
|
for action in actions/checkout oven-sh/setup-bun actions/upload-artifact actions/download-artifact softprops/action-gh-release gitleaks/gitleaks-action; do
|
|
tag=$(grep -r "$action@" .github/workflows/ | head -1 | grep -o '#.*' | tr -d '# ')
|
|
[ -n "$tag" ] && echo "$action@$tag: $(gh api repos/$action/git/ref/tags/$tag --jq .object.sha 2>/dev/null)"
|
|
done
|
|
```
|
|
|
|
If any SHA differs from what's in the workflow files, update the pin and version comment.
|
|
|
|
|
|
## PR descriptions cover the whole branch
|
|
|
|
Pull request titles and bodies must describe **everything in the PR diff against the
|
|
base branch**, not just the most recent commit you made. When you open or update a
|
|
PR, walk the full commit range with `git log --oneline <base>..<head>` and write the
|
|
body to cover all of it. Group by feature area (schema, code, tests, docs) — not
|
|
chronologically by commit.
|
|
|
|
This matters because reviewers read the PR body to understand what's shipping. If
|
|
the body only covers your last commit, they miss everything else and can't review
|
|
properly. A 7-commit PR with a body that describes commit 7 is worse than no body
|
|
at all — it actively misleads.
|
|
|
|
When in doubt, run `gh pr view <N> --json commits --jq '[.commits[].messageHeadline]'`
|
|
to see what's actually in the PR before writing the body.
|
|
|
|
## Community PR wave process
|
|
|
|
Never merge external PRs directly into master. Instead, use the "fix wave" workflow:
|
|
|
|
1. **Categorize** — group PRs by theme (bug fixes, features, infra, docs)
|
|
2. **Deduplicate** — if two PRs fix the same thing, pick the one that changes fewer
|
|
lines. Close the other with a note pointing to the winner.
|
|
3. **Collector branch** — create a feature branch (e.g. `garrytan/fix-wave-N`), cherry-pick
|
|
or manually re-implement the best fixes from each PR. Do NOT merge PR branches directly —
|
|
read the diff, understand the fix, and write it yourself if needed.
|
|
4. **Test the wave** — verify with `bun test && bun run test:e2e` (full E2E lifecycle).
|
|
Every fix in the wave must have test coverage.
|
|
5. **Close with context** — every closed PR gets a comment explaining why and what (if
|
|
anything) supersedes it. Contributors did real work; respect that with clear communication
|
|
and thank them.
|
|
6. **Ship as one PR** — single PR to master with all attributions preserved via
|
|
`Co-Authored-By:` trailers. Include a summary of what merged and what closed.
|
|
|
|
**Community PR guardrails:**
|
|
- Always AskUserQuestion before accepting commits that touch voice, tone, or
|
|
promotional material (README intro, CHANGELOG voice, skill templates).
|
|
- Never auto-merge PRs that remove YC references or "neutralize" the founder perspective.
|
|
- Preserve contributor attribution in commit messages.
|
|
|
|
## Checking out PRs from garrytan-agents
|
|
|
|
`garrytan-agents` is the AI-authored PR account and is NOT a collaborator on
|
|
this repo. Its PRs live in a fork, so GitHub Actions triggered by
|
|
`pull_request` events on those PRs do not receive base-repo secrets. Any CI
|
|
job that needs `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or similar will fail
|
|
with empty-env auth errors, regardless of what's set on the base repo. This
|
|
is a GitHub security default, not a config bug.
|
|
|
|
When the user says "check out <PR link>" and the PR is from `garrytan-agents`
|
|
(or any other non-collaborator fork), move the branch into the base repo
|
|
before running CI:
|
|
|
|
1. `gh pr checkout <N>` — pull down the fork's branch. Note the PR number and
|
|
head branch name (`gh pr view <N> --json headRefName --jq .headRefName`).
|
|
2. `git push origin HEAD:<branch-name>` — push the same branch to the base
|
|
repo (origin points at `garrytan/gbrain`, not the fork). This is the move
|
|
that gives CI access to secrets.
|
|
3. `gh pr close <N> --comment "moving to base-repo branch for secret access"`
|
|
— close the fork PR so the queue stays clean.
|
|
4. `gh pr create --base master --head <branch-name>` — open the replacement
|
|
PR from the base-repo branch. **Preserve the original PR's title and body
|
|
verbatim** (`gh pr view <N> --json title,body`); contributor attribution
|
|
moves to a `Co-Authored-By:` trailer if needed.
|
|
|
|
Why this over alternatives: adding `garrytan-agents` as a collaborator, or
|
|
flipping the repo-wide "send secrets to fork PRs" toggle, both broaden
|
|
secret distribution to every fork PR from that account or any fork. Moving
|
|
the branch keeps secret scope tight to just the one PR being shipped.
|
|
|