Skip to content

Releases: andreineacsu/gbrain

v0.60.64.0

Choose a tag to compare

@github-actions github-actions released this 05 Oct 13:47
2142119

An agent connected to a remote brain can now save a stack of long pages in under a minute, and gbrain tells it how.

Agents writing to a brain over MCP (for example through gbrain serve --http on another machine) used to save pages one at a time, and each page cost several seconds of back-and-forth with the database. A large page also outlasted the reply's built-in wait, so the agent got "still pending" and had to keep asking. Writing 27 long research pages took about 15 minutes, followed by around 30 more calls to wire up links the pages already contained.

Now the agent sends its pages in batches with put_pages, waits for the commit in the same reply with wait_ms, and the server links [[wikilinks]] between the pages by itself. The server also does far less database work per page and publishes a whole batch in one transaction. The instructions every MCP agent receives at connect time say all of this up front.

Measured with 25 pages of 60 KB each, written over MCP to a Postgres brain 30 ms away (round trip), with embeddings and Git on:

Before Now
25 pages, one put_page at a time, with polling nearly 5 minutes (about 11 s a page) not needed
25 pages, 4 put_pages calls sent one after another not available 49 s, no polling
One 60 KB put_page 7.2 s reply saying "pending", then polls (about 11 s total) committed in about 6 s, in the reply
SQL statements on one page's publish path 135 115
Backlinks between the pages you just wrote about 30 manual add_link calls built automatically after the commit

To take advantage of v0.60.64.0

gbrain upgrade should do this automatically. Remote agents pick up the new instructions the next time they connect.

  1. Tell your agent (or let the connect-time instructions do it): "When you save more than three pages to gbrain, use put_pages with one request_id per batch and wait_ms 25000."
  2. Turn mention links off if you don't want them: gbrain config set mcp.remote_auto_links false.
  3. Verify: gbrain call put_pages '{"request_id":"<uuid>","pages":[{"slug":"notes/test-a","content":"# A"},{"slug":"notes/test-b","content":"# B\n\nSee [[notes/test-a]]."}],"wait_ms":25000}' returns "state": "committed", and gbrain call get_links '{"slug":"notes/test-b"}' shows the mentions link to notes/test-a.

Itemized changes

Batch writes

  • New MCP tool put_pages (full surface): 1-50 complete pages, 8 MB of content at most, per call. Every page is an ordinary put_page write with the same fences, revision checks and receipts. The pages are accepted together, so queue capacity (request count and bytes) is reserved for the whole batch, or the call refuses with queue_capacity and accepts none.
  • One receipt per batch: the state of each page with its revision or its own error envelope, totals, links, and next (done, poll or fix_pages). A page refused for its own reason does not stop the others. A grant that cannot write refuses the whole call with one permission_denied.
  • Replaying the identical call with the same request_id never writes twice. put_pages with only request_id reports progress without resending content. A replay with different pages refuses with idempotency_conflict and names the changed pages.
  • Every page needs put_page permission; bound clients get each page's slug fenced like put_page.

Waiting for the commit

  • put_page and put_pages accept wait_ms (0-30000, counted from arrival; default 5000 for put_page, 25000 for put_pages). It is not part of the write's identity, so a replay with a different wait is still the same write. Out-of-range values refuse with invalid_write_wait before anything is accepted.
  • A pending receipt's retry_after_ms comes from the server's recent publishing pace instead of a fixed 1 s.
  • get_write_request states its poll cadence and final states; a pending put_page names wait_ms and put_pages.

Faster publishing

  • A put_pages batch publishes up to 8 pages per database transaction, with file-safe recovery: if one page fails, the group rolls back, every file is restored, and the pages publish one at a time so each failure stays with its own page.
  • Fewer database round trips per write: claiming, admission, recovery records and completion each take fewer statements, a page's own writes skip repeated reads, and a batch's shared admission reads run once.
  • Connector writes wait for a pending withdrawal rewrite of the same page before reading its file, so a sync right after a forget no longer fails with source_changed.

Links for remote writes

  • Remote put_page, put_pages, capture and edit_page writes queue a links effect after the commit. It turns [[wikilinks]] and markdown links in the page body into plain mentions links to pages that already exist in the same source, are visible to the writer and sit inside its slug grant. Typed, frontmatter and timeline edges stay off for untrusted writes. A batch links its pages to each other once its last page commits.
  • On by default; mcp.remote_auto_links false turns it off. Receipts report auto_links.mention_links and the effect's added/removed counts.

Agent guidance

  • The connect-time instructions put the prompt-critical lines first, so harnesses that read only the first 2,048 characters still see them: context_pack at session start, put_page replaces the whole page, and the writing guidance (put_pages, wait_ms, following next).
  • put_page, add_link and add_timeline_entry descriptions say when links and timeline entries come from the page itself.
  • The simple HTTP transport's 413 reply tells the agent to split the request and names GBRAIN_HTTP_MAX_BODY_BYTES.

