mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 00:48:18 +00:00
master
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e2ff128e35 |
fix(config): register sources.default as a known key + validate it at set time (#3941)
* fix(config): register sources.default as a known key + validate it at set time
`gbrain config set sources.default <id>` printed
WARN: writing unknown key "sources.default" with --force.
Nothing in gbrain reads this.
That last sentence is false. `source-resolver.ts` tier 5 reads
`getConfig('sources.default')` on every unqualified call, and
`gbrain sources default <id>` writes exactly this key. The key was
simply missing from KNOWN_CONFIG_KEYS.
The message matters more than the missing entry: pinning
`sources.default` is the documented way to stop tier 5.5
(sole-non-default-source auto-routing) from silently moving where
unqualified writes land. An operator who hits that and reaches for
this knob is told the knob does nothing.
- Register the exact key, not a `sources.` prefix. A prefix would
bless arbitrary unread `sources.*` keys and weaken the unknown-key
guard this list exists to provide.
- Validate at set time, mirroring `runDefault()`: reject a malformed
id, and reject one that is not registered. Without this, registering
the key would turn `config set` into a way around the check
`sources default` already performs — and tier 5 calls
assertSourceExists, so a typo would surface later as a throw on
unrelated commands rather than at the point of the mistake.
Tests: membership, the absence of a blanket `sources.` prefix, and the
three set-time paths (malformed / unregistered / accepted).
* fix(config): keep flag literals out of the new error text + refresh flag registry
CI caught this: the #2185 freshness guard failed because the generated
flag registry no longer matched a fresh generator run.
Two separate causes, only one of them mine:
1. Mine. The "source is not registered" message named `--path` while
suggesting `gbrain sources add`. The generator scans command sources
for flag tokens, so a flag named in prose silently gets granted to
`gbrain config` — exactly the prose-bleed class #3902 closed for
safety flags. Reworded to carry no flag literals. (The first attempt
at a warning comment reproduced the bug by quoting a placeholder
flag in the comment itself; that is gone too.)
2. Pre-existing. Regenerating also adds `--federated-read`, which comes
from `src/commands/auth.ts` and appears in no line this branch
touches. The committed registry on master is stale with respect to
its own sources, so the guard fails for any branch cut from master
until it is refreshed. Committed here because the guard's contract is
"committed registry == fresh generator run"; flagged in the PR body
so it is not mistaken for part of this change.
`bun test test/cli-flag-validation.test.ts` — 24 pass / 0 fail.
* fix(config): propagate lookup failures, describe the real id grammar
Round-2 review findings:
- Dropped `.catch(() => null)` around `fetchSource`. It turned a
connection failure / permission error / SQL regression into
"source is not registered", sending the operator after a
registration problem that doesn't exist while the real fault was
swallowed. `fetchSource` already absorbs the one expected
legacy-column case; everything else should surface as itself.
Test added: `executeRaw` throws → the error propagates and nothing
is written.
- The malformed-id message quoted `[a-z0-9-]{1,32}`, which is looser
than `SOURCE_ID_RE` (`-wiki` reads as legal under the printed rule
but is rejected). Now states the actual grammar.
- Tightened the accept-path assertion from `toContainEqual` to an
exact `setCalls` equality.
Also corrects the previous commit's explanation of the flag-registry
drift: `--federated-read` is NOT pre-existing master drift. It enters
`config`'s flag set because this change adds
`await import('../core/source-id.ts')`, and that module's comment
mentions the flag — the generator follows imports, so prose-bleed
crosses module boundaries. Master is not stale; this branch pulled it
in. The regenerated entry is deterministic and harmless (`config` is
flag-validation-exempt), and stays in this PR because the guard's
contract is "committed registry == fresh generator run".
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
3257758492 | fix(doctor): align maintenance scope with canonical files (#3854) | ||
|
|
61ef727710 |
fix(contextual): route synopsis through configured models (#3678)
Co-Authored-By: cvillarroel2 <20239888+cvillarroel2@users.noreply.github.com> |
||
|
|
4320527785 |
feat: support OpenRouter API key in config (#1714)
* feat: support OpenRouter API key in config * fixup: dedupe openrouter_api_key vs master, drop no-op compile-guard test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5ecab70a21 |
fix(agent): provider-neutral help + one truthiness parser for the gateway-loop toggle (#2753) (#3437)
* v0.42.67.0 fix(agent): provider-neutral help + one truthiness parser for the gateway-loop toggle (#2753) The gbrain agent help described --model as Anthropic-only and named only ANTHROPIC_API_KEY. It also overclaimed that any recipe works and that MCP submitters get permission_denied. Reviewing that turned up a live mismatch: the doctor accepted true/1/yes/on for agent.use_gateway_loop, the subagent worker accepted only true/1. So config set ... yes reported healthy and still refused the job. Both now share isConfigTruthy() in src/core/config.ts. Item 1 of the issue (registering the key) is already on master, so this scopes to the help text, the parser, and the regression test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * drop VERSION/package.json/CHANGELOG bump — contributor PRs in this repo do not carry it Checked precedent on my own merged PRs (#3253, #3248, #3241, #3236): none touch VERSION, package.json or CHANGELOG. The version-first title + 5-file sync rule in CLAUDE.md is the maintainer ship flow, not the contributor path. Carrying the bump here would just hand the maintainer a guaranteed conflict on every merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f64505b75f |
v0.42.66.0 feat(conversation-parser): wire the opt-in LLM fallback (#2247) (#3371)
* v0.42.66.0 feat(conversation-parser): wire the opt-in LLM fallback (#2247) (takeover of #3292) Rebase of PR #3292 onto current master (version trio re-resolved to 0.42.66.0; code applied cleanly). Wires the existing conversation-parser LLM fallback into conversation fact extraction behind the exact, default-off conversation_parser.llm_fallback_enabled=true privacy gate. Deterministic parsing stays first; dry runs never call a provider. Co-authored-by: FloridaStyle <daniel.wiggins@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: drop version-trio bump — individual fixes do not carry version bumps (release PRs do) --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: FloridaStyle <daniel.wiggins@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e1cefd0654 |
fix(subagent): orchestration fix-wave G — configurable timeouts/caps, honest child outcomes, fenced timeline writes (#2937)
Four verified-open issues in the subagent-orchestration family, one PR: - #1594: dream synthesize subagent job/wait timeouts promoted from hardcoded 30/35-min constants to config keys dream.synthesize.subagent_timeout_ms / subagent_wait_timeout_ms. Approach ported from stale PR #1596 (credit @ai920wisco). - #2778: add_timeline_entry joins the subagent brain-tool allowlist, fenced fail-closed by the shared enforceSubagentSlugFence (extracted from put_page's inline check — same trusted-workspace allow-list / wiki/agents/<id>/ namespace policy). The per-turn output cap is now resolveMaxOutputTokens (data.max_tokens → agent.max_output_tokens → 8192, was hardcoded 4096); a max_tokens stop surfaces as stop_reason 'max_tokens' instead of a silent end_turn, and a mid-tool-round cap hit injects a truncation note so the model re-issues the dropped call. - #2782: patterns phase status now reflects the child outcome — non-complete outcome with zero writes → fail (PATTERNS_CHILD_<OUTCOME>), partial writes → warn. Patterns timeouts get the same config-key pair (dream.patterns.subagent_timeout_ms / subagent_wait_timeout_ms). - #2113: facts extraction cap is config facts.extraction_max_tokens (default 4000, was hardcoded 1500); stopReason 'length' is checked, retried once at 2x the cap, and surfaced on stderr instead of silently extracting zero facts. Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: ai920wisco <ai920wisco@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
06f58c2b32 |
feat(gateway): config-driven provider_chat_options passthrough (fixes #2577) (#2857)
Add provider_chat_options alongside provider_base_urls and thread it through the gateway config path into chat(). The chat request now deep-merges provider-scoped options and model-scoped overrides into providerOptions keyed by recipe id, preserving existing gateway-built options such as Anthropic cacheControl and leaving absent-config behavior unchanged. This lets operators disable thinking for small-budget hybrid-reasoning utility calls without hardcoding that behavior for every use of those models. |
||
|
+4 |
86962b242b |
fix(gateway): consolidate tool-loop resume + provider fixes (fix-wave A) (#2820)
Collector branch superseding the gateway tool-loop duplicate cluster and adjacent provider fixes. Re-implemented from the best of each PR (deduped by content, not file-overlap); every fix carries test coverage. Gateway tool-loop resume (supersedes #1934 #2062 #2065 #2112 #2274 #2487 #2336 #2257 #2499 #2491, test #2063): - toolLoop now persists the tool-result user turn per round (onToolResultTurn), so a resumed subagent job reloads a balanced transcript instead of dangling assistant tool-calls that non-Anthropic providers reject with AI_MissingToolResultsError. - runSubagentViaGateway reconciles an already-corrupted transcript on resume: it heals every dangling assistant tool-call turn (not just the tail) from the settled subagent_tool_executions rows, re-dispatching idempotent-pending tools and throwing on non-idempotent, mirroring the legacy Anthropic path. Terminal early-return for a transcript that already reached end_turn. - repairToolPairing() is a last-resort normalization at the chat() boundary (from #2336): back-fills error stubs for any assistant tool-call still unanswered (partial turns, provider-duplicated/dropped IDs on local models, length-truncated batches). No-op on balanced input. - toModelMessages is Date-safe (Postgres timestamptz -> ISO via a JSON round trip at the SDK boundary, never a ::jsonb cast; degrades bigint/circular to a string instead of throwing) and drops non-string text blocks reasoning models emit that AI SDK v6 rejects (#2488). Adjacent provider fixes: - Model-aware default max output tokens: thinking-by-default Claude 5 models get headroom (gateway 32000, think 16000) while everything else stays 4096/4000, so DeepSeek/OpenAI subagents don't exceed provider caps (#2614 #2806). - DeepSeek: promote reasoning_content into content when content is empty, via a fail-open recipe fetch shim (#2617). - OpenRouter: map openrouter_api_key (config + env) into OPENROUTER_API_KEY through buildGatewayConfig; register agent.use_gateway_loop, zeroentropy and openrouter keys in KNOWN_CONFIG_KEYS so `config set` accepts them (#2572, config key from #2112). Preserves JSONB (no JSON.stringify into ::jsonb), engine parity, source isolation, and trust-boundary invariants. Verified with the gbrain-pr-test-env consumer matrix (clawlancer/Postgres, gstack + hivemindos/PGLite) baseline-FAIL -> candidate-PASS on the same repro, plus bun run verify (31/31) and 439 targeted unit + e2e tests. Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: thomaskong119 <thomaskong119@hotmail.com> Co-authored-by: maxpetrusenkoagent <max.petrusenko.agent@gmail.com> Co-authored-by: brettdavies <brettdavies@users.noreply.github.com> Co-authored-by: ivandebot <187176982+ivandebot@users.noreply.github.com> Co-authored-by: Rafael Reis <rafael.reis@contabilizei.com.br> Co-authored-by: fbal23 <fbal.public@gmail.com> Co-authored-by: javieraldape <javieraldape@users.noreply.github.com> Co-authored-by: David Carolan <david@joyrestart.com> Co-authored-by: Masashi-Ono0611 <masashi.ono.0611@gmail.com> Co-authored-by: spiky02plateau <155588579+spiky02plateau@users.noreply.github.com> Co-authored-by: psam-717 <mphilannorbah@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5c49225e4b |
v0.42.45.0 feat(sync): delta-aware cost estimator — stop wedging the daily cron (#2139) (#2224)
* feat(core): shared computeSyncDelta + spend-posture module (#2139) sync-delta.ts: ONE implementation of "what changed since last_commit", consumed by both the sync executor and the inline cost estimator so the gate's dollar figure can't drift from what the sync imports. spend-posture.ts: spend.posture config + parseUsdLimit/formatUsdLimit off-switch parsing (off/unlimited/none → Infinity; undefined at the budget boundary so ledger rows never serialize null). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(sync): delta-aware cost estimator + non-TTY auto-defer + per-source failure acks (#2139) The inline-embed cost gate was a ~400x phantom: it priced the entire tree whenever the working tree was dirty (always, on an active brain), then blocked the daily cron with exit 2. Now: - performSyncInner + the estimator both route through computeSyncDelta, so the estimate mirrors execution (fetch-first delta; dirty-but-caught-up tree → $0). - shouldBlockSync is posture-aware; non-TTY above floor AUTO-DEFERS embeds to capped backfill jobs (exit 0) instead of wedging — single shared runInlineCostGate on both --all and single-source paths. - --full prices delta + stale backlog (full sync sweeps it inline). - off/unlimited on the cost knobs; tokenmax bypasses the backfill cap (still ledgered) but never the cooldown. - --skip-failed/--retry-failed scoped per source; the D15 parallel refusal is lifted (the #1939 ledger is per-source + lock-serialized). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(config): register spend-control keys + validate spend.posture (#2139) Adds spend.posture + the five previously --force-only spend knobs to KNOWN_CONFIG_KEYS so `config set` accepts them directly (removes the archaeology the issue complained about), and rejects invalid spend.posture values at set time with a paste-ready hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(reindex,enrich,onboard): spend.posture across the remaining cost gates (#2139) reindex-code: tokenmax makes the cost gate informational; --max-cost accepts off/unlimited. enrich + onboard --auto: tokenmax lifts the refuse-without-cap guardrail and runs UNCAPPED (spend still ledgered by BudgetTracker). Explicit --max-usd always wins over posture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: cost-gate, delta estimator, spend-posture, off-switch coverage (#2139) New sync-delta + sync-cost-estimate unit suites; rewritten cost-gate serial tests (auto-defer instead of exit 2, posture, off-switch, format split, single-source); parseUsdLimit/posture-aware shouldBlockSync; backfill cap-off + tokenmax-bypass + cooldown-still-refuses; config known-key acceptance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(spend-controls): single spend-control surface + ref-map + follow-up TODOs (#2139) New docs/operations/spend-controls.md (every gate, key, default, off switch, posture interaction); CLAUDE.md reference-map row; two P3 follow-up TODOs (measured chunk-count gating, per-source defer granularity). llms bundles regenerated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(spend): SSRF-harden estimator fetch + complete off/uncapped across reindex/enrich/onboard (#2139) Ship-stage codex pre-landing review caught four P1s in the secondary cost gates: - The delta estimator's fetch-first ran `git fetch` through the plain git() helper, bypassing the GIT_SSRF_FLAGS + GIT_TERMINAL_PROMPT=0 hardening that real sync uses. Added `fetchRemote()` to git-remote.ts (same flags as pullRepo) and route the estimator through it — a cost preview / dry-run can no longer hit a remote through a less-protected path. - `reindex --max-cost off`, `enrich --max-usd off`, `onboard --auto --max-usd off` were parsed but didn't actually proceed/uncap. Now: explicit off (and spend.posture=tokenmax) proceed past the confirmation/missing-cap refusal AND run uncapped. enrich threads an Infinity sentinel mapped to "no BudgetTracker ceiling" (never raw Infinity → no null in audit rows); reindex/onboard use their native undefined=uncapped path. Spend still ledgered. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump version and changelog (v0.42.45.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(KEY_FILES): update sync/embedding/git-remote/reindex entries to post-#2139 truth document-release pass: the cost-gate entries described the pre-#2139 behavior (full-tree-ceiling estimator, --skip-failed-rejects-under-parallel, exit-2 confirmation gate). Updated to current truth — delta-aware estimator via the shared computeSyncDelta, per-source failure acks under parallel, non-TTY auto-defer (no exit 2), posture-aware shouldBlockSync. Added entries for the two new core modules (sync-delta.ts, spend-posture.ts) + fetchRemote on git-remote.ts + reindex --max-cost off. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a55de71221 |
v0.37.10.0 feat(init): env-detection + interactive picker + preflight invariants (#1278)
* feat(core): Levenshtein helper + preflight schema-dim resolvers Foundation for v0.37.10.0 env-detection wave. Two pure modules: - src/core/levenshtein.ts: editDistance(a,b) + suggestNearest(input, candidates, maxDistance). Used by config-set "did you mean" suggestions and env-var typo detection at init. - src/core/embedding-dim-check.ts: resolveSchemaEmbeddingDim() + resolveSchemaMultimodalDim() pure functions. Validate resolved dim against recipe default_dims + per-provider Matryoshka allow-lists (OpenAI text-3, Voyage flexible-dim, ZeroEntropy zembed-1) BEFORE any DB write. Plus EmbeddingDisabledError + assertEmbeddingEnabled() runtime guard for the deferred-setup path (D9). New PGVECTOR_COLUMN_MAX_DIMS=16000 exported. Tests: 41 unit cases across both modules. * feat(providers): extract formatRecipeTable + add init provider picker Two changes prepping the env-detection wave: - providers.ts: extract formatRecipeTable() helper from runList(). Picker reuses it so UI can't drift from \`gbrain providers list\`. Also adds the codex finding #10 warn-line to \`providers test\` when the tested model differs from the configured default ("Note: tested X in isolation; gbrain's configured embedding is Y — this test does NOT verify your brain's active path."). envReady() takes an explicit env arg for testing. - init-provider-picker.ts (NEW): interactive picker mirroring init-mode-picker.ts. Filters candidate recipes to env-ready ones (codex finding #3), prompts via readLineSafe, exports printSubagentAnthropicCaveat() for shared use from initPGLite/initPostgres. Tests: 17 unit cases (10 providers + 7 picker). * feat(config): embedding_disabled sentinel + strict unknown-key rejection Two changes for the v0.37.10.0 wave: - src/core/config.ts: add embedding_disabled?:boolean to GBrainConfig (D9 deferred-setup sentinel, mutually exclusive with embedding_model). Export KNOWN_CONFIG_KEYS (60+ canonical keys, file-plane + DB-plane) and KNOWN_CONFIG_KEY_PREFIXES (search., models., dream., cycle., etc.) for validation use. - src/commands/config.ts: D6 strict-default unknown-key rejection. Unknown key + no --force → exit 1 with Levenshtein suggestion against KNOWN_CONFIG_KEYS. Prefix matches accepted without --force. --force escape hatch accepts arbitrary keys with stderr WARN. Closes the silent-no-op class the bug reporter hit (embedding.provider, embedding.model, embedding.dimensions all exit 1 with right suggestion). Tests: 19 unit cases pinning the bug-reporter regression + gate logic. * feat(init): env-detection auto-pick + preflight + atomic persist + --no-embedding Core of the v0.37.10.0 wave (D1-D7, D9-D11). Closes the bug where a fresh \`gbrain init --pglite\` silently produced a broken brain when no provider key matched the v0.36 default. resolveAIOptions rewritten with per-touchpoint env detection: - Explicit flag → shorthand → env auto-pick (group by provider id, codex #2) - Picker fires when multiple providers env-ready (D1+D2 hybrid) - Non-TTY zero-key exits 1 with paste-ready setup hint (D3) + Levenshtein typo detection for OPENAPI_API_KEY → OPENAI_API_KEY (D13) - All three touchpoints covered (embedding + expansion + chat, D4) - Local-only providers (Ollama/llama-server) excluded from auto-pick; picking Ollama silently when user has OPENAI_API_KEY set was wrong UX initPGLite + initPostgres: - Drop conditional configureGateway gate → always call before initSchema - Preflight resolveSchemaEmbeddingDim() BEFORE engine.initSchema() (D11) — invalid dim refuses with paste-ready hint, no disk write - Atomic embedding-config persistence (codex #13): either resolved tuple or embedding_disabled:true sentinel, never partial state - Post-initSchema invariant assertion stays as regression guardrail - --no-embedding opt-in flag (D9) for deferred-setup mode - Subagent-Anthropic caveat (D7) fires post-init when chat_model is non-Anthropic AND ANTHROPIC_API_KEY missing Exported groupReadyByProvider() + findEnvKeyTypos() for unit testing. Tests: 21 unit cases covering provider grouping + typo detection edge cases. * feat(embed,import): refuse cleanly when --no-embedding deferred-setup is active T7 of the v0.37.10.0 wave. Both runEmbedCore and runImport now call assertEmbeddingEnabled(loadConfig()) at entry. When the brain was init'd with --no-embedding (config has embedding_disabled:true), they exit 1 with a paste-ready hint: gbrain config set embedding_model <provider>:<model> gbrain config set embedding_dimensions <N> gbrain init --force --embedding-model <provider>:<model> \`gbrain import --no-embed\` flag still works (chunks land without vectors), so users can still ingest in deferred-setup mode and backfill embeddings later with \`gbrain embed --stale\`. * feat(doctor): empty-config drift detection + subagent-Anthropic caveat extension Two doctor check extensions for v0.37.10.0: T9 — embedding_provider check extended for the v0.36 silent-default repair case. When config is empty AND schema column dim differs from the gateway-resolved default, surface the mismatch with empty-brain vs non-empty-brain repair branching (codex finding #7 nuance): - Empty brain (0 embedded chunks) → \`gbrain init --force --pglite --embedding-model <id> --embedding-dimensions <N>\` (drop and re-init) - Non-empty brain → \`gbrain retrieval-upgrade --to <id> --reindex\` Gated on totalChunks > 0 so pristine empty brains aren't pre-warned. Never recommend rm -rf ~/.gbrain. T10 — subagent_provider check (v0.31.12) extended per D7. When chat_model is non-Anthropic AND ANTHROPIC_API_KEY is missing, warn that subagent features (gbrain dream, gbrain agent run, gbrain autopilot) will fail at job submission. Chat alone (gbrain think) still works. * feat(reindex,test): multimodal preflight + E2E suite for fresh PGLite init T11 — reindex-multimodal.ts: hook resolveSchemaMultimodalDim() preflight BEFORE the reindex sweep. Mirrors the text-side contract from initPGLite — if the configured multimodal model can't produce a dim matching the schema column, fail loud here with a \`gbrain config set\` hint rather than mid-reindex with a vector(N) INSERT error. T12 — test/e2e/init-fresh-pglite.test.ts (NEW, 14 cases): subprocess-driven E2E verification of the bug-reporter's repro scenarios: - Happy path: OPENAI_API_KEY set → auto-pick OpenAI, persists config - D3 non-TTY fail-loud (with and without env-key typos) - D6 regression: bug-reporter's three no-op config keys all exit 1 with Levenshtein suggestions - D9 deferred-setup mode + gbrain import refusal (and --no-embed bypass) - D11 preflight refuses BEFORE any disk write - Explicit --embedding-model wins over env detection Each test uses its own throw-away GBRAIN_HOME for hermetic runs. * docs: env-detection + headless-install + close v0.32 picker TODO T13/T14 docs sync for v0.37.10.0: - docs/integrations/embedding-providers.md: TL;DR table refreshed to reflect ZE as v0.36 default; added "Init resolves your provider from env keys" section explaining the auto-pick → picker → fail-loud chain; added "If first import fails" troubleshooting block pointing at gbrain doctor instead of \`rm -rf ~/.gbrain\`. - docs/operations/headless-install.md (NEW): Docker/CI sequencing guide. Two acceptable patterns — provider key at build time (Pattern 1) or --no-embedding opt-in + runtime config (Pattern 2). Codex finding #11. - README.md: Troubleshooting section with one-paragraph repair hint and links to embedding-providers.md + headless-install.md. - TODOS.md: closed v0.32.x "interactive provider chooser" entry as SUPERSEDED by this wave. Added four follow-up entries (dedicated v0.36 broken-install migration, namespaced ext fields, runtime config-key audit, value-level Levenshtein on config set). * chore: bump version and changelog (v0.37.10.0) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(doctor): empty brain scores 100/100 + hermetic doctor-report-remote test Two fixes coupled because the test couldn't pass without the formula fix: src/core/pglite-engine.ts + src/core/postgres-engine.ts — empty brain (pageCount === 0) now gets FULL marks (100/100), not 0/100. Semantically an empty brain has no coverage problem to penalize — there's nothing to embed, nothing to link, nothing to orphan. Vacuous truth applies. The pre-fix "empty = 0" caused fresh-init brains to score as critically unhealthy on \`gbrain doctor\`, which was a structural surprise to users who'd just run init successfully. Same fix on both engines. test/brain-score-breakdown.test.ts — updated the "empty brain" assertion to match the new contract (was: 0/0/0/0/0/0; is: 100/35/25/15/15/10). test/doctor-report-remote.test.ts → renamed to .serial.test.ts and made hermetic. The pre-fix test pulled audit data from the host ~/.gbrain (reranker_health, sync_failures, etc.), which made the assertion non-deterministic depending on whoever ran the suite. Now isolates GBRAIN_HOME to a tempdir via beforeAll/afterAll; env mutation requires serial-quarantine per scripts/check-test-isolation.sh R1. Closes the master-state flake that was failing on every \`bun run test\` run regardless of my branch contents. * docs: update CLAUDE.md and TODOS.md for v0.37.10.0 empty-brain fix - CLAUDE.md: annotate src/core/pglite-engine.ts + src/core/postgres-engine.ts entries with v0.37.10.0 empty-brain 100/100 contract. Vacuous truth: an empty brain has no coverage to penalize, so getBrainScore returns full marks (35/25/15/15/10 breakdown) when pageCount === 0. Pre-fix 0/100 was structurally surprising on fresh init and caused the v0.37.8.0 doctor-report-remote.test.ts flake. - TODOS.md: mark P0 doctor-report-remote.test.ts:65 TODO completed (resolved by commit 9aa571f3's empty-brain-100/100 fix; test renamed to .serial.test.ts and made hermetic per scripts/check-test-isolation.sh R1). - llms-full.txt: regenerated from updated CLAUDE.md per CLAUDE.md "Auto-derived files" rule. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(init): D5 persisted-config-wins on re-init + CI mechanical E2E Two coupled fixes for the v0.37.10.0 wave's interaction with CI's Tier-1 mechanical E2E suite (which runs without any embedding-provider env var). src/commands/init.ts — Honor D5 properly at resolveAIOptions entry. Pre-fix the env-detection branch fired on EVERY init regardless of persisted config. A non-TTY re-init with no env keys exited 1 (D3 fail-loud) even when ~/.gbrain/config.json already had embedding_model set from a prior successful init. Now resolveAIOptions reads loadConfig() first and seeds out.embedding_model / embedding_dimensions / expansion_model / chat_model from the file plane BEFORE running env detection. Also honors embedding_disabled (D9 sentinel) on re-init so deferred-setup brains don't re-trigger fail-loud. test/e2e/mechanical.test.ts:722 — Setup Journey's first init runs against a fresh DB with no persisted config. Pass --embedding-model explicitly (openai:text-embedding-3-large) so the preflight resolves offline. After this init writes config, subsequent inits in the file (RLS self-heal v24, RLS event-trigger probes, etc.) honor the persisted config via the D5 fix above. Verified locally: full test/e2e/mechanical.test.ts → 78 pass / 0 fail. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |