Download Latest Version v0.51.0.0 source code.zip (25.6 MB) Google Add to Preferred Sources
Home / v0.50.1.0
Name Modified Size InfoDownloads / Week
Parent folder
gbrain-darwin-arm64 2026-09-15 145.6 MB
gbrain-linux-x64 2026-09-15 183.7 MB
README.md 2026-09-15 40.7 kB
v0.50.1.0 source code.tar.gz 2026-09-15 21.7 MB
v0.50.1.0 source code.zip 2026-09-15 24.8 MB
Totals: 5 Items   375.8 MB 1

The community fix wave, rebased onto 0.50.0.0: 21 contributor pull requests adopted or reworked with credit, 35 verified open issues fixed directly. gbrain sync --json prints one JSON document again, so piping it into jq works, and a sync that only swept dead pages tells you so instead of "Already up to date". Brains on Gemini embeddings get a real dollar estimate in the cost gate, facts extraction on a local Ollama model asks for schema-constrained JSON so small models stop returning malformed output, and gbrain dream --dry-run stops billing you for takes and calibration while the patterns phase skips outright when nothing new has been reflected on. A remote search on a brain that has not been reindexed says so instead of claiming a clean miss, each Codex rollout lands on its own conversation page, and gbrain config set accepts the search.* keys the search path actually reads. A source marked syncEnabled: false is left alone by the daemon, the phantom redirect no longer aborts on a canonical page that already holds facts, and emoji folder names carrying an invisible variation selector slugify cleanly. Every adopted code fix carries a regression test proven red before the fix.

To take advantage of v0.50.1.0

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. No new schema migration ships in this release; the mechanical side is a binary upgrade. Brains whose pages predate the safe-chunk index now see degraded: [safe_index_pending] on empty remote search/query results until gbrain reindex --markdown (or a fresh import) rebuilds their chunks.
  3. Verify the outcome: bash gbrain doctor gbrain search modes

Say to your agent: "run the brain health check and tell me what changed in this upgrade"