For contributors

  • New e2e coverage: Postgres statement budgets per write phase, two-connection page-guard tests, grouped put_page publication (happy path and per-member failure), and the remote links effect on both engines.
  • The initialize-instructions and served-schema budgets grew for the write guidance; a new test pins the prompt-critical lines inside the first 2,048 characters.

v0.60.32.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 05:06
48ed5e8

Fix wave 7: automatic capture stops double-storing what you remember, the maintenance sweep reads whole transcripts, Gmail commitments and timelines work again on managed brains, gbrain upgrade refuses a Bun it can't run, the contradiction judge stops excusing undated conflicts, "who is waiting on me" ages and ranks requests honestly, and 62 community pull requests land.

When two notes give different numbers for the same thing and neither note has a date, nothing tells you which came first, so that is a conflict you need to resolve. The judge kept calling those pairs a change over time instead, guessing an order from the numbers themselves (the bigger headcount must be newer) or from the kind of note (a board deck must be newer than a memo). Prompt v4 tells it plainly not to do that, and that one note saying nothing about headcount does not contradict another note that gives one. On a fresh test world it now calls 149 of 150 planted same-time conflicts contradictions (was 131), all 50 undated ones included (was 37). The price is a few more false alarms: 14 of 820 unrelated pairs (was 11) and 5 of 50 compatible pairs (was 2).

gbrain waiting had four rough edges, all found by the gbrain-evals open-loops wave. A reply that only said "Thanks!" to someone's question closed the loop although the question was still open. An FYI whose only question mark sat inside a link opened a loop as if you had asked something. A first sync of an old inbox ranked a request that had waited 30 days as no older than one from yesterday, because a loop's age started when gbrain noticed it. And the ranking moved with the wall clock, so a published order could not be reproduced. All four are fixed; --as-of pins the clock.

After upgrading Before After
Undated same-fact conflicts called a contradiction (fresh N2 seed) 37/50 50/50
All same-time conflicts called a contradiction (fresh N2 seed) 131/150 149/150
False alarms on unrelated / compatible pairs (fresh N2 seed) 11/820, 2/50 14/820, 5/50
"Thanks!" in reply to a question closes the loop loop stays open
FYI whose only ? is inside a link opens a loop after 72 h opens nothing
A 30-day-old request found on first sync ranked as new ranked 30 days old
gbrain waiting order moves with the wall clock pinnable with --as-of
"Who works at Acme?" when "Acme Labs" also exists no seed, arm silent seeds from the page titled "Acme"
A meeting note's Participants: list mentions attended
segments.join("/") in a file that defines join resolved as a caller of join unresolved
Keyword-only answer to a question no page can answer graded moderate graded weak
check-backlinks fix on a managed brain one refusal per page; dry run claims success one clear refusal, dry run too
A fact remembered, then seen again by automatic capture stored twice stored once (corrections always kept)
Sweep extraction of a 120 KB session transcript first 8,000 characters whole transcript, in windows
Gmail commitments on a managed brain never extracted extracted every sweep
gbrain upgrade to a release needing a newer Bun installs, then fails to start refuses (exit 78); autopilot holds
Remote title search, rare term, 40k pages (Postgres) ~490 ms ~34 ms
Conversation-facts backlog with source-evidence pages marked conversation_parseable: false never reaches zero drains
chronicle-backfill --limit N, run twice the same N pages again the next N pages

These came from the gbrain-evals category waves A4 (abstention), N2 (contradictions), N7 (open loops), N9 (multi-hop), N12 (meeting formats) and N13 (code intelligence), plus open issues garrytan#5341, garrytan#5330 and garrytan#5329, and the GBRA-35 lane's fixes for garrytan#5888, garrytan#5887, garrytan#5889, garrytan#5890, garrytan#5892, garrytan#5855, garrytan#5902, garrytan#5875, garrytan#5867, garrytan#5856, garrytan#5877, garrytan#5854, garrytan#5868 and garrytan#5869.

Memory capture got two fixes that change what lands in your brain. A fact your agent saved with remember was often stored a second time by automatic capture a moment later; capture now skips an exact same-claim copy on the same entity, and never skips a correction. And the maintenance sweep only ever read the first 8,000 characters of each session transcript, so everything said after that never became a fact. It now reads whole transcripts in turn-aligned windows and remembers where it stopped. That costs more model calls on long sessions (about 15 for a typical 120 KB transcript, capped per sweep).

