v0.34.1.0 fix(mcp): MCP fix wave — source-isolation P0 + PKCE DCR + federated_read + 3 more#996
Merged
Conversation
OpenClaw's bundle-mcp gateway and similar wrappers pipe the JSON-RPC handshake on stdin then close their stdin half. Pre-fix, both stdin 'end' and 'close' listeners (server.ts:65-66 and serve.ts:204-206) treated this as a permanent disconnect and shut the server down before the first tool call arrived. Guard both sites with `process.env.MCP_STDIO !== '1'`. Signal handlers (SIGTERM/SIGINT/SIGHUP), transport.onclose, and the parent-process watchdog still cover legitimate shutdown paths. The serve.ts site threads the env read through an injectable `mcpStdio?: boolean` on ServeOptions so tests stay isolated (no process.env mutation per scripts/check-test-isolation.sh R1). Tests: 3 new cases in test/serve-stdio-lifecycle.test.ts pin the guard's invariants — mcpStdio=true must NOT trigger shutdown on stdin EOF, signals must still drive shutdown with mcpStdio=true, and mcpStdio=false (default) preserves existing CLI behavior. 25/25 pass. Origin: PR #870. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ents
RFC 7591 §3.2.1: when a DCR client declares
token_endpoint_auth_method="none" (PKCE-only public clients like Claude
Code, Cursor), the authorization server MUST NOT issue a client_secret.
Pre-fix, registerClient unconditionally minted a secret, and the MCP
SDK's clientAuth middleware then rejected valid public-client flows on
/token because it expected client.client_secret to match.
Three changes to src/core/oauth-provider.ts:registerClient:
- Gate clientSecret generation on isPublicClient = (auth_method === 'none').
Public clients store client_secret_hash = NULL.
- Omit client_secret from the response payload for public clients.
Confidential clients (default client_secret_post and explicit
client_secret_basic) keep their existing one-time-reveal shape.
- Normalize NULL secret_hash to JS undefined in getClient so SDK
middleware (which checks client.client_secret === undefined, not
=== null) correctly identifies public clients and skips the
secret-comparison branch on /token.
Schema is already permissive (client_secret_hash TEXT, no NOT NULL on
both src/schema.sql and src/core/pglite-schema.ts) — no migration
needed.
Tests: 5 new cases in test/oauth.test.ts pin:
- public client → no client_secret in response (#11 from plan)
- default auth_method → secret unchanged (regression guard)
- explicit client_secret_post → secret unchanged
- getClient NULL→undefined normalization
- PKCE full /authorize → /token end-to-end with no secret (#15 from plan)
69/69 oauth.test.ts cases pass. typecheck clean.
Origin: PR #909.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `gbrain serve --http --bind <interface>` to control which network interface the HTTP MCP server listens on. Default flipped from `0.0.0.0` (pre-v0.34) to `127.0.0.1` (v0.34.0+). Why the flip: gbrain's primary use case is a personal-knowledge brain on a laptop. The previous default exposed brains on every interface — one accidental `--http` invocation away from publishing the brain to a LAN. Server operators who need remote access pass `--bind 0.0.0.0` (or a specific interface). Codex's outside-voice on the original PR #864 correctly flagged that the additive flag wasn't actually the fix; the default needed to change for the safety claim to hold. If `--public-url` is set but `--bind` is unset, runServeHttp prints a loud stderr WARN at startup recommending `--bind 0.0.0.0`. Declaring a public URL while quietly binding loopback is almost always a misconfiguration; we want the operator to see it on first start, not silently fail remote requests. Startup banner now includes a `Bind:` row so the listening interface is visible alongside Port / Engine / Issuer. Origin: PR #864, extended with D11 (default flip) per /plan-eng-review codex outside-voice review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-fix, an authenticated OAuth MCP client scoped to source-A could
enumerate source-B pages via six read-side ops: search, query (text
AND image paths), list_pages, traverse_graph, and find_experts. The
v0.31.8 source-scoping pattern shipped through dispatch.ts but the op
handlers never threaded ctx.sourceId into their engine calls, and
hybridSearch.ts:223's explicit SearchOpts rebuild dropped sourceId
even when callers passed it.
Sealing the leak:
- src/core/operations.ts adds sourceScopeOpts(ctx), the canonical
precedence ladder: ctx.auth.allowedSources (federated) wins over
ctx.sourceId (scalar) wins over nothing. Threaded into all 5
read-side op handlers + the query-image-path searchVector call
(the 6th leak surface codex caught in plan review).
- src/core/search/hybrid.ts:223 now threads sourceId + sourceIds
fields through the inner SearchOpts rebuild. The explicit pick
shape is preserved (HNSW inner-CTE ordering depends on it) but
extended.
- src/core/types.ts adds sourceIds?: string[] to SearchOpts +
PageFilters (D9: federated read needs array-shaped engine filter
or fan-out; array wins for hot retrieval).
- src/core/operations.ts AuthInfo gains sourceId + allowedSources
(D2: identity surface symmetric with the federated_read column
#876 will add).
- Both engines now apply WHERE source_id = $N (scalar) or = ANY($N::text[])
(array) at the SQL layer for searchKeyword, searchKeywordChunks,
searchVector, listPages, traverseGraph, traversePaths. Array form
wins when both are set. The searchVector filter pushes into the
inner HNSW CTE (codex flagged this placement during plan review).
- traverseGraph + traversePaths signatures gain opts.sourceId +
opts.sourceIds; engine.ts interface updated.
- findExperts (the whoknows op, D3 5th leak surface) accepts
sourceId + sourceIds and threads them into its internal
hybridSearch call. PR #861 was authored before v0.33 shipped so
this op wasn't covered in the original PR.
Auth wiring:
- GBrainOAuthProvider.verifyAccessToken populates AuthInfo.sourceId
from oauth_clients.source_id. JOIN guarded by isUndefinedColumnError
so pre-v55 brains degrade to legacy projection rather than refusing
every token verification.
- GBrainOAuthProvider.registerClientManual gains a sourceId
parameter (defaults to 'default'). DCR registerClient also sets
source_id='default' on the inserted row.
- serve-http.ts:929 cleanup: AuthInfo.sourceId is now a real typed
field. The cast + GBRAIN_SOURCE env fallback chain is gone (D13).
Legacy bearer tokens default to 'default' source in
verifyAccessToken.
- http-transport.ts (legacy access_tokens path) threads
sourceId='default' through DispatchOpts so v0.22.7 callers stay
source-scoped.
- auth.ts CLI adds --source flag to gbrain auth register-client.
Migration v55 (D10 + D13):
- ALTER TABLE oauth_clients ADD COLUMN source_id TEXT (nullable).
- Backfill UPDATE source_id = 'default' WHERE source_id IS NULL —
preserves v0.33 effective behavior verbatim for legacy clients.
- ADD CONSTRAINT FK ... REFERENCES sources(id) ON DELETE SET NULL,
wrapped in DO block so re-runs against fresh-install brains (where
the FK already lives inline in SCHEMA_SQL) no-op cleanly.
- CREATE INDEX idx_oauth_clients_source_id WHERE source_id IS NOT NULL
for the verifyAccessToken JOIN.
- GBRAIN_ACCEPT_SILENT_WIDEN env-flag wired through the runner via
SET LOCAL gbrain.accept_silent_widen — reserved for future migrations
that hit the silent-widen footgun codex flagged. This migration
doesn't need it (column is brand new; no pre-existing stale values
possible by definition).
- src/core/pglite-schema.ts + src/schema.sql include the column +
FK + index inline for fresh installs.
Tests: new test/e2e/source-isolation-pglite.test.ts with 13 regression
cases — one per leak surface (search/list_pages/traverse/etc.) plus
explicit AuthInfo.sourceId and AuthInfo.allowedSources op-handler
threading checks. Full unit suite: 6034 pass / 0 fail. PGLite
initSchema time dropped from 2.4s to 850ms after consolidating v55's
DO blocks (multiple DO blocks were slow on PGLite; one DO block for
the FK install only is fine).
Origin: PR #861 + plan-eng-review decisions D2/D3/D4/D9/D10/D13 + F2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-fix, embedMultimodal hardcoded a recipe.id === 'voyage' branch and
threw AIConfigError for every other recipe. Multimodal-capable providers
fronted by LiteLLM (or any openai-compatible proxy) were unreachable
even when the operator had wired up the model.
The fix:
- src/core/ai/gateway.ts adds embedMultimodalOpenAICompat() that
POSTs to the standard /embeddings endpoint with content arrays
carrying image_url entries. Routing comes from the existing
recipe.implementation switch — Voyage stays on its own
/multimodalembeddings path; every other openai-compatible recipe
flows through the new helper.
- src/core/ai/recipes/litellm-proxy.ts declares
supports_multimodal: true so embedMultimodal accepts the recipe.
No multimodal_models allow-list: LiteLLM is a passthrough proxy
and the user owns model-id selection; provider rejection (400 from
upstream) is the right enforcement layer there. Voyage's static
allow-list shape stays unchanged (its 12 models share
supports_multimodal but only one is multimodal-capable).
- D12 runtime dimension validation: the new helper checks the
returned vector length against the recipe's declared default_dims
(preferred) or the brain's embedding_dimensions config. Mismatch
throws AIConfigError with model id + observed + expected so the
operator can swap models or rebuild the column. Pre-fix, a
wrong-dim response would surface as a cryptic pgvector
"vector dimension mismatch" at INSERT time.
- Auth resolution routes through the existing defaultResolveAuth
helper so optional-auth recipes (LiteLLM proxy with no
LITELLM_API_KEY) and required-auth recipes both share one code
path. Optional-auth sends "Authorization: Bearer unauthenticated"
which servers like Ollama / llama-server ignore but the SDK
contract requires.
Tests: 11 new cases in test/openai-compat-multimodal.test.ts cover
happy-path, multi-input batching, unauthenticated proxy, D12 dim
mismatch + default-dim fallback, 401 / 400 / malformed-JSON / non-array
error paths, and an explicit Voyage-regression test pinning that the
new openai-compat route doesn't accidentally hijack the Voyage path.
All 41 multimodal-related tests pass (existing voyage suite + new).
typecheck clean.
Origin: PR #875 + plan-eng-review D12 (runtime dim validation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-fix, OAuth clients had a single source-scope axis (source_id, added in v55). A client could either write+read one source OR be a super-reader across all sources (via NULL source_id). There was no middle ground — WeCare-style L3 dept clients that need to write to dept-x but read dept-x + parent canon + shared canon had no expression. #876 adds federated_read TEXT[] as an orthogonal read-scope axis. source_id is the WRITE authority; federated_read is the READ authority. They default to matching values (read scope == write scope, the pre-v0.34 default) when a client is registered without an explicit federated read list. Migrations v56-v60 (six new migrations on top of v55): - v56: ALTER TABLE ... ADD COLUMN federated_read TEXT[] NOT NULL DEFAULT '{}'. - v57 (F5): explicit CASE backfill so source_id IS NULL → '{}' (not an array containing NULL — codex caught this ambiguity during plan review). - v58: post-backfill validation. Fails loud if any row's source_id isn't in its federated_read array, pointing at a logic bug in v57 if fired. - v59: flip the source_id FK from ON DELETE SET NULL to ON DELETE RESTRICT now that federated_read provides the alternative scope-loss path. Pre-flip, deleting a source could silently widen any oauth_client to super-reader; post-flip, source delete is refused if any client references it (operator must revoke/re-scope first). - v60: GIN index on federated_read for array-containment queries. Auth wiring: - GBrainOAuthProvider.verifyAccessToken JOINs c.federated_read and populates AuthInfo.allowedSources. Pre-v56 / pre-v55 brains degrade via the existing isUndefinedColumnError fallback chain. - registerClientManual gains a federatedRead?: string[] parameter (defaults to [sourceId]). - DCR registerClient sets source_id='default' + federated_read=['default'] on the inserted row. - auth.ts CLI adds --federated-read SRC1,SRC2,... flag. The register-client output now prints "Federated reads:" so operators confirm the scope they set. Engines consume the federated array through the SearchOpts.sourceIds / PageFilters.sourceIds field that #861 added (no engine changes here — the plumbing was D9). sourceScopeOpts in operations.ts already prefers the auth.allowedSources array over scalar ctx.sourceId when set. Test seam: - test/book-mirror.test.ts now spawns the CLI with GBRAIN_HOME pointed at a tempdir so the test isn't sensitive to the developer's local ~/.gbrain/config.json. Pre-fix the test could silently inherit a real Postgres connection and hang past the default 5s test timeout. Fresh GBRAIN_HOME → "No brain configured" → exit 1 in <1s. - test/e2e/source-isolation-pglite.test.ts gains one more regression case: AuthInfo.allowedSources = [] (explicit empty) MUST NOT widen scope to "all sources" — the silent-widen footgun precedence ladder. - test/openai-compat-multimodal.test.ts is part of the wave's commits via the migrate.ts changes that bump the schema chain. typecheck-only fix on a captured-auth type was already in #875's tree. 6045 unit tests pass / 0 fail. typecheck clean. PGLite initSchema runs v55-v60 in ~786ms total (within the test-harness budget for tests using the canonical beforeAll engine pattern). Origin: PR #876 + plan-eng-review F5 (CASE backfill). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VERSION + package.json + CHANGELOG bump for the six-PR MCP fix wave. Schema chain extends from v54 → v60; oauth_clients gains source_id + federated_read columns; auth'd MCP clients now stay inside their scope across all read-side ops; PKCE-only DCR works; --bind defaults to loopback; LiteLLM multimodal embedding ships. Contributed by @Hansen1018 (#870), @ding-modding (#909), @DukeDawg (#864), @toilalesondev (#861 + #876), @yoelgal (#875). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # VERSION # package.json # src/core/migrate.ts
Sync README, CLAUDE.md, SECURITY.md, docs/architecture/topologies.md, and docs/mcp/DEPLOY.md to reflect the v0.34.0.0 MCP fix wave: - README: document --bind HOST default (loopback), --source + --federated-read register-client flags, PKCE public-client gate - SECURITY.md: note loopback-by-default for serve --http, update the trust-proxy contract to point at the new default - CLAUDE.md: annotate operations.ts (sourceScopeOpts helper), oauth-provider.ts (verifyAccessToken JOIN + PKCE public clients), serve-http.ts (--bind flag), gateway.ts (openai-compat multimodal + dim validation), mcp/server.ts (MCP_STDIO guard), auth.ts (--source + --federated-read), migrate.ts (v58-v63 chain), engine.ts (sourceIds field). Add 4 new test-file entries for source-isolation-pglite, openai-compat-multimodal, serve-stdio-lifecycle, oauth.test.ts PKCE cases - docs/architecture/topologies.md: source-scoped register-client example, --bind 0.0.0.0 for thin-client host setup - docs/mcp/DEPLOY.md: --bind explanation in the ngrok section, source-scoped client recipe - llms-full.txt: regenerated per the CLAUDE.md-edit chaser rule Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renumbering the MCP fix wave from v0.34.0.0 to v0.34.1.0 so the
release slot lands between master's v0.33.2.1 and the next minor.
Touches every release-artifact mention:
- VERSION: 0.34.0.0 → 0.34.1.0
- package.json: same
- CHANGELOG.md header + "To take advantage" block
- CLAUDE.md key-files annotations (8 entries that document this wave)
- llms-full.txt (regen from CLAUDE.md)
- README.md / SECURITY.md / docs/architecture/topologies.md / docs/mcp/DEPLOY.md
- Wave code-comment markers ("// v0.34.0 (#NNN):" → "// v0.34.1 (#NNN):")
Test files renamed alongside since they were committed with the wave.
Commit subjects on the original 6 PR commits + the v0.34.0.0 bump
commit (4f533c7 → 6b47db7) intentionally NOT rewritten — those are
history. `git log` finds the implementation by message subject, not by
version tag.
6275 unit tests pass, typecheck clean, migration chain v58-v63 unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # VERSION # package.json # src/core/migrate.ts
# Conflicts: # CHANGELOG.md # VERSION # package.json # src/core/migrate.ts
garrytan
added a commit
that referenced
this pull request
May 15, 2026
Master shipped v0.34.1.0 (PR #996, the MCP source-isolation wave) which added migrations v60-v65 (oauth_clients.source_id FK column, federated_read TEXT[], backfill, validation, RESTRICT flip, GIN index). Our previously v60-numbered embed_stale_partial_index renumbered to v66. Version: 0.34.3.0 → 0.34.4.0 per user direction (skipping over claimed slots in the queue). Conflict resolutions: - package.json: take v0.34.4.0 - src/core/migrate.ts: keep both v0.34.1.0's v60-v65 cluster AND our embed_stale_partial_index, the latter renumbered to v66 - CHANGELOG.md: my v0.34.4.0 entry on top, master's v0.34.1.0 below - TODOS.md: my embed-stale follow-ups + master's MCP fix-wave follow-ups - test/migrate.test.ts + test/e2e/embed-stale-pagination.test.ts: rename v60 → v66 references (24 + 3 occurrences) Typecheck clean. Migrate + PGLite + embed.serial tests: 238 / 0. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 task
This was referenced May 25, 2026
brandonlipman
added a commit
to brandonlipman/gbrain
that referenced
this pull request
May 29, 2026
* upstream/master: v0.35.1.0: embedder shootout prereqs (pricing + gateway export + --resume-from) (garrytan#1055) v0.35.0.0 feat: ZeroEntropy zembed-1 + zerank-2 reranker (garrytan#1008) v0.34.4.0 fix(embed): cursor-paginated --stale hardening wave (D2/D3/D4/D6/D7/D8 + regression test) (garrytan#991) v0.34.3.0 fix: supervisor treats code=0 watchdog exits as crashes (garrytan#1003) v0.34.2.0 fix(import): path-based checkpoint resume — kills parallel-drop + failed-file-skip + sort-flip bugs (garrytan#988) v0.34.1.0 fix(mcp): MCP fix wave — source-isolation P0 + PKCE DCR + federated_read + 3 more (garrytan#996) v0.34.0.0 feat: Cathedral III — recursive code intelligence + Leiden clusters + eval gate (garrytan#994) v0.33.3.0 feat(v0.33.3): code intelligence MCP foundation (v0.34 W0a-c + W3) (garrytan#934) v0.33.2.1 docs: fork-PR workflow for garrytan-agents (garrytan#992) fix(sync): raise maxBuffer to 100 MiB to prevent silent ENOBUFS crash (garrytan#982) v0.33.2.0 feat(search-lite): token budget + semantic query cache + intent weighting (garrytan#897) v0.33.1.1 fix: Voyage output_dimension + flexible-dim guard + OOM-cap rethrow (garrytan#962)
brentyates-swx
added a commit
to swxtchio/gbrain
that referenced
this pull request
Jun 9, 2026
Single consolidated patch carrying the swxtch customizations whose goals are still valid against current upstream. Re-implemented against today's code (the original per-commit history is discarded); verified each goal, dropped the ones upstream now solves. Cross-model reviewed (Gemini + Codex). Still-valid goals, re-implemented: - Multi-domain: index.md is a syncable folder entry page (removed from SYNC_SKIP_FILES). (src/core/sync.ts + 2 tests) - GBRAIN_TOP_DIRS: scope a multi-repo brain root to an allowlist of top-level dirs — ported once into the now-central collectSyncableFiles walker. - N-segment slug resolver: accept domain/docs/page (3-segment) slugs, not just upstream's 2-segment shape; getPage()/allSlugs.has() decide existence. (src/core/link-extraction.ts, src/commands/extract.ts) - C/C++ chunker: extract symbols instead of falling back to text — collectSemanticNodes recursion through header guards / extern "C" / namespaces / templates (PASSTHROUGH_TYPES); type_definition/enum/union top-level types; declarator-chain name extraction; C/C++-scoped symbol-preserving merge. Surfaces functions, prototypes, typedefs, struct/union/enum, object- and function-like macros, namespace members, and templated fn/class. (src/core/chunkers/code.ts) - code-def: accept the C/C++ symbol_types the patched chunker emits. (src/commands/code-def.ts) - gbrain-safe-update: the non-destructive fork-preserving upgrade wrapper. (scripts/gbrain-safe-update) Dropped — already handled by upstream (verified): - source_id cross-source slug scoping → upstream source-isolation (all engine slug subqueries are source-scoped; federated grants, garrytan#1999/garrytan#1881/garrytan#996). - gbrain doctor OPENCLAW_WORKSPACE → upstream uses autoDetectSkillsDirReadOnly. - cli.ts executable bit → upstream ships src/cli.ts mode 100755. Cross-model review (Gemini + Codex) caught and fixed: C++ namespace/template members were collapsed into one opaque chunk (moved to PASSTHROUGH so the walker recurses to the inner symbol); C++/function-like macros weren't extracted (added preproc_def/preproc_function_def to the C/C++ top-level sets); a second stale index.md skip-test (test/sync.test.ts). Verified: tsc --noEmit clean; sync/sync-isSyncable/chunkers/code-def-refs/ edge-extractor/parent-scope suites green; C/C++ symbol extraction confirmed empirically (functions, prototypes, typedefs, macros, namespace members, templated fn/class) while TS small-sibling merging is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Six community PRs bundled into v0.34.0.0:
Source isolation (P0) —
#861+ plan-eng-review extensions D2, D3, D9, D10, D13 + F2:search,querytext path,queryimage path,list_pages,traverse_graph,find_experts)AuthInfo.sourceId+AuthInfo.allowedSourcespopulated fromoauth_clientsat token-verification timesourceScopeOpts(ctx)helper insrc/core/operations.tsencodes the federated > scalar > none precedencehybrid.ts:223explicit-pick now threadssourceId+sourceIds(the structural bug shape that caused the original leak)traverseGraph/traversePathssignatures gain source-scope optsSearchOpts/PageFiltersgainsourceIds?: string[](D9 federated-array filter)Federated read scope —
#876+ F5:oauth_clients.federated_read TEXT[]for L3-dept-style "write to one, read from N" clientsauth register-client --federated-read SRC1,SRC2,...CLI flagON DELETE RESTRICT, add GIN indexMCP transport stability —
#870:MCP_STDIO=1so OpenClaw bundle-mcp piped-stdin handshake doesn't kill the server pre-handshakeOAuth public clients —
#909:token_endpoint_auth_method: "none"(RFC 7591 §3.2.1): public clients get noclient_secretgetClientnormalizes SQL NULL → JS undefined so SDK middleware identifies public clients correctly/register+/tokenLoopback by default —
#864+ D11:gbrain serve --httpdefault--bind 127.0.0.1(was0.0.0.0)--public-urlis set but--bindis not--bind 0.0.0.0for self-hosted server operatorsMultimodal openai-compat —
#875+ D12:embedMultimodalOpenAICompatroutes openai-compatible recipes to the standard/embeddingsendpoint with content arraysAIConfigErrorbefore storage on dim mismatchTest Coverage
16 mandatory E2E regression tests per the IRON RULE (one per leak surface plus federated/migration/transport paths). New files:
test/e2e/source-isolation-pglite.test.ts— 14 cases pinning engine-layer + op-handler source-scope filteringtest/openai-compat-multimodal.test.ts— 11 cases for the new multimodal path + dim validation + Voyage regressionExtended existing files:
test/oauth.test.ts— 5 new PKCE DCR cases (no secret for public, default unchanged, NULL→undefined normalization, full/authorize→/tokenround-trip)test/serve-stdio-lifecycle.test.ts— 3 cases pinning theMCP_STDIO=1guard (don't shutdown on EOF; do shutdown on SIGTERM; preserve CLI behavior when unset)test/book-mirror.test.ts—runClihelper now uses freshGBRAIN_HOMEtempdir so the test isn't sensitive to the developer's local~/.gbrain/config.jsonResults: 6275 unit tests pass / 0 fail. typecheck clean. PGLite initSchema runs the v58-v63 migration chain in ~786ms total.
Pre-Landing Review
Wave was reviewed via
/plan-eng-review(CLEAR — PLAN) with codex outside-voice. 5 cross-model tensions resolved via D9-D13. 4 codex findings (F1 migration-numbering, F2 query-image-path leak, F5 explicit-CASE backfill, F7 PKCE full-flow E2E, F9 CHANGELOG voice) folded into the implementation.Post-merge adversarial review surfaced 5 INFORMATIONAL findings (0 CRITICAL) — all filed as v0.34.x follow-up TODOs:
takes_*ops never had source scoping (pre-existing, surfaced by this wave's framing)ctx.sourceIdpattern instead ofsourceScopeOpts(ctx)(federated_read silently drops for those)--force-retryrace vs v62 (probability low)embedMultimodalOpenAICompatdoesn't batch (single-input per HTTP call)'{}'rows (theoretical, near-zero probability)Plan Completion
Plan file:
~/.claude/plans/system-instruction-you-are-working-virtual-puddle.md. All planned items DONE — every D1-D13 decision implemented, every F1-F9 codex correction folded in, all 16 IRON-RULE regression tests written.TODOS
7 v0.34.x follow-up items added to
TODOS.md: source-scope the 14 unscoped ops, source-scopetakes_*, v58/v62 idempotency guard, multimodal batching, doctor orphan check, sources purge UX, hybrid.ts pick refactor.Documentation
Documentation updated for v0.34.0.0 in commit
b99f275b:--bind HOST(default 127.0.0.1) usage;--source+--federated-readforauth register-client; PKCE public-client sectionserve --http; trust-proxy contract refresh--bind 0.0.0.0for remote setup--bind+--public-urlinteractionTest plan
bun run test— 6275 pass, 0 fail (parallel 8-shard fast loop)bun run typecheck— cleanbun run test:e2eagainst real Postgres (recommend running before merge — touches schema)bun install -g github:garrytan/gbraininstallContributed by @Hansen1018 (#870), @ding-modding (#909), @DukeDawg (#864), @toilalesondev (#861 + #876), @yoelgal (#875).
🤖 Generated with Claude Code