fix(cli): stop thin-client jobs/config from fabricating a scratch PGLite (#2949) - #2951
Merged
time-attack merged 1 commit intoJul 20, 2026
Merged
Conversation
`jobs list|get` have had remote MCP routing since v0.32, but the CLI shell still ran connectEngine() before dispatch — on a thin-client install that fabricates an empty scratch PGLite in the thin-client GBRAIN_HOME and replays the entire migration chain on every invocation, before the remote call even runs. Host-only jobs subcommands (work, supervisor, submit, ...) and `config` did the same instead of refusing. - cli.ts: dispatch thin-client `jobs list|get` engine-free (runJobs(null, ...)); refuse the other jobs subcommands with a pinpoint hint; add `config` to THIN_CLIENT_REFUSED_COMMANDS with a hint (it reads/writes the host brain's config plane). - jobs.ts: widen runJobs to accept a null engine, guarded so null can only reach the MCP-routed list/get branches. - tests: behavioral (no scratch store created, no migration replay, refusals carry hints) + source-audit pins in the existing idioms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ss251
added a commit
to ss251/gbrain
that referenced
this pull request
Jul 21, 2026
…arrytan#3026) The thin-client branches receive MinionJob rows as parsed JSON off the MCP wire — every timestamp an ISO string — while formatJob / formatJobDetail and the stalled-detection comparison hold a Date contract (locally hydrated by MinionQueue.rowToJob). `jobs get <id>` on a thin client crashed with "job.started_at.toISOString is not a function" the moment the remote routing actually worked (unmasked by the garrytan#2951 scratch-engine fix). Rehydrate once at the unpack boundary via an exported helper that coerces valid ISO strings to Dates, leaves Dates/nulls/malformed strings untouched, and preserves the input type. Unit tests + source-audit pins for both unpack sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hungcuong9125
pushed a commit
to hungcuong9125/gbrain
that referenced
this pull request
Jul 24, 2026
…ite (garrytan#2951) `jobs list|get` have had remote MCP routing since v0.32, but the CLI shell still ran connectEngine() before dispatch — on a thin-client install that fabricates an empty scratch PGLite in the thin-client GBRAIN_HOME and replays the entire migration chain on every invocation, before the remote call even runs. Host-only jobs subcommands (work, supervisor, submit, ...) and `config` did the same instead of refusing. - cli.ts: dispatch thin-client `jobs list|get` engine-free (runJobs(null, ...)); refuse the other jobs subcommands with a pinpoint hint; add `config` to THIN_CLIENT_REFUSED_COMMANDS with a hint (it reads/writes the host brain's config plane). - jobs.ts: widen runJobs to accept a null engine, guarded so null can only reach the MCP-routed list/get branches. - tests: behavioral (no scratch store created, no migration replay, refusals carry hints) + source-audit pins in the existing idioms. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
time-attack
pushed a commit
that referenced
this pull request
Jul 28, 2026
…3026) (#3027) The thin-client branches receive MinionJob rows as parsed JSON off the MCP wire — every timestamp an ISO string — while formatJob / formatJobDetail and the stalled-detection comparison hold a Date contract (locally hydrated by MinionQueue.rowToJob). `jobs get <id>` on a thin client crashed with "job.started_at.toISOString is not a function" the moment the remote routing actually worked (unmasked by the #2951 scratch-engine fix). Rehydrate once at the unpack boundary via an exported helper that coerces valid ISO strings to Dates, leaves Dates/nulls/malformed strings untouched, and preserves the input type. Unit tests + source-audit pins for both unpack sites. Co-authored-by: Claude Fable 5 <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.
Fixes #2949.
Problem
jobs list|gethave had remote MCP routing since v0.32, but the CLI shell still ranconnectEngine()before dispatch. On a thin-client install that fabricates an empty scratch PGLite in the thin-clientGBRAIN_HOMEand replays the entire migration chain (Schema version 1 → 122, 117 migration(s) pending…) on every invocation, before the remote call even runs. The host-onlyjobssubcommands (work,supervisor,submit,stats, …) andconfigdid the same instead of refusing —jobs statshappily reports an empty queue for a busy host, andconfig setwrites rows nobody will ever read (the v0.31.1 silent-empty-results bug class).Change
Follows the existing dispatch patterns:
src/cli.ts: thin-clientjobs list|getdispatch engine-free (runJobs(null, args)) beforeconnectEngine(); every otherjobssubcommand refuses viarefuseThinClientwith a pinpoint hint (naming the routable subcommands and thesubmit_job/list_jobs/get_jobMCP tools);configjoinsTHIN_CLIENT_REFUSED_COMMANDSwith a hint pointing at the host's file-plane config.jobsis deliberately NOT in the refused set — the partial dispatch owns it (noted in a comment at the set).src/commands/jobs.ts:runJobsacceptsBrainEngine | null, guarded at entry so null can only reach the MCP-routedlist/getbranches (which never touch the engine); narrowed once so the host-only cases typecheck unchanged.cli-dispatch-thin-client.test.ts(refusals carry hints + exit 1;jobs get/jobs listcreate no scratch store and print no migration replay) and source-audit pins inthin-client-routing-audit.test.ts(the new dispatch block, theconfigentry + hint,jobs's deliberate absence from the set, the null-engine guard).Verification
bun test test/thin-client-routing-audit.test.ts test/cli-dispatch-thin-client.test.ts test/init-mcp-only.test.ts test/thin-client-upgrade-prompt.test.ts: 107 pass / 0 fail.bunx tsc --noEmit: clean.bun run teston this branch: 6924 pass; the only failures (hybrid-meta expansion, worker-registry) are environment-dependent on my machine (live~/.gbrain,OPENAI_API_KEYpresent) and fail identically on an untouched master checkout.jobs getprinted the 117-migration replay above.Related: #2948 / #2950 (same debugging session —
remote pingpolling the wrong field).🤖 Generated with Claude Code