Managed brains with Google connected had quietly stopped doing several jobs: Gmail commitments were never extracted, Gmail and Calendar pages got no links or timeline entries, quiet threads never opened their loops once the waiting window passed, and closing a commitment loop claimed it retired the fact when it hadn't. All four work again. gbrain upgrade now checks the target release's Bun requirement before swapping, so a host can no longer upgrade itself into a binary it can't start. An exact-title page ranks first in title search again, and remote title search is index-backed (a rare term on a 40k-page Postgres brain: about 490 ms to about 34 ms).

The release also absorbs 62 open community pull requests, each re-checked against current master: sync stops deleting pages it does not own and keeps skillpack files out of managed checkouts, backups and storage restores cover every source and every live page, Claude Sonnet 5.5 works and Sonnet 5 is priced at its standard rate, list_pages pages losslessly, a multi-line fact no longer blocks its page from being written, and two search paths get faster. Thank you to everyone listed below.

To take advantage of v0.60.32.0

gbrain upgrade installs the binary. There is no schema migration. Restart every gbrain serve, autopilot and worker afterwards so they run the new code.

  1. Meeting pages re-derive their attendees on their own. The link extractor version moved, so the next gbrain extract --stale (or the autopilot cycle) re-reads Participants: lists.
  2. Expect one paid re-judge of contradictions. The judge prompt version moved to 4, so the next gbrain eval suspected-contradictions re-judges every pair. It prints its cost estimate first, as on any prompt change.
  3. Existing loops get request-time ages when their thread is next checked. An open thread loop keeps the earlier of its stored age and the oldest unanswered message's time, so the next sync that touches a thread corrects an age set at detection time.
  4. Code call graphs pick up the member-call fix when a file re-imports. To refresh every code page now: gbrain reindex-code.
  5. Preview the two new explicit repairs, and apply only after you agree. gbrain doctor reports captured_facts_active (facts captured before v0.60.30.0 from gbrain's own claude-cli sessions or pasted text) and loop_facts_drift (facts of loops closed before this release):
    gbrain repair captured-facts            # preview; apply with --apply --expect <hash>
    gbrain repair loop-facts                # preview
    Guide: docs/guides/repair.md.
  6. Decide on connector atom extraction. Atom extraction of connector email and meeting pages is now opt-in on every brain. To keep it (the text of each thread or event goes to your extract_atoms model): gbrain config set cycle.extract_atoms.connector_pages true.
  7. Cached search results are invalidated once (KNOBS_HASH_VERSION 29 -> 30). If you changed your FTS language, run gbrain reindex-search-vector so remote title search matches it.
  8. Verify:
    gbrain waiting --as-of 2026-10-01T12:00:00Z --json | grep as_of
    gbrain sweep --once --json        # corpus_files[] shows windows_done / windows_remaining
    gbrain doctor
  9. If any step fails, file an issue at https://github.com/garrytan/gbrain/issues with the output of gbrain doctor and ~/.gbrain/upgrade-errors.jsonl if it exists.

Behavior changes

  • Automatic capture skips an exact copy of a claim already stored in the same source on the same entity (or another entity the claim names), visible where the new fact would be, and written within 15 minutes of the turn or by capture in the same conversation. Reworded copies are never skipped, so corrections ("is not moving", a changed amount or place) are always kept. Explicit remember is never skipped (garrytan#5888).
  • The sweep extracts whole transcripts in turn-aligned windows of about 8,000 characters, with a <file>.progress sidecar, capped at 8 windows per file and 32 per sweep (GBRAIN_CORPUS_WINDOWS_PER_SWEEP). Transcripts marked done before this release are not re-read; only turns added after the upgrade are extracted (garrytan#5887).
  • gbrain upgrade and gbrain self-upgrade refuse a release whose Bun floor the host does not meet (exit 78, nothing changed); --no-bun-floor-check upgrades anyway when the floor cannot be read. Autopilot holds the upgrade and self_upgrade_health shows the hold and fix (garrytan#5855).
  • Remote title matching follows the language search_vector was built with (garrytan#5889).
  • A synthesis publish still pending after the 30 s writer wait is a phase warning (publish_pending), not a failure, and the next cycle finishes it (garrytan#5854).
  • Connector email and meeting atom extraction is opt-in on every brain (cycle.extract_atoms.connector_pages). The auto-drain's daily cap counts attempts at the per-attempt estimate, so 6 attempts a day by default (garrytan#5856).
  • loops_close returns { closed, id, status, fact_expired, retryable, reason? } and expires the fact and strikes its fence row in one coordinated write (garrytan#5869).
  • gbrain extract timeline --source db exits non-zero when a write is refused, naming the code and recovery (garrytan#5904 probe).
  • Short random passwords are redacted reliably. The high_entropy_assignment rule (retriev...
Read more

v0.60.27.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 06:04
ad7900d

GBrain now needs Bun 1.4 or newer, because a bug in older Bun releases could make GBrain wait forever on a helper process that had already finished.

GBrain starts small helper processes all the time: git for your brain repo, background workers, syncs. Older Bun releases had a bug where one of those helpers could exit and Bun would never pass the news along, so whatever was waiting for it waited forever. On a laptop that looks like a command that never returns. On an always-on brain it looks like a worker that quietly stops making progress. Bun 1.4 includes the fix, so 1.4.0 is now the minimum.

Compiled release binaries already carry Bun 1.4.2, so if you run the gbrain binary there is nothing to do. If you installed from source with Bun, run bun upgrade first.

On an older Bun, every command (including serve, jobs work, hooks and autopilot) stops at startup with one message that names your Bun, the minimum and the fix, instead of starting and hanging later:

GBrain requires Bun 1.4.0 or newer (found Bun 1.3.14).
Fix: run `bun upgrade`, then restart GBrain. If a `gbrain upgrade` stopped here, finish it with `gbrain post-upgrade`.
You run GBrain on What happens after this release
The compiled gbrain binary Nothing changes
Bun 1.4.0 or newer Nothing changes; gbrain doctor shows a new bun_runtime row
Bun 1.3.x Commands refuse with the message above until you run bun upgrade

Things to watch

  • If you run gbrain upgrade while still on Bun 1.3, the new version installs but its migrations stop at the runtime check, and the install prints the same message. Run bun upgrade, then gbrain post-upgrade.
  • Services under launchd, systemd or cron keep retrying and recover by themselves once Bun is upgraded. The refusal is in ~/.gbrain/autopilot.log.
  • gbrain --version still answers on an older Bun, so tools that check the version keep working.

To take advantage of v0.60.27.0

  1. Upgrade Bun (source installs only; compiled binaries skip this):

    bun upgrade
    bun --version   # 1.4.0 or newer
  2. Upgrade GBrain, or finish an upgrade that stopped at the runtime check:

    gbrain upgrade        # or, if you already upgraded on the old Bun: gbrain post-upgrade

    Then restart anything long-running: gbrain serve (restart your agent harness for stdio MCP), gbrain jobs supervisor, autopilot.

  3. Your agent reads skills/migrations/v0.60.27.0.md the next time you interact with it. It checks the runtime, asks before running bun upgrade for you, and finishes the upgrade.

  4. Verify the outcome:

    gbrain doctor   # bun_runtime: "Bun 1.4.x (minimum 1.4.0)"
  5. If any step fails, please file an issue:
    https://github.com/garrytan/gbrain/issues with:

    • output of gbrain doctor
    • output of bun --version and which -a bun
    • contents of ~/.gbrain/upgrade-errors.jsonl if it exists

    This feedback loop is how the gbrain maintainers find fragile upgrade paths. Thank you.

Say to your agent: "Is my Bun new enough for GBrain?" or "Finish upgrading GBrain after the Bun upgrade."

Itemized changes

Runtime floor

  • MINIMUM_BUN_VERSION in src/core/runtime-version.ts and engines.bun in package.json are now 1.4.0, the lowest 1.4 release. It carries the Linux child-exit fix (oven-sh/bun#30301, first shipped in 1.3.14), and test/bounded-child-exec.test.ts passes on it.
  • unsupportedBunMessage() builds the one refusal text: found version, minimum, bun upgrade, restart, and gbrain post-upgrade for an interrupted upgrade. assertSupportedBun() throws it with code UNSUPPORTED_RUNTIME.
  • The CLI entrypoint (src/cli.ts) checks before any command runs, so every subcommand fails fast with exit 1. gbrain --version prints the version and exits 0, with the refusal on stderr, so an upgrade started by an older gbrain can still confirm what it installed. gbrain autopilot also writes the refusal to stdout, because its services log stdout to autopilot.log and stderr to an autopilot.err nothing points at.
  • scripts/postinstall.ts prints the refusal when an install or bun update lands on an older Bun, and still exits 0.
  • New doctor check bun_runtime (ops): Bun <version> (minimum 1.4.0), or a failure with the fix command.
  • gbrain bootstrap cloud-setup-script installs Bun through npm when the sandbox has no Bun or one older than 1.4.0, and its launcher runs that Bun.
  • Install docs (README, INSTALL_FOR_AGENTS.md, BOOTSTRAP_FOR_AGENTS.md, CONTRIBUTING.md, SECURITY.md, docs/guides/authorization-upgrade.md) state Bun 1.4.0 or newer and name bun upgrade.

What Bun 1.4 does not fix

  • Bun still drops a child's pipe events when a callback re-enters the event loop (for example bun:test expect().resolves); a raw repro without execFileBounded still hangs on 1.4.2. The in-code bounds on git children stay, and their comments now say which part Bun fixed.

For contributors

  • The minimum-version CI lanes move to 1.4.0: test.yml security regressions and all four persistence-validation.yml matrices run 1.4.0 and 1.4.2 (pull requests still skip the minimum). native-locks.yml drops 1.3.11 and 1.3.13 and runs 1.4.0 and 1.4.2 on full scope (16 native pairs, 4 musl, 4 per Windows probe).
  • test/scripts/ci-pr-scope.test.ts requires every minimum-version matrix to be exactly [MINIMUM_BUN_VERSION, primary]. New test/runtime-version.test.ts pins the refusal text, the doctor row, and the floor in package.json and the cloud setup script.
  • Doctor goldens scrub the running Bun version (Bun <bun-version>).
  • If your local Bun is 1.3.x, bun upgrade before running the CLI or CLI-spawning tests.

v0.60.25.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 06:21
a4c5ea3

CI now runs on Bun 1.4.2, so contributors stop seeing random test hangs.

Bun 1.3 had a bug where a finished child process could go unnoticed. The process exited, but the "it exited" signal got lost, so whatever was waiting for it waited forever. In GBrain's test suites that showed up as hangs that ended in "killed 1 dangling process" and a red run nobody could reproduce. Bun 1.4 fixes the bug at the source. Every CI job that pinned Bun 1.3.13 now pins 1.4.2, the same version that already compiles the release binaries.

Two things behaved differently on 1.4 and are fixed. The out-of-band watchdog that kills a stuck gbrain sync or a wedged PGLite close kept killing on time, but its log lines (parent alive ..., SIGTERM, SIGKILL) stopped appearing while the process was stuck, because Bun 1.4 routes a worker thread's stderr through the main thread. They now go straight to the terminal, so cron logs show why a process died again. One test's fake database server also closed a socket twice, which Bun 1.4 reports as an error.

If you run GBrain from source on Bun 1.3.x, nothing changes. Bun 1.3.11 is still the minimum and CI still tests it on pushes to master and nightly. The worst case of the old bug, a background git call that never returns, has been bounded in code since v0.60.16.0.

CI lane Before After
Unit, serial, slow, E2E, verify, release publishing Bun 1.3.13 Bun 1.4.2
Security and persistence matrices 1.3.11 and 1.3.13 1.3.11 and 1.4.2
Native lock matrix on pull requests 1.3.13 1.4.2 (pushes still run 1.3.11, 1.3.13 and 1.4.2)
Local gates (ci:local, ci:ubicloud) Bun 1.3.13 Bun 1.4.2

To take advantage of v0.60.25.0

Nothing to do. gbrain upgrade as usual; there is no migration and no Bun upgrade is required.

Itemized changes

  • The sync hard-deadline watchdog and the stall watchdog (src/core/process-watchdog.ts) write log lines with a direct fd 2 write from their worker thread, so heartbeat, SIGTERM and SIGKILL lines stay visible while the main thread is starved on Bun 1.4.
  • New in-agent installs (scripts/setup-in-agent.sh) download the checksummed Bun 1.4.2 runtime. A repair keeps the runtime version recorded in its receipt.

For contributors

  • Pins moved from 1.3.13 to 1.4.2: every bun-version: in test.yml, e2e.yml, heavy-tests.yml, macos-validation.yml, persistence-validation.yml and release.yml; the GBRAIN_CI_BUN_TAG default in docker-compose.ci.yml; the BUN_VERSION default in scripts/ubicloud/setup-ci-vm.sh and the fallback in scripts/ci-ubicloud.ts; the oven/bun image in tests/docker/.
  • test.yml's security matrix and every persistence-validation.yml matrix run 1.3.11 and 1.4.2; pull requests still skip 1.3.11. native-locks.yml keeps all three versions on full scope and narrows pull requests to 1.4.2.
  • test/scripts/ci-pr-scope.test.ts fails when any single-version pin drifts from the primary, or when a matrix stops running the minimum supported Bun.
  • test/postgres-engine-singleton-lifecycle.test.ts's fake endpoint ends each refused socket once.
  • Bun 1.4.2 accepts the committed bun.lock unchanged. package.json engines stay at >=1.3.11.

v0.60.11.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 08:54
f8d1e39

Managed brains stop re-writing the same pages every hour, every maintenance job finishes again, and after an upgrade doctor shows you each leftover problem with the exact fix.

If you connect Gmail, Calendar, Contacts or GitHub to a managed brain, each connector used to forget where it stopped after every maintenance cycle. So every hour it walked its whole window again and spent a new permanent write ID and receipt on every unchanged page. Now it resumes where it stopped, skips pages that did not change, and keeps going when the owner is slow instead of giving up after one write.

On the same brains, a dozen maintenance steps still used a writer that managed brains refuse. Concept synthesis paid for the model call and then died, taking the rest of the nightly job with it. Every maintenance writer now goes through the coordinator, and one failing step no longer stops the steps after it.

After an upgrade, gbrain post-upgrade prints a preview banner, and gbrain doctor --remediation-plan lists every repair with its command. gbrain doctor --remediate --yes --include-repairs --max-usd 2 applies the ones you agree to, under a spending cap.

Managed brain, test fixtures on both engines Before After
Page admissions on a quiet connector's second run every window item 0
Connector runs that resume their cursor after a maintenance cycle none all
Global maintenance after synthesize_concepts is refused job dies later phases run, job reports the failure
Repairs listed by doctor --remediation-plan none every kind with work, each with its command
Commands from gbrain post-upgrade to a clean repair plan not possible 3
Grandfathering 250 pages on a pushed repository, seconds per page (PGLite / Postgres) 0.456 / 0.821 0.060 / 0.070

Things to watch: the first connector run after the upgrade may re-admit its window once (see below). A page you saved to an unbound Postgres source still refuses by default; the error now names both fixes.

To take advantage of v0.60.11.0

gbrain upgrade should do this automatically. If it didn't, or if gbrain doctor warns about a partial migration:

  1. Upgrade hosts in this order, with autopilot paused on connector hosts. Mixed versions refuse with typed errors instead of losing data, and this order keeps them away:

    gbrain autopilot pause --reason "upgrading to v0.60.11.0"   # on each host that runs connector jobs
    gbrain upgrade                                              # first: every consumer and worktree-owner host
    gbrain upgrade                                              # then: the connector hosts
    gbrain doctor --remediation-plan                            # preview; ask the user before applying
    gbrain autopilot resume                                     # on each host you paused
    gbrain sources status                                       # verify

    If schema work did not complete, run gbrain apply-migrations --yes --no-autopilot-install on the brain host.

  2. Expect one connector re-walk, once. Migration 176 moves each connector's cursor to its new key. A source whose newest cursor receipt was compacted re-walks its window once on its next run, and connector writes still queued from before the upgrade fail once with connector_intent_outdated (detail pre_upgrade, nothing to do) and are fetched again. The post-upgrade banner and gbrain sources status count those sources. This first spike is expected and is not the hourly churn this release fixes.

  3. Your agent reads skills/migrations/v0.60.11.0.md the next time you interact with it. It previews the recovery plan and asks you before applying anything.

  4. Verify the outcome:

    gbrain sources status --json          # after the second run a quiet connector shows page_admissions: 0
    gbrain doctor --remediation-plan      # no repair steps left once you applied the agreed ones
    gbrain sources writer status --json   # writer versions per host
  5. 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.

Itemized changes

Connectors (garrytan#5686, garrytan#5470, garrytan#5600, garrytan#5601)

  • Stable connector identity. The checkpoint key, the admission and publication change checks and the attachment-repair preview use the parsed connector settings minus credential-delivery fields. Cycle stamps and other bookkeeping in sources.config no longer restart a connector or fail its pending writes with source_changed.
  • Account pinning. Each connector source pins the account its credential belongs to. Google checks it through every enabled service before importing; GitHub records the App installation, or the login under scope: auto. A different account refuses with connector_account_changed and two exact ways out.
  • Unchanged items take no admission. Connector sync, managed gbrain import, gbrain sync --working-tree and company-profile sync run each item through its own publication preparation first and skip it when publishing would change nothing. Pages below the safe-chunk fence, pages whose search projection lags, deleted pages and changed pages are still published. An unchanged cursor is not saved again; freshness for gbrain waiting is stamped directly.
  • Accepted writes count as progress. A connector keeps going when the owner is slow, waits at most 30 seconds in total per run, and records writes still pending; the next run resolves them first and retries failed ones by itself. extract_atoms reports a batch the owner accepted but has not published as pending, not failed, and dream no longer halts on it.
  • gbrain sync --source <id> --reset-checkpoint re-walks one connector's window. Unchanged pages are not admitted again and the account pin is kept.
  • gbrain sources status shows each connector's upgrade recovery state and its last run's admissions, skipped pages, pending writes and checkpoint admissions.
  • Mixed-version safety. Connector writes use a new intent format. An older consumer refuses it with unsupported_mutation_protocol and the connector names the hosts to upgrade; an older connector's writes fail connector_intent_outdated and are fetched again after the upgrade.
  • Your notes on connector pages survive a re-walk. A timeline entry you add to a connector page (add_timeline_entry) is kept when the connector renders the page again from the provider, and adding one to a database-only connector source stays database-only instead of claiming the source or refusing.

Maintenance writers on managed brains (garrytan#5484, garrytan#5280, garrytan#5523, garrytan#5405)

  • synthesize_concepts publishes through the maintenance coordinator in the same order as before: private first, then the provenance edges, then promotion to world. The authority check runs before any model call.
  • A failing phase no longer kills maintenance. A phase that throws is reported as a failed phase and later phases still run; the job still reports the failure and gbrain dream exits non-zero. Cancellation, a lost cycle lease and budget exhaustion still stop the job. A phase that fails after paid model calls is counted in doctor's dream_paid_loop check.
  • More writers go through the coordinator: Life Chronicle events with their timeline row, the purge of deleted pages, gbrain enrich and enrich_thin (keeping facts and takes fences), the drift report and grade_takes auto-resolutions, the managed extract walk, add_link / remove_link as coordinated database-only writes (a manual link survives re-derivation), gbrain bootstrap verify cleanup, and the facts, phantom-redirect, open-loop and conversation-facts writers.
  • Every phase and mutating operation is classified for managed brains, and a matrix test runs every phase once on a managed PGLite and Postgres brain.
  • Unbound Postgres sources (garrytan#5254). Saving a page to a source with a checkout folder but no owner still refuses by default, but the error (owner_unavailable, detail unbound_source) names both fixes: bind with the printed gbrain sources writer claim command, or run gbrain config set persistence.unbound_write database_only. Migration 177 records those pages as database-only, so they stay that way after binding, and a publication racing a bind fails instead of committing. Doctor's unbound_source check is ok while the source is unbound and warns once it is bound; when a canonical file appears at such a page's path, gbrain sources reconcile <source> <slug> --preview shows both sides and --apply resolves it.

Embeddings and migrations (garrytan#5680, garrytan#4616, garrytan#5621, garrytan#5530, garrytan#5289)

  • Embedding migration budget (garrytan#5680). Each provider request is charged at its maximum and settled to reported usage, once per attempt, summed across retries and splits. When --max-cost-usd is below the printed worst case, the migration stops before touching any vector and prints the command that covers it (embedding_budget_below_worst_case).
  • Degenerate vectors are refused per chunk (garrytan#4616). A zero-norm, NaN or infinite vector fails only its own chunk with embedding_zero_norm; the page's other vectors are kept, on managed brains too, and the failure names gbrain embed <slug>. Empty inputs never reach the provider. gbrain reindex --vectors rebuilds vector indexes after a PGLite crash repair, and the repair notice names it.
  • Contextual retrieval mode (garrytan#5621). --no-embed imports record their mode. Contributed by @woprrr (garrytan#5630). gbrain repair contextual-mode stamps existing pages exactly as a fresh import would and re-embeds only pages whose input changes.
  • Grandfathering groups its Git work (garrytan#5530). The effect runner...
Read more

v0.59.0.0

Choose a tag to compare

@github-actions github-actions released this 27 Sep 09:06
e78f1c3

The LongMemEval reader now checks the evidence before giving its short answer.

When an answer depends on several old conversations, the benchmark reader now
briefly extracts the relevant facts and reasons over them before answering. A
matched study over 361 questions moved from 308 to 324 judged correct answers
with the same full sessions, dates and question text. A separate replication
using the published supporting sessions also found a gain from notes in both
natural language and JSON; changing the format to JSON alone did not help.
These are reader comparisons, not evidence that everyday gbrain think
improved. The full research record includes losses, ambiguous grades, and nine
notes responses that reached the original 512-token output limit.

How to use it

gbrain eval longmemeval DATASET --reader-mode notes --reader-max-tokens 1024 --judge --output notes.ndjson
gbrain eval longmemeval DATASET --reader-mode direct --judge --output direct.ndjson

The first command's reader settings are now the defaults. Direct keeps the
original prompt and 512-token cap, so existing baseline runs remain
reproducible. --reader-mode notes --reader-max-tokens 512 reproduces the
original treatment's output budget, but risks the same cutoffs. The larger
default cap is a mitigation, not a separately measured answer-quality gain. A
bounded nine-case replay of the prior cutoffs finished all nine naturally at
1024 tokens (445–603 output tokens), but did not remeasure answer accuracy.

What to watch

The reader still sees the same sanitized full conversations in the same order;
notes do not recover facts missing from retrieval. Output-limit, empty and
unknown completions now record an error and preserve any partial text only as
a diagnostic. They cannot be judged as complete answers or silently inflate
accuracy. Receipts pin the reader mode, prompt, model, output budget and finish
reason; resume rejects a different reader configuration even with the
retrieval-mixing override. Paid reader and judge calls remain opt-in.

To take advantage of v0.59.0.0

gbrain upgrade should apply the update. If it reports a partial migration or
gbrain doctor flags one, run gbrain apply-migrations --yes --no-autopilot-install
and then gbrain doctor. Read skills/migrations/v0.59.0.0.md for the
reader-default comparison boundary and verify the flags with
gbrain eval longmemeval --help. No database migration or new model key is
required; running a judged benchmark still needs its configured providers.

Itemized changes

Added

  • Add a shared LongMemEval reader config and request builder with explicit
    direct|notes selection and output budget flags, plus per-row and summary
    configuration pins and strict resume checks. The stable package subpath
    gbrain/eval/longmemeval/reader lets companion evaluation tools use the
    same request builder instead of copying prompts. Existing invocation-guard
    and canonical-pricing modules are also exported through stable package
    subpaths so a capped companion evaluator can reuse the real cost controls.
  • Preserve original transfer and oracle comparison receipts, negative excerpt
    experiments, source audits and the primary-study compendium under docs/eval/
    and docs/research/, without promoting the experimental selector.

Fixed

  • Retain all provider text blocks and any incomplete partial text separately
    from a completed hypothesis. Output-limit, empty and unknown completions are
    recorded as failed reader rows, kept in the judged denominator, and fail the
    benchmark instead of passing a partial answer to the judge.
  • In the inactive experimental excerpt runner, future incomplete reader
    responses also stop before judging rather than entering a completed pair;
    historical records and results remain untouched.

v0.57.0.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 14:08
467ff67

Know when an accepted write needs attention.

An accepted write is not always a finished write. When a page or remembered fact is waiting, its receipt can now distinguish ordinary pending work from a known blocker or an unusually old request. Your agent gets a sensible polling interval and a clear instruction to inspect the existing owner when waiting alone is no longer enough. It keeps the original request reference instead of creating a duplicate.

Locked expired work no longer holds up the entire scheduler while unrelated work could proceed. Slow preparation and supported database waits have deadlines, and repeated receipt waits no longer accumulate unfinished reads. A deadline does not turn a pending write into a successful save or authorize another publisher. Work that cannot actually be cancelled stays tracked until it settles, and shutdown keeps the existing safety protections in place. Once contention clears, the original accepted request can finish without losing its identity or applying its content twice.

How to check a pending write

Keep the original request_id and arguments. Read its receipt, then inspect the selected brain's existing owner when advised:

gbrain write-request <request-id> --json
gbrain sources writer status --brain <brain> --probe --json
What the receipt knows What to do
Recent request, no known blocker Poll after 1 second, then 5 seconds as it ages.
Ordinary contention or an earlier write Retain the original request and poll after 5 seconds.
At least two minutes old, or an operator-required blocker Inspect the existing owner; poll no faster than every 30 seconds.

Age is advisory, not proof of a dead owner. Older clients may omit the optional diagnosis. Never remove locks, transfer ownership, or discard recovery records just because a request is old.

To take advantage of v0.57.0.0

Use gbrain upgrade during your approved owner rollout. If automatic migrations did not complete, run:

gbrain apply-migrations --yes --no-autopilot-install
gbrain sources writer status --brain <brain> --probe --json
gbrain stats

Schema migration 165 adds an index for database-only pending writes. It does not rewrite accepted requests or change the writer protocol. Quiesce the existing owner before replacing or rolling it back, preserve original receipts and recovery state, and verify canonical page or fact readback before calling a deployed incident recovered. If migration or verification fails, report sanitized doctor output and the failing step at https://github.com/garrytan/gbrain/issues; do not include credentials or private content.

Itemized changes

  • Pending receipts include validated, privacy-filtered age, assessment, reason and next action. Initial responses, replay, receipt helpers and frozen memory verbs preserve their existing required fields and error codes.
  • Receipt health enrichment is authorization-first, batched for at most 100 receipts, bounded to a 500ms caller wait, and limited to one unsettled query per engine. The new pending index keeps retained terminal history out of this lookup.
  • Expired-claim sweeps skip locked rows without bypassing same-root order. Supported scheduler and renewal waits use cancellation budgets; ordinary put_page and remember preparation gets a cooperative deadline with late-result fencing.
  • PostgreSQL timeout cancellation isolates the affected query from neighboring work, including transaction siblings and connections reassigned after a disconnect. Cancellation failures do not authorize blind statement retries.
  • PostgreSQL pool shutdown rejects work still waiting for a connection instead of silently reconnecting after shutdown. Recognized shutdown cancellations no longer appear as resident storage failures.
  • Trusted writer status exposes process-local phases, deadlines and attempts, with inspection advice consistent with receipt health. Operator guidance distinguishes observation from recovery authority and local tests from live recovery.