Behavior changes (read before you upgrade)

  • gbrain sync --json prints exactly one JSON document. The cost-gate status object that used to print as a second top-level value now nests inside the envelope as cost_gate, for single-source and --all runs alike, so sync --json | jq and JSON.parse work as documented. schema_version stays 1; callers that read the gate from the first stdout line must read .cost_gate instead. (#4684)
  • A sweep-only sync reports what it did. When a pass only soft-deletes un-syncable pages, deleted counts the swept pages and the result reads synced instead of up_to_date / "Already up to date."; a run whose pull failed reports its swept pages the same way. The strategy root cause shipped in v0.48.5.0. (#4786)
  • gbrain config set accepts the search.* keys the search path actually reads. The registered keys are the camelCase search.tokenBudget, search.intentWeighting and search.searchLimit plus the reranker, cross-modal and relational knobs. The three snake_case spellings nothing read (search.token_budget, search.intent_weighting, search.limit_default) are still accepted through the search. prefix but have no reader; re-set any override under the name gbrain search modes prints for it. (#4605)
  • Remote searches on a not-yet-reindexed brain say so. Remote/MCP search and query on a brain whose pages predate the safe-chunk index now report degraded: [safe_index_pending] when they come back empty, instead of asserting a clean miss; the pages themselves are still withheld from remote callers, as before. gbrain doctor counts them and names the fix, gbrain reindex --markdown. (#5004)
  • Company links in list sections no longer inherit a person's role. A person page whose bio says "is an advisor" or "partner at" used to type every bare company link on the page with that role, including links inside Timeline, See-also, Related, Facts, Sources, Links, Email mention links, Backlinks and Significant moments sections. New extractions type those links as mentions; per-edge verbs still type normally everywhere. The extractor watermark moves to 2026-09-09, so the next gbrain extract --stale re-extracts every brain, but that re-run only adds mentions rows beside the older works_at / advises / invested_in edges: those rows are not removed and keep feeding who works at X style relational answers until a separate repair. (#4977, @ethanbeard)
  • By-mention checkpoints reset once. The extract --by-mention resume checkpoint now hashes every gazetteer entry, so it notices a new entity even when it shares a first word with an existing one. Existing checkpoints are invalidated once on upgrade: the first gbrain extract links --by-mention --source db after upgrading rescans every page. Checkpoints are resume state, not data; gbrain extract --stale does not perform this rescan. (#4858, @proxynico)
  • Gemini embeddings are priced. Cost estimates now know Google's gemini-embedding-001 and gemini-embedding-2, directly or through OpenRouter, so the sync cost gate and --max-cost checks stop tripping the no-pricing guard and stop pricing these models at another provider's rate. Expect a real dollar figure where you used to see "estimate unavailable". (#4953, @morven-ai; hosted-google coverage test from [#4989], @dov-kela)
  • gbrain dream --dry-run no longer bills. It skips propose_takes, grade_takes and calibration_profile (status skipped, reason no_dry_run_support) instead of making model calls and writing proposal, grade and profile rows; --help names the trio. (#4823)
  • The dream patterns phase skips when nothing new happened. A completed run records the newest reflection it consumed (dream.patterns.last_evidence_ts); until a newer reflection lands in the lookback window, re-runs skip with no_new_evidence instead of paying for another model pass, so autopilot's hourly tick cannot mint near-duplicate pattern pages. gbrain dream --phase patterns --once forces a run. (#4879)
  • context_pack and delta honor budget_tokens on the injectable text. Each item is costed as the line it renders to, and the envelope plus section headers are reserved up front (roughly 32 tokens for context_pack, 48 for delta with a since cursor). A budget below that reserve returns an empty pack: context_pack reports it through dropped_count equal to the item count (it has no has_more field), while delta also sets has_more: true. delta never drops open threads; only pages and facts count toward the budget. Unbudgeted output is unchanged. (#4761)
  • Codex rollouts land one page per thread. Each forked or subagent rollout gets its own conversation page, keyed by its own per-thread id (the root session id stays as a legacy fallback), so child rollouts no longer collapse onto the parent's page, re-import on every pass, or get dropped as duplicates. Existing root-session pages keep their slug; a parent page that had absorbed child content re-imports once. (#4981)
  • Emoji names with an invisible variation selector re-key. Folder and file names carrying the VS16 selector that macOS Finder and most pickers append now slugify to the clean slug, and [[wikilinks]] typed with the same emoji resolve to it. Pages keyed under the old selector-bearing slug re-key on their next touch or one gbrain sync --full; twins that already exist are not retired for you (gbrain orphans lists them), so rename or delete them once. (#4985, @ozp)
  • syncEnabled: false is honored by the daemon. Autopilot's freshness dispatcher skips such a source before queuing a sync job, the full-cycle fan-out drops the sync phase (and its pull) while keeping lint, backlinks, extract and the freshness stamp, and sync --all and gbrain sources status agree with it, including on PGLite brains. An explicit gbrain sync --source <id> naming the source still syncs it. (#4952, @Masashi-Ono0611)
  • The phantom redirect no longer aborts on idx_facts_fence_key. When the canonical page already holds fenced facts, migrateFactsToCanonical (both engines) renumbers the migrated rows past the canonical page's current max row_num (NULL stays NULL); extract_facts re-harmonises exact numbering against the disk fence on its next pass. (#4558)
  • A stall-aborted embed drain fails the phase. The dream/autopilot embed phase now reports a stall-watchdog-aborted drain (reason: stall_timeout) as a failed phase instead of a healthy one, matching every other non-CLI embed consumer. The root cause of the wedge itself stays open. (#4599)

AI providers

  • Embedding cost estimates now price Google's gemini-embedding-001 and gemini-embedding-2, so brains on Gemini embeddings (directly or through OpenRouter) get a dollar figure instead of "estimate unavailable", are no longer silently priced at another provider's rate in the sync cost gate, and no longer trip the no-pricing gate under --max-cost (#4953, thanks @morven-ai; the hosted-google coverage-gate test is folded from [#4989], thanks @dov-kela)
  • Facts extraction on a local Ollama model now requests schema-constrained JSON (Ollama enforces json_schema server-side), so small models such as gemma3:4b stop emitting malformed extractor output; every other provider lane is unchanged. An openai-compatible backend that rejects response_format: json_schema at call time (an older Ollama build, a strict proxy) gets one retry without the schema and is remembered for the rest of the process instead of failing every extraction; the facts schema is OpenAI-strict-safe (every property required, optional ones nullable). Say to your agent: "use my local Ollama model for facts extraction" — your agent runs gbrain config set facts.extraction_model ollama:<model> (#4968, closes [#4863], thanks @ofroiland)
  • claude-cli declares prompt caching (Claude Code caches --print runs itself), so gbrain doctor no longer tells subscription users to move a claude-cli subagent tier to the metered API for a cache they already have (#4973, thanks @LongPV)
  • GLM-4.5+/5.x models routed through the ollama: (ollama:glm-5.3-flash:cloud) and openrouter: (openrouter:z-ai/glm-5) recipes now get the same thinking-by-default output headroom the zhipu: recipe already grants, so gbrain think and gateway chat/tool-loop calls no longer truncate at 4000/4096 tokens while the model is still reasoning; pre-4.5 GLM tags keep the conservative caps (#4727)

Dream / cycle

  • gbrain dream's recompute_emotional_weight phase no longer holds the cycle lock inside one silent full-brain UPDATE: rows whose weight is unchanged are skipped on both engines (no per-row trigger fan-out), and writes go out in bounded slices with a progress tick, cycle-lock refresh, and abort check between them, so a long first run is visible and interruptible (#4797)
  • gbrain dream --dry-run now skips propose_takes, grade_takes and calibration_profile (status skipped, reason no_dry_run_support) instead of billing LLM calls and writing proposal / grade / profile rows; --help names the trio as skipped (#4823, the dry-run aside only)
  • Dream subagent writes (synthesize agentic lane, patterns, delegated jobs) no longer mint page types the active schema pack does not declare: an explicit undeclared type: is stored as note with the model's type kept in frontmatter.legacy_type, so gbrain sync stops warning every night about reflection/original/pattern pages. Existing rows keep warning until gbrain jobs submit unify-types retypes them (#4852)
  • A completed run records the newest reflection it consumed (dream.patterns.last_evidence_ts); until a reflection in the lookback window is newer, re-runs skip with no_new_evidence instead of paying for another model pass, so autopilot's hourly global tick can't mint near-duplicate pattern pages. gbrain dream --phase patterns --once forces a run (and still records what it consumed). Say to your agent: "run the patterns phase now even though nothing changed" — your agent runs gbrain dream --phase patterns --once (#4879)

Atoms / extraction / facts

  • Conversation parser: a matched speaker anchor whose date cannot be reconstructed (e.g. a localized month name in a Telegram export) now opens its own message instead of being dropped and folding its words into the previous speaker; the parse reports date_fallback_count, and gbrain conversation-parser scan prints it as date_fallback_count in JSON and date_fallbacks in human output (narrow cut from [#4681], contributed by @jeanpierre121)
  • Per-slug extractors (sync inline, GitHub/Google source inline, cycle incremental) now resolve each slug to its real on-disk file via a shared buildSlugPathIndex instead of rebuilding slug + '.md', so pages whose filenames are not already slugs (spaces, capitals, punctuation) get their links and timeline extracted; a slug the index cannot resolve falls back to the legacy slug + '.md' path before being treated as deleted, so _-prefixed files and dot-dirs waived via sync.include_hidden are still extracted inline. The sync call sites stamp links_extracted_at only for the slugs actually read (slugsSafeToStamp), so a skipped page stays visible to gbrain extract --stale instead of being marked fresh forever. The published gbrain/extract subpath's extractLinksForSlugs/extractTimelineForSlugs now return { created, processed } instead of a number (no in-repo consumer). (#4967, @IvanPham03)
  • Phantom redirect no longer aborts on idx_facts_fence_key when the canonical page already holds fenced facts: migrateFactsToCanonical (both engines) now renumbers migrated rows past the canonical page's current max row_num (NULL stays NULL); extract_facts re-harmonises exact numbering against the disk fence on the next pass (#4558)
  • The extract_facts cycle reconcile now honors visibility and notability edits on existing ## Facts fence rows. Editing either cell on a row whose claim, source, and row number were unchanged used to be a silent no-op (the cycle reported 0 reconciled and the DB kept the old value indefinitely); the page now re-heals once through the existing atomic wipe+reinsert, and a page whose fence and DB already agree cannot churn (#4870)

Doctor / brain health

  • Schema drift self-heal: a brain whose migration ledger reads current but whose links_link_source_check constraint reverted to the pre-v114 closed allowlist (drifted restore, manual DDL) silently rejected every kebab provenance link write (atom-provenance, concept-provenance). gbrain apply-migrations (and every migrate pass) now probes the live constraint shape once the ledger has reached v114 (older brains get the gate from migration v114 itself) and restores the kebab-case gate: it counts violating rows first and refuses loudly, altering nothing, when existing rows fail it; on Postgres the restore uses the same non-blocking two-phase form as v114 (ADD ... NOT VALID in a transaction, VALIDATE CONSTRAINT outside it, validate-only when the definition is right but unvalidated), while PGLite keeps the one-shot form. gbrain doctor gained a links_link_source_check row (fail when the definition rejects kebab writes; warn when the gate is absent or NOT VALID). Same self-heal class as the timeline-dedup and pages-upsert-arbiter repairs (#4613)
  • gbrain reindex-search-vector now survives interruption: it records an in-progress marker before flipping the FTS trigger language, checkpoints every batch so a re-run with the same GBRAIN_FTS_LANGUAGE resumes where it stopped, and gbrain doctor fails with fts_reindex_incomplete (naming the resume command) until the run completes; previously a killed run silently left keyword search split across two tokenizers. The marker is cleared when the very first DDL statement fails on a fresh run (a missing privilege cannot leave a permanent false fts_reindex_incomplete) and preserved when a resume fails, so doctor keeps warning about a genuinely split index and never about one that never started (#4795)
  • gbrain doctor image_assets no longer reports Supabase/S3 storage objects as "missing from disk": file writers (files upload, upload-raw, files sync, the file_upload op) now stamp metadata.storage with the configured backend, doctor skips explicitly non-git lanes with a note pointing at gbrain files verify, and every files upsert merges metadata on conflict so older unmarked rows heal on their next changed upload. Genuinely vanished local/git assets are still detected (#4910)
  • gbrain doctor's source_config_shape repair hint no longer promises that "any gbrain sources config write" heals a non-object source config (set-cr-mode, rename, set-path write other columns and left the check red). It now leads with the paste-ready SQL, names gbrain sources federate <id> / unfederate <id> (re-assert the source's current federation state; federate on an isolated source also flips it into default search), and lists the commands that do not repair it; the runtime warning in sources-load says the same. Text only (#5002)
  • gbrain doctor's truncated-pages hint no longer tells you to run gbrain sync --force (a flag that never existed); it gives the working re-import steps and points at docs/integrations/reliability-repair.md. Text only.
  • The remote doctor's contextual_retrieval_coverage check counts only the caller's granted sources, matching the other scope-aware checks.

Search / eval

  • Searches through the query op read search-mode settings once instead of twice, saving a config read on hosted Postgres and closing a cache-key/result mismatch window (#4972, @Masashi-Ono0611)
  • gbrain config set now registers exactly the search.* keys the search path reads (the camelCase search.tokenBudget / search.intentWeighting / search.searchLimit plus the reranker/cross-modal/relational knobs; the three unread snake_case spellings are gone), gbrain search modes prints the real copy-pasteable config key for every overridden knob, and search.adaptive_return* set on the DB plane now reaches the adaptive return policy as documented. The three retired spellings (search.token_budget, search.intent_weighting, search.limit_default) are still accepted by config set through the search. prefix but have no reader; re-set them under the camelCase names shown by gbrain search modes (#4605)
  • Remote/MCP search and query on a brain whose pages have not yet been reindexed to the safe-chunk index version now report degraded: [safe_index_pending] (the MCP empty-result block names it) instead of asserting a clean miss; gbrain doctor counts the withheld pages and names the consequence with the gbrain reindex --markdown fix. The remote withholding itself is unchanged, and the disclosure probe uses the chunker_version index rather than scanning every markdown page (#5004)
  • The gbrain init mode picker's cost note no longer claims the semantic result cache is free while result caching is disabled.

MCP / schema / auth

  • MCP: remote put_page/capture now say so in the response (write_through.warning) when a write landed only in the database because the resolved source has no repo or local path configured; local CLI output is unchanged. Addresses [#4844]'s minimum ask (#4956, @javieraldape)
  • docs/mcp/DEPLOY.md gains an "Owner login links for AI agents" section documenting the existing owner login flow (POST /admin/api/issue-magic-link with the server bootstrap credential, five-minute single-use link, private delivery, never GET the link to verify it) and gbrain auth --help points at it, so an agent asked for the admin login link no longer falls back to the static /admin/ URL or the bootstrap token. Say to your agent: "Give me the GBrain admin login link" — your agent calls POST /admin/api/issue-magic-link with the server bootstrap credential and hands you a five-minute single-use link (#5008, fixes [#5007], contributed by @garrytan-agents)
  • gbrain schema show / graph / lint / explain / stats and the schema_graph / schema_explain_type / schema_lint / schema_stats MCP ops now resolve the active pack through the brain-wide DB-config schema_pack tier, matching what gbrain schema active and get_active_schema_pack already report, so there are no more split answers about which pack is active within one process (#4653)
  • A path-shaped schema_pack value in the DB or gbrain.yml config plane now resolves as an unknown pack instead of being joined into the schema-packs directory path.
  • gbrain serve's idle maintenance sweep now reconciles links for the same source the stdio session resolves (.gbrain-source dotfile, registered local_path, sources.default), not just GBRAIN_SOURCE/default, so a dotfile-scoped MCP session gets its body wikilinks reconciled without a serve restart. The brain-ops skill, the put_page description and its remote hint now say plainly that MCP writes (stdio and HTTP) skip inline auto-link and are reconciled asynchronously by the serve sweep or gbrain sweep --once (#4679)
  • context_pack / delta: budget_tokens is now honored by the injectable text. Each item is costed as the line it renders to and the envelope plus section headers are reserved up front (roughly 32 tokens for context_pack, 48 for delta with a since cursor); budget_used reports the token estimate of text, envelope and headers included. A budget below the reserve returns an empty pack: context_pack reports the loss as dropped_count equal to the item count (it has no has_more field) and delta sets has_more: true. delta never budget-drops open threads: every thread is rendered whole and only pages and facts count toward the budget (dropped_count / has_more count only pages and facts; budget_used exceeds the budget only when the envelope plus the threads alone do). Cursor semantics are unchanged from v1 (the page keyset, no hold or skip state). Unbudgeted output is unchanged (#4761)
  • docs/mcp/DEPLOY.md states the dual-mode /mcp auth contract: the same route verifies OAuth 2.1 tokens and gbrain auth create bearers, and the 401 + resource_metadata challenge is spec-mandated discovery emitted for ANY header-less request, not proof the configured bearer failed. It adds a Troubleshooting entry for "client status shows needsAuth but tool calls succeed" (judge from whoami / gbrain auth test), and docs/guides/agent-to-gbrain.md's gbrain connect row cross-links it (#4893)

Sync / import

  • gbrain import --json also reports source_id, the resolved destination source the pages were written to (sourceId ?? 'default', the same value the ingest_log row records) (#4779, @chris-conte)
  • Emoji folder/file names written with a variation selector (the VS16 macOS Finder and most pickers append) now slugify to the clean slug, and [[wikilinks]] written with the same emoji resolve to it; pages previously keyed under the invisible-selector slug re-key on next touch or one gbrain sync --full. Say to your agent: "re-sync my vault in full" — your agent runs gbrain sync --full. Existing pages that were keyed under a selector-bearing slug are not retired automatically; rename or delete those twins once (gbrain orphans lists them), and remote put_page still accepts selector characters in slugs (#4985, @ozp)
  • On durability-hardened brains, delete_page now commits the page removal (path-limited, best-effort) so the post-commit hook pushes it and commit-driven sync sees it, matching the write side; the delete_page result's write_through carries committed: true when the removal landed in git (hardened repos only) (#4994, @howardpark)
  • New persisted sync.include_hidden config key, the dot-directory waiver's twin to sync.exclude, so sync --all, autopilot and the dream cycle honour a committed .github/-style directory that --include-hidden (refused under --all, unavailable to internal callers) could only admit per invocation. Same dialect (comma/newline list, trailing / = whole subtree), unioned with the per-call flag, read above the full-sync early returns so the very first sync honours it; default unchanged (unset admits nothing). The --all refusal and --help now name the key. Say to your agent: "index my repo's .github folder on every sync" — your agent runs gbrain config set sync.include_hidden '.github/'. [#4901]'s dot-directory default question stays open. (#5003, @Jey2311)
  • gbrain sync and gbrain import now refresh a page's recorded source_path on the unchanged-content skip path, so legacy rows whose bookkeeping still named a pre-rename file self-heal on the next sync --full instead of being reconcile-deleted, and write-through stops recreating the old directory on every write; the stored path (now projected by getPage on both engines) is compared first, so re-syncing an unchanged tree issues no bookkeeping writes (#4588)
  • gbrain sync --json (single-source and --all) now emits exactly ONE JSON document on stdout: the cost-gate status object is nested inside the envelope as cost_gate instead of printed as a second top-level value, so sync --json | jq and JSON.parse work as documented (schema_version stays 1; --json callers that read the gate from stdout line 1 must read .cost_gate) (#4684)
  • A sync pass that soft-deletes un-syncable pages now reports them: deleted counts the swept pages and a sweep-only run reports synced instead of up_to_date / "Already up to date."; a sync whose pull failed still reports the un-syncable pages it swept in deleted on its partial / pull_failed result instead of zero (the strategy root cause shipped in v0.48.5.0; this closes Expected [#3] of [#4786])
  • Sync no longer queues an automatic embed-backfill after a run that only swept un-syncable pages or deleted files (nothing new to embed); the jobs sync handler reports embed_skip_reason: 'no_new_content' for such runs, so a real import inside the old cooldown window is no longer skipped.

Transcripts / code intel

  • Transcripts: Claude Code workflow-run artifacts (nested subagents/workflows/<wf>/ agent logs, their journal.jsonl, and title/metadata-only session stubs) no longer read as host-format drift or unknown format errors, so gbrain transcripts ingest --since last advances its watermark again instead of re-scanning the same files forever (#4969, thanks @LongPV)
  • code_callees / gbrain code-callees now honor the documented "bare or qualified name" contract: a bare method name whose chunk identity is namespace/class-qualified (C#, Java, Ruby, Rust, TS class methods) resolves via an exact fallback on the chunk's bare symbol name instead of returning 0. Say to your agent: "what does SubmitAsync call?" — your agent runs gbrain code-callees SubmitAsync (#4670)
  • Codex transcript imports: each forked/subagent rollout now lands on its own conversation page. Identity comes from the rollout's own per-thread id (with the root session id as a legacy fallback) and the first session header wins, so child rollouts no longer collapse onto the parent's page, re-import on every pass, or get dropped as duplicates. Existing root-session pages keep their slug; a parent page that previously absorbed child content re-imports once. Say to your agent: "import my conversations from Codex" (#4981)

Minions / autopilot

  • A source with config.syncEnabled: false is no longer auto-synced by the daemon. Autopilot's per-source freshness dispatcher skips it before queuing a sync job, and the full-cycle fan-out drops the sync phase (and the pull that feeds it) from that source's autopilot-cycle job while keeping lint/backlinks/extract and the freshness stamp; sync --all shares the same predicate (and now also honours the flag when PGLite hands the config back as a JSON string), and gbrain sources status / the status snapshot report sync_enabled: false for such a source on PGLite brains, matching what sync --all skips. An explicit gbrain sync --source <id> naming the source is unchanged. refs [#4399] (its cycle_freshness exemption ask stays open). (#4952, @Masashi-Ono0611)

Bootstrap / hooks

  • Ambient writeback's Stop hook now banks the real prompt when a Claude Code turn ends in a tool call: tool-result entries (which Claude stores under a user role) no longer shadow the human prompt or suppress the wide-tail retry, and <command-name> / <local-command-stdout> transcript records are no longer treated as the user's prompt. Say to your agent: "Turn on ambient writeback so my preferences get remembered" — your agent runs gbrain config set memory.auto_writeback salient (#4980, @markkasdorf)
  • gbrain bootstrap harness --status now derives the claude-code MCP target's state from the live claude mcp get ownership check: a registration replaced by another install (different URL, or a non-HTTP serve at user scope) reads failed — <reason> in the human line and --json targets[] and exits 1 instead of replaying the receipt's apply-time confirmed; not-found/unparseable output keeps the honest degrade (#4586)
  • gbrain frontmatter install-hook now installs for sources registered as a subdirectory of a git repo (the <workspace>/brain layout gbrain bootstrap creates): the hook lands at the enclosing repo root, scoped by git pathspec to that subdirectory so host-repo markdown is never validated; several nested sources share one hook (union of scopes, uninstall drops one at a time); root-registered sources get a byte-identical script (#4600)
  • gbrain frontmatter install-hook never silently disables hooks you already have. It leaves core.hooksPath unset, still writes the hook, and reports installed_unwired with the reason and the manual wiring step when the host repo's .git/hooks already holds active hooks, when .githooks/ already holds other executable hook scripts (wiring would have started running them), or when core.hooksPath in any scope already points somewhere other than .githooks (husky, secret-scanner templates, dotfiles); a value that already resolves to .githooks is left alone and counts as wired. Re-runs and fresh clones that already carry the committed hook now reach the wiring step: unchanged means current AND wired, and an inert hook reports installed (wired now) or installed_unwired (blocked, with the reason).
  • The frontmatter pre-commit hook validates staged files whose names contain spaces or non-ASCII characters (NUL-separated listing and a POSIX read loop, proven under dash and bash-as-sh). install-hook refuses a symlinked .githooks/ or pre-commit instead of writing (or, on --uninstall, removing) through the link, and refuses a source path that contains a line terminator instead of embedding it in the generated script; in both cases the CLI prints the real skip reason for that source and continues with the remaining sources, on install and on --uninstall alike.
  • Bootstrap.md's install table now says GBRAIN_STOP_PUSH and hooks.stop_push_debounce_min govern the Stop-hook push only, documents the SessionEnd push as switch-free (GBRAIN_HOOKS=0 is the only off-switch) with its reason: push_spawned heartbeat attribution, and adds the SessionStart crash-recovery push row (#4986)

CLI

  • gbrain reindex --code, a flag reindex never parsed, now fails loud as an unknown flag instead of being accepted silently; run gbrain reindex-code. The CLI flag registry no longer legalises flags that only appeared in code comments, and the three docs that still said reindex --code were corrected.
  • gbrain maintain --safe --json emits a single parseable JSON document; the embedded stale-extraction summary no longer leaks onto stdout.
  • gbrain files upload-raw no longer clears a file's stored metadata type when the same file is re-uploaded without --type.

CI / test infra

  • Nightly hermes door: installer digest re-pinned after an upstream installer change so the door re-verifies digest + payload + version pins again; the door still loud-fails on schedule until the owner sets ANTHROPIC_API_KEY (#4991, thanks @janusch; fixes [#4990])

serve --http

  • gbrain serve --http no longer retains every accepted TCP connection for the life of the process (Bun's node:http never emits socket close on server-side sockets), so a deployment whose health checker opens a fresh connection per probe no longer grows its memory with every check. Accepted sockets are now tracked weakly; shutdown still severs live keep-alive connections (#4955, @pavelpp-topia)
  • gbrain serve --http shutdown is bounded: if server.close() has not returned after 5s (a connection the socket tracker could no longer reach), the daemon logs one line and finishes shutting down instead of hanging, and a close() error that arrives after that deadline is logged instead of dropped.
  • docs/mcp/DEPLOY.md documents the tailnet/LAN-only serve --http shapes (Tailscale Serve over the loopback bind with an https:// --public-url; plain-HTTP bearer-only with no --public-url), adds a Troubleshooting entry for the MCP SDK's Issuer URL must be HTTPS startup exit and its MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL opt-in, notes that --source-guard is stdio-only (HTTP writes are token-scoped) and cross-links the GBRAIN_HTTP_CORS_ORIGIN allowlist; docs/mcp/ALTERNATIVES.md splits Tailscale Serve (tailnet-only) from Funnel (public) (#4500)
  • Mention linking with link_resolution.cross_source on now treats same-slug pages in different sources as distinct targets (a foreign namesake of the scanning page is no longer dropped as a self-link, and two sources sharing a slug both link), and the extract --by-mention resume checkpoint notices a new entity even when it shares a first word with an existing one (its hash now covers every gazetteer entry). Existing by-mention checkpoints are invalidated once on upgrade, so the first gbrain extract links --by-mention --source db after upgrading rescans every page; checkpoints are resume state, not data (#4858, @proxynico)
  • Person-page role prior (bio "is an advisor" / "partner at" language) no longer types bare company links inside Timeline, See-also, Related, Facts, Sources, Links, Email mention links, Backlinks and Significant moments sections: new extractions type those links as mentions, per-edge verbs still type normally everywhere, and a ## Timeline / ## See also heading quoted inside a fenced code block no longer suppresses the prior for the prose that follows it. Precision over recall, the same posture as [#3466]. The link-extractor watermark moves to 2026-09-09, so the next gbrain extract --stale re-extracts every brain; that re-run only adds mentions rows beside older works_at / advises / invested_in edges from list sections and does not remove or retype them, so who works at X / who advises X answers that rested on those edges keep coming back until a separate, opt-in repair (#4977, @ethanbeard)
  • The autopilot.incremental_extract_include_frontmatter knob is now honoured on every extraction path (sync's inline extract, the GitHub/Google source inline extracts, the extract_stale minion, gbrain maintain, and a flagless gbrain extract --stale), not just the autopilot cycle. Those paths used to skip related:/sources: frontmatter edges AND stamp the page fresh, so an enabled knob was silently defeated on cron/webhook/minion/maintain runs and the cycle's stale drain never revisited the pages. 1/yes/on now count as true for the key, and a hand-edited string or number in config.json no longer resolves to false and shadows gbrain config set (only a real boolean is a file-plane answer). No new config key; default unchanged. Say to your agent: "keep my frontmatter related: links up to date on every sync" — your agent runs gbrain config set autopilot.incremental_extract_include_frontmatter true. Fixes [#4997]. (#4999, @sheelcheyne)
  • Markdown links carrying a #anchor ([Notes](registry/tally.md#section), [see](b.md#section)) now produce a graph edge to the target page on both the DB and --source fs extract paths (the anchor is dropped, as wikilinks already did), and pass-1 markdown targets are lowercased to match how slugs are stored, so a brain that links with anchors no longer reports an empty graph (#4995)

Pages

  • delete_page and restore_page now say what they do to disk: the MCP tool description, gbrain delete --help, gbrain pages --help, and the TOOL_CATALOG row all state that deleting a page also removes its markdown file from the source working tree (the source local_path, or sync.repo_path when the source has none; skipped when sync.write_through is off) and that restoring re-creates it, with the write_through result field reporting the outcome. Text only; the behavior itself is unchanged (#4829)

Docs

  • The context-audit skill's deterministic token pre-pass now divides wc -c bytes by the calibrated 2.8 (ceil integer math (n*10+27)/28), so the always-loaded-stack estimate lands within about ±8% of the measured count rather than 13-24% under. This replaces the chars/4 estimate (which undercounted Claude-loaded markdown by 25-35%); the report header states the divisor (Estimate basis: bytes/2.8) and a host-reported exact context breakdown (e.g. Claude Code /context) outranks the estimate. Say to your agent: "context audit" / "reduce context size" (#4988, [#5009], contributed by @javieraldape)
  • The mode-bundle expansion row and the search.expansion key govern no shipped verb: gbrain query expands in every mode unless --no-expand, while search, the memory verbs and the eval harnesses pin expansion per call; search-modes.md, the init picker copy, the CLAUDE.md matrix, RETRIEVAL.md, eval-bench.md and the gbrain search modes per-call note now say so (#4601)
  • docs/mcp/OPENCLAW.md Option 2 now registers gbrain with openclaw mcp add gbrain --command <abs path> --arg serve --env GBRAIN_HOME=$HOME (OpenClaw 2026.8.x reads mcp.servers in ~/.openclaw/openclaw.json, not mcpServers in config.json), Verify checks openclaw mcp list, Option 1 adds the install-the-plugin-by-id (gbrain-context-engine) step before setting the gbrain-context slot, and a note says to leave GBRAIN_SOURCE unset unless single-source retrieval is intended; the README OpenClaw bullet matches (#4842)
  • System-of-record.md's Timeline row and compiled-truth.md now say timeline extraction scans the whole page, not the section after the <!-- timeline --> sentinel: a dated [Source: ..., YYYY-MM-DD] citation in compiled truth mints a timeline row (dated by the citation, summary = the bullet it sits in); the one placement that mints nothing is a citation alone in its own paragraph, and gbrain config set auto_timeline off disables the write-path extraction (#4987)

Other

  • The dream/autopilot embed phase now reports a stall-watchdog-aborted drain (reason: stall_timeout) as a failed phase instead of a healthy one, matching the contract every other non-CLI embed consumer already honours (#4599, residual; root cause of the wedge stays open)
  • Skillopt: a validation gate where every task fails (dead target provider, e.g. a logged-out claude-cli child) or every rollout's judge errors now ends the run errored with the provider message as the failure detail (exit 2) instead of finishing as a plausible no_improvement at 0.000; steps where the optimizer proposed no edits say so on stderr and log audit reason no_edits_proposed; the hermetic-config docs no longer claim macOS survives the empty-config-dir form (#4741)
  • Brainstorm/lsd: a judge-phase failure no longer deletes the run checkpoint (every paid cross result stays on disk with judge_done=false); the advice now names the existing gbrain <brainstorm|lsd> --resume <run_id>, which re-runs only the judge and re-scores into the same saved idea page the failed run wrote (the slug rides in the run checkpoint) instead of saving a second page under a new slug, rather than a --retry-judge flag that never existed (#4766)

With thanks to every contributor whose pull request this wave adopts: @chris-conte, @dov-kela, @ethanbeard, @garrytan-agents, @howardpark, @IvanPham03, @janusch, @javieraldape, @jeanpierre121, @Jey2311, @LongPV, @markkasdorf, @Masashi-Ono0611, @morven-ai, @ofroiland, @ozp, @pavelpp-topia, @proxynico, @sheelcheyne, and to the reporters whose verified issues drove the direct fixes.

Source: README.md, updated 2026-09-15