mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
814258dda67945ffec9457a1e73980e947b7e462
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
71ed8d0d21 |
v0.32.0 feat: 5 new embedding recipes + discoverability pass (closes 17-PR cluster) (#810)
* feat(ai/types): add resolveAuth + probe + user_provided_models fields
Foundation commit for the embedding-provider fix-wave (5 API-key recipes
+ discoverability pass). Three optional additions to the recipe contract:
- `EmbeddingTouchpoint.user_provided_models?: true` (D8=A): flag for
recipes that ship without a fixed model list. Consumed by the contract
test (permits empty `models[]`), gateway.ts:223 (replaces hardcoded
`recipe.id === 'litellm'` check in a follow-up commit), and
init.ts:resolveAIOptions (refuses implicit "first model" pick for
shorthand `--model <provider>`).
- `Recipe.resolveAuth?(env): {headerName, token}` (D12=A): unified auth
seam across embed / expansion / chat. Default behavior (returns
`Authorization: Bearer <env-key>`) covers the existing 9 recipes
unchanged. Recipes deviating (Azure with `api-key:`; future OAuth
providers) override this single seam instead of adding parallel
mechanisms in 3 places. Codex review caught that auth was triplicated
at gateway.ts:281/728/931; D12=A unifies all three in one follow-up
commit.
- `Recipe.probe?(): Promise<{ready, hint?}>` (D13=A): recipe-owned
readiness check for local-server providers (ollama, llama-server).
Replaces the hardcoded `recipe.id === 'ollama'` special case in
providers.ts. Wrapped in 200ms timeout at the call sites.
Pure type additions — no behavior change. Typecheck green; existing 9
recipes work unchanged because all three fields are optional.
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (decisions
D8=A, D11=C, D12=A, D13=A).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ai/gateway): unify openai-compatible auth via Recipe.resolveAuth (D12=A)
Pre-v0.32, openai-compatible auth was duplicated 3 times in gateway.ts at
instantiateEmbedding, instantiateExpansion, instantiateChat — with subtle
drift (embedding had a `${recipe.id.toUpperCase()}_API_KEY` fallback the
other two lacked). Codex outside-voice review caught this during /plan-eng-review.
D12=A: unify all three through `Recipe.resolveAuth?(env)` (declared in the
prior commit). Two new module-level helpers:
- `defaultResolveAuth(recipe, env, touchpoint)` — applied when a recipe
doesn't declare its own resolver. Returns Authorization Bearer with
`auth_env.required[0]`, falling back to the first present
`auth_env.optional` env var, or 'unauthenticated' for no-auth recipes
like Ollama. Throws AIConfigError with the recipe's setup_hint when
required env is missing.
- `applyResolveAuth(recipe, cfg, touchpoint)` — returns
`createOpenAICompatible` options. Bearer-via-Authorization paths use
the SDK's native `apiKey` field; custom-header paths (Azure: api-key)
use `headers` and OMIT apiKey to avoid double-auth leaks.
The 3 `case 'openai-compatible':` branches in instantiateEmbedding (line
~281), instantiateExpansion (line ~728), instantiateChat (line ~931) each
collapse from ~10 lines of bespoke auth handling to a single
`applyResolveAuth(recipe, cfg, '<touchpoint>')` call.
Also: the litellm-template hardcode at gateway.ts:223 (`recipe.id ===
'litellm'`) is replaced with a union check for
`EmbeddingTouchpoint.user_provided_models === true` (D8=A wire-through
per Codex finding #3). Pre-v0.32 builds keep working via back-compat
`recipe.id === 'litellm'` clause; new recipes declaring
user_provided_models pick up the same gating automatically.
Existing 9 recipes (openai, anthropic, google, deepseek, groq, ollama,
litellm-proxy, together, voyage) gain zero per-recipe edits — the
default resolver covers their existing behavior. Behavior change for
ollama expansion/chat only: now reads OLLAMA_API_KEY when set (pre-v0.32
silently passed 'unauthenticated' for those touchpoints; embedding
already read it). Ollama servers ignore the header so no real-world
impact; this aligns the 3 touchpoints.
Tests: bun test test/ai/ — 77/77 pass.
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (D8=A,
D12=A; addresses Codex findings #3, #4).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(ai): IRON RULE regression test for v0.32 resolveAuth refactor
Pins the contract that the v0.32 D2/D12=A resolveAuth refactor preserves
auth behavior for the 9 existing recipes (openai, anthropic, google,
deepseek, groq, ollama, litellm-proxy, together, voyage).
10 cases covering:
- the 9 expected recipe ids are still registered
- every recipe with non-empty required[] returns Authorization Bearer <key>
- missing required env throws AIConfigError naming recipe + touchpoint + env-var
- Ollama (empty required, optional set) reads first present optional env
- Ollama (no env) falls back to "Bearer unauthenticated"
- all 3 touchpoints (embedding/expansion/chat) produce identical auth
shape for the same recipe + env (this is the core regression: pre-v0.32,
embedding had a fallback the other two lacked)
- applyResolveAuth converts Authorization Bearer to {apiKey} (SDK-native)
- applyResolveAuth respects a custom-header override (Azure preview; the
recipe ships in commit 8) and emits {headers} WITHOUT apiKey to avoid
double-auth
- native-* recipes (openai, anthropic, google) intentionally have no
resolveAuth declared (they use AI-SDK adapters directly)
- all openai-compatible recipes ship without resolveAuth in v0.32 (default
applies); the first override is Azure in commit 8
Also: export `defaultResolveAuth` and `applyResolveAuth` as @internal
gateway helpers so tests can pin them directly. Mirrors the pattern of
`splitByTokenBudget` and `isTokenLimitError` already exported with the
same @internal annotation.
Tests: bun test test/ai/ — 87/87 pass (10 new + 77 existing).
Typecheck: clean.
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (IRON RULE
per Section 3 test review).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ai): add llama-server recipe (#702 reworked)
10th recipe in the registry; first to ship Recipe.probe (D13=A) and the
second user_provided_models recipe (litellm-proxy is the first).
llama.cpp's llama-server exposes an OpenAI-compatible /v1/embeddings
endpoint. Distinct from Ollama: different default port (8080), different
model-management story (you launch it with --model <path>; the server
serves whatever was passed). Recipe ships with `models: []`,
`user_provided_models: true`, `default_dims: 0` so the wizard refuses
implicit defaults and forces explicit --embedding-model + --embedding-dimensions.
Added:
- src/core/ai/recipes/llama-server.ts (61 lines)
- probeLlamaServer() in src/core/ai/probes.ts; reads
LLAMA_SERVER_BASE_URL with default http://localhost:8080/v1
- Registered in src/core/ai/recipes/index.ts (10 recipes total now)
- test/ai/recipe-llama-server.test.ts (8 cases): registered + shape,
user_provided_models flag, probe declared + reachability fail-with-hint,
default-auth covering no-env / API_KEY / URL-shaped-only paths
Hardening: defaultResolveAuth in gateway.ts now skips URL-shaped optional
env entries (names ending in _URL or _BASE_URL) when picking a fallback
auth token. Pre-fix, OLLAMA_BASE_URL=http://my-ollama would have become
the Bearer token; Ollama ignores it but llama-server (and future
local-server recipes) shouldn't depend on the server tolerating garbage
auth. The regression test (recipes-existing-regression) gains one case
pinning this contract.
Per-recipe test file follows D7=B (per-recipe over DRY for readability).
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 4
of 11). Reworked from #702 because the original PR didn't model the
recipe-owned probe pattern (D13=A) or user_provided_models (D8=A).
Tests: bun test test/ai/ — 95/95 pass (8 new + 87 existing).
Co-Authored-By: SiyaoZheng <noreply@github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ai): add MiniMax recipe (#148 reworked)
11th recipe. embo-01 model, 1536 dims, $0.07/1M tokens.
OpenAI-compatible at api.minimax.chat. MiniMax requires a `type:
'db' | 'query'` field for asymmetric retrieval (documents indexed with
type='db', queries embedded with type='query'). gbrain has no
query/document signal at the embed-call site today, so v1 defaults to
type='db' for both indexing and retrieval — same vector space, symmetric
similarity. Asymmetric query support is a follow-up TODO that needs the
embed seam to thread query/document context.
Plumbed via src/core/ai/dims.ts: dimsProviderOptions returns
{openaiCompatible: {type: 'db'}} for modelId === 'embo-01'.
Conservative max_batch_tokens=4096 declared (MiniMax docs don't publish
the limit). Recursive halving in the gateway catches token-limit errors
at runtime.
Tests: bun test test/ai/ — 101/101 (6 new + 95 prior).
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 5
of 11). Reworked from #148.
Co-Authored-By: cacity <20351699+cacity@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ai): add Alibaba DashScope recipe (#59 split, part 1/2)
12th recipe. text-embedding-v3 (current) + text-embedding-v2; 1024
default dims with Matryoshka options [64, 128, 256, 512, 768, 1024].
OpenAI-compatible at dashscope-intl.aliyuncs.com. China-region users
override via cfg.base_urls['dashscope']; v0.32 ships with the
international default.
Conservative max_batch_tokens=8192 + chars_per_token=2 declared because
Alibaba doesn't publish a hard batch limit and text-embedding-v3 mixes
English + CJK heavily (CJK density closer to Voyage than OpenAI tiktoken).
Tests: bun test test/ai/ — 106/106 (5 new + 101 prior).
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 6
of 11). Reworked from #59 (DashScope+Zhipu split into 2 commits per
the plan; Zhipu lands next).
Co-Authored-By: Magicray1217 <267836857+Magicray1217@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ai): add Zhipu AI (BigModel) recipe (#59 split, part 2/2)
13th recipe. embedding-3 (current) + embedding-2; 1024 default dims
with Matryoshka options [256, 512, 1024, 2048].
OpenAI-compatible at open.bigmodel.cn. embedding-3 at 2048 dims exceeds
pgvector's HNSW cap of 2000 — those brains fall back to exact vector
scans via the existing chunkEmbeddingIndexSql policy at
src/core/vector-index.ts. Default stays at 1024 (HNSW-fast); users who
want maximum fidelity opt into 2048 via --embedding-dimensions and
accept the slower retrieval.
Tests pin the HNSW boundary: 1024 returns the index SQL, 2048 returns
the skip-index/exact-scan SQL.
Tests: bun test test/ai/ — 112/112 (6 new + 106 prior).
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 7
of 11). Reworked from #59. Together with DashScope (commit 6), closes
the China-region embedding gap users repeatedly reported (DashScope
covers Alibaba, Zhipu covers BigModel; both ship with international
endpoints by default).
Co-Authored-By: Magicray1217 <267836857+Magicray1217@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ai): add Azure OpenAI recipe (#459 reworked)
14th recipe and the first to exercise both v0.32 architectural seams:
- resolveAuth (D12=A) returns `{headerName: 'api-key', token: <key>}`
instead of the default Authorization Bearer. Azure rejects double-auth,
so applyResolveAuth puts the key in `headers` and OMITS apiKey.
- A new `Recipe.resolveOpenAICompatConfig?(env)` seam (Recipe.ts) lets
the recipe template the baseURL from env (Azure: ENDPOINT + DEPLOYMENT
combine into a non-/v1 path) and inject a custom fetch wrapper that
splices ?api-version= onto every request URL.
The fetch wrapper is type-safe via `as unknown as typeof fetch`; AI SDK
never calls TS's strict `preconnect()` method on the wrapper so the cast
is sound. `applyOpenAICompatConfig` (new gateway helper) routes through
the recipe override or falls back to the pre-v0.32 base_urls/base_url_default
behavior — existing 13 recipes get zero behavior change.
API version defaults to `2024-10-21` (current stable as of 2026-05);
override via AZURE_OPENAI_API_VERSION env. Endpoint trailing slash gets
stripped during URL construction so users can copy-paste from the Azure
portal.
Tests (12 cases in test/ai/recipe-azure-openai.test.ts):
- resolveAuth returns api-key NOT Authorization Bearer
- applyResolveAuth puts key in headers, NOT apiKey (no double-auth)
- baseURL templating from endpoint + deployment, with trailing-slash strip
- AIConfigError on missing endpoint OR deployment
- fetch wrapper splices api-version (default + AZURE_OPENAI_API_VERSION override)
- fetch wrapper does NOT double-add api-version when caller already set it
- applyOpenAICompatConfig honors recipe override
IRON RULE regression test updated: now asserts azure-openai is the
documented exception that overrides resolveAuth; any future override
needs review.
Tests: bun test test/ai/ — 124/124 (12 new + 112 prior).
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 8
of 11, plus the resolveOpenAICompatConfig seam discovered during fold-in).
Reworked from #459. The original PR proposed a hardcoded AzureOpenAI
client switch; this implementation routes through the unified seams so
future Azure-shaped providers (other custom-URL services) can reuse them.
Co-Authored-By: JamesJZhang <32652444+JamesJZhang@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ai): adjacent fixes — no_batch_cap (#779) + config-key fallbacks (#121)
Two small ergonomics fixes folded together (#765 deferred — see TODOS.md
follow-up; the CJK PGLite extraction was bigger than the plan estimated).
#779 reworked (alexandreroumieu-codeapprentice): silence the
missing-max_batch_tokens startup warning for recipes with genuinely
dynamic batch capacity. New `EmbeddingTouchpoint.no_batch_cap?: true`
field. Set on ollama (capacity depends on locally loaded model +
OLLAMA_NUM_PARALLEL), litellm-proxy (depends on backend), llama-server
(set by --ctx-size at server launch). Three less stderr warnings on
every gateway configure; google still warns (it's a real fixed-cap
provider that ought to ship a max_batch_tokens declaration).
Bonus: litellm-proxy now declares `user_provided_models: true`, removing
the last consumer of the legacy `recipe.id === 'litellm'` hardcode in
gateway.ts:223 (D8=A wire-through completion).
#121 reworked (vinsew): self-contained API keys. Two parts:
1. config.ts: ANTHROPIC_API_KEY env merge was silently missing.
loadConfig() merged OPENAI_API_KEY but not ANTHROPIC_API_KEY into
the file-config-shape result. One-line addition.
2. cli.ts:buildGatewayConfig: when ~/.gbrain/config.json declares
openai_api_key / anthropic_api_key but the process env doesn't
have those env vars set (common for launchd-spawned daemons,
agent subprocess tools, containers that don't propagate
~/.zshrc), fold the config-file values into the gateway env
snapshot. Process env still wins (loaded last) so per-process
overrides keep working.
Tests (4 cases in test/ai/no-batch-cap-suppression.test.ts):
- Ollama / LiteLLM / llama-server all declare no_batch_cap: true
- configureGateway does NOT warn for those three
- configureGateway STILL warns for google (regression guard)
- Cross-cutting invariant: empty-models recipes declare user_provided_models
Tests: bun test test/ai/ — 128/128 (4 new + 124 prior).
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 9 of 11).
#765 (Hunyuan PGLite + CJK keyword fallback) deferred to TODOS.md
follow-up; the CJK extraction (~150 lines + scoring logic + tests) is
larger than the wave's adjacent-fix lane should carry. Closes that PR
with a deferral note.
Co-Authored-By: alexandreroumieu-codeapprentice <noreply@github.com>
Co-Authored-By: vinsew <noreply@github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(discoverability): doctor alt-provider advisory + init user_provided_models refusal
Two small but high-leverage changes that address the discoverability
problem the v0.32 wave is trying to fix.
src/commands/doctor.ts: new `alternative_providers` check (8c). After
the existing embedding-provider smoke test, walks listRecipes() and
surfaces any recipe whose required env vars are ALL present in the
process env but is not the currently configured provider. Reports as
status: 'ok' with an informational message — never errors. Helps users
discover that, e.g., `OPENAI_API_KEY=x DASHSCOPE_API_KEY=y` configured
for openai means they have a Chinese-region alternative ready without
extra setup.
src/commands/init.ts: user_provided_models recipes (litellm, llama-server)
now refuse the implicit "first model" pick from shorthand --model with
a structured setup hint pointing the user at the explicit form
`--embedding-model <provider>:<your-model-id> --embedding-dimensions <N>`.
Pre-fix, shorthand --model litellm threw "no embedding models listed"
which was technically correct but unhelpful. The new error includes the
recipe's setup_hint when available.
Tests: bun test test/ai/ — 128/128 pass; typecheck clean.
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 10
of 11). The full interactive provider chooser in init.ts (the bigger
piece of the discoverability lane) is deferred to a v0.32.x follow-up;
this commit ships the doctor advisory + cleaner refusal that close the
80% case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(v0.32.0): embedding-providers.md + README callout + CHANGELOG + TODOS.md
Final commit of the v0.32 wave. Closes the discoverability gap that
generated the 17-PR community cluster.
- New docs/integrations/embedding-providers.md: capability matrix, decision
tree, per-recipe one-pagers, OAuth provider notes, "my provider isn't
listed" pointer to LiteLLM proxy. Voice: capability not marketing per
CLAUDE.md voice rules.
- README.md: embedding-providers callout near the top, naming the count
(14 recipes) and pointing at the new doc.
- CHANGELOG.md: v0.32.0 entry following the verdict-headline format from
CLAUDE.md voice rules. Lead-with-numbers ("14 providers, 5 new"), what-this-
means-for-users closer, "to take advantage" upgrade block, itemized
changes, contributor credits, deferred-with-context list.
- VERSION + package.json: 0.31.1 → 0.32.0. Minor bump justified by the
new public Recipe surface (resolveAuth, resolveOpenAICompatConfig, probe,
user_provided_models, no_batch_cap fields), the new OAuth subsystem
scaffold (deferred to v0.32.x but typed in v0.32.0), and the 5 new
recipes.
- TODOS.md: 7 follow-up entries for the v0.32 wave's deferred work
(Vertex ADC, Copilot OAuth, Codex OAuth, CJK PGLite, interactive
wizard, real-credentials CI matrix, MiniMax asymmetric retrieval,
multimodal hardcode un-stuck). Each entry has full context + the
exact file paths + the spike work needed so a future contributor can
pick up cleanly.
Tests: bun test test/ai/ — 128/128 pass; typecheck clean.
Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 11
of 11). Wave complete: 11 commits, ~1500 net lines, 5 new recipes, full
docs, doctor advisory, IRON RULE regression test, 7 TODOS for the
v0.32.x follow-up wave.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: regenerate llms.txt + llms-full.txt for v0.32.0
After commit
|
||
|
|
d97f159793 |
v0.26.4 test: parallel unit-test loop (12x speedup, failure-first logging) (#605)
* test: parallel unit-test wrapper + failure-first logging (commit 1/8) Lay foundation for v0.26.4 parallel test loop: - scripts/run-unit-parallel.sh: spawns N shards (default min(8, cpu_count)) via run-unit-shard.sh, captures per-shard logs, post-shard single-writer failure-log aggregation at .context/test-failures.log, 10s heartbeat to stderr, per-shard 600s timeout (gtimeout/timeout/bg-pid fallback chain), loud final banner with absolute path + tail-30 of failures, summary file for at-a-glance status. Single writer eliminates concurrent-write hazards on the failure log. - scripts/run-serial-tests.sh: discovers *.serial.test.ts files (concurrency- unsafe by design), runs them with --max-concurrency=1. Invoked after the parallel pass. - scripts/run-unit-shard.sh: now accepts --max-concurrency=N (forwarded to bun test); --dry-run-list moved into argv parsing alongside; excludes *.serial.test.ts in addition to *.slow.test.ts. - bunfig.toml: trim stale comment about typecheck-chained timeout. - .gitignore: add .context/ (Conductor workspace artifacts directory; the failure log + summary + per-shard logs all live here). No package.json changes yet (commit 2). No test reorganization yet (commits 4-7). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: split package.json scripts; bun run test = parallel fast loop (commit 2/8) Per Codex Tension #4 (verify scope), distinguish three tiers cleanly: - `bun run test` = fast loop, file-level parallel fan-out via the new wrapper (scripts/run-unit-parallel.sh). No pre-checks, no typecheck, no wasm compile in the hot path. ~15s of pre-test gates removed. - `bun run verify` = CI's authoritative gate set: check:jsonb + check:progress + check:wasm + typecheck. Matches what .github/workflows/test.yml runs on shard 1, no scope drift. The 4 checks not in CI (privacy, no-legacy-getconnection, trailing-newline, exports-count) move to `bun run check:all` for opt-in local use. - `bun run test:full` = verify + parallel + slow + smart e2e (runs e2e only if DATABASE_URL is set; else loud skip notice to stderr per Open Item #7). The local equivalent of "everything CI runs." Adds `bun run test:serial` for the *.serial.test.ts subset (concurrency- unsafe files run with --max-concurrency=1). Bumps VERSION + package.json to 0.26.4. Both move together per the CI version-gate contract in CLAUDE.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: fix-wave for parallel wrapper + tighten privacy gate (commit 3/5) Wave: makes the new wrapper actually green and tightens the CI gate it exposed. Wrapper bug fixes (scripts/run-unit-parallel.sh): - grep_count helper: avoids the `grep -c | echo 0` double-output bug where 0 matches yields a 2-line "0\n0" string and breaks arithmetic. - bun_summary_count helper: parses Bun's actual end-of-shard summary format (`N pass` / `N fail` / `N skip`), not the per-test markers (which are `✓` / `(fail)`, never `(pass)` / `(skip)`). - Heartbeat now reads `^\s+✓` (Bun's per-test pass marker) for live progress mid-run; final summary still uses the summary-line counts for accuracy. Privacy gate tightening: - Move scripts/check-privacy.sh into `bun run verify` (was previously only in the now-removed `bun run test` chain). Without this, after commit 2 the privacy check ran in nothing automatic. - .github/workflows/test.yml now calls `bun run verify` instead of inlining the gate list. Single source of truth for "what's the ship gate." This is what verify == CI was supposed to mean per Codex T#4. - Pre-existing `Wintermute` references in src/core/mounts-cache.ts:6 and :324 caught by the now-running gate; replaced with `your OpenClaw` per CLAUDE.md privacy rule (verify gate now passes on master HEAD). - test/privacy-script-wired.test.ts updated: regression guard now asserts verify includes check:privacy AND that test.yml runs `bun run verify`, replacing the obsolete "test script includes check-privacy.sh" assertion. Quarantine 2 cross-file-contention flakes: - test/brain-registry.test.ts: 28 tests pass alone (41ms); 1 test ("empty/null/undefined id routes to host") fails when run alongside other files in the same shard. Renamed → *.serial.test.ts so it runs in scripts/run-serial-tests.sh's serial pass after the parallel pass completes. - test/reconcile-links.test.ts: 6 tests pass alone (1s); a beforeEach hook times out (~896s) under cross-file contention. Same treatment. Both flakes are bun-process-level shared-state leaks (PGLite singletons or top-level imports). Fixing them properly is the v0.27.0+ intra-file parallelism project (TODO P0 — see commit 5). Measurement after this commit: bun run test = 94s (was 18 min sequential) 3639 pass, 0 fail, 0 skip across 8 parallel shards + 34 serial tests Failure-log + heartbeat + summary all working Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: regression tests for parallel wrapper + serial-test contracts (commit 4/5) Three regression suites pin the v0.26.4 contracts. Without these, future refactors of the wrapper or shard scripts could silently regress the work in commits 1-3. test/scripts/run-unit-shard.test.ts (4 cases — gap b): - Asserts the unit-shard `--dry-run-list` output excludes every *.slow.test.ts and *.serial.test.ts file, plus the test/e2e/ subtree. - Catches a future `find` expression that drops one of the `-not -name` clauses and silently un-quarantines slow/serial files into the parallel pass. test/scripts/serial-files.test.ts (3 cases — gap e): - Every checked-in *.serial.test.ts (via `git ls-files`) is listed by scripts/run-serial-tests.sh's `--dry-run-list`. - The script's source contains `bun test --max-concurrency=1` (the serial-pass guarantee that quarantined files don't run intra-file concurrent and reintroduce the contention they were quarantined for). - Disjoint set: a file is never in both the unit-shard list AND the serial list — pins the carve-out contract. test/scripts/run-unit-parallel.test.ts (6 cases — gaps a + d): - Exit-code propagation (a): wrapper exits non-zero when ANY shard has a failing test; exits zero when all pass. The hardest contract to silently break in a fan-out wrapper (`for ... &; wait` returns the LAST child's status, not any failure's). - Failure-log contract (d): on failure, .context/test-failures.log exists, is non-empty, contains the `--- shard N:` prefix and the failing test's describe text. Stderr banner contains the absolute log path. On success, the log is cleared (no stale content). - Summary file format: `shard N/M: pass=X fail=Y skip=Z rc=W` per shard, machine-parseable for future tooling. The wrapper test runs against a 4-file tempdir (3 pass + 1 fail) so it executes in ~500ms; spawning the wrapper against the real test suite would take ~90s and isn't worth the cost in a regression suite. All 13 cases pass on first run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(v0.26.4): testing tier docs + CHANGELOG + intra-file P0 TODO (commit 5/5) Closes the v0.26.4 ship. CLAUDE.md Testing section rewritten: - New tier table: test (fast loop, 85s) / verify (CI gates, 12s) / test:full (everything local) / test:slow / test:serial / test:e2e / check:all. Each row names its scope, wallclock, and when to use. - Intentional CI vs local divergence section: CI matrix (test-shard.sh, hash-bucketed, includes slow) vs local fast loop (run-unit-shard.sh, round-robin, excludes slow + serial). Codex correctly flagged that a parity test would always fail by design — this is the documentation that explains why. - Failure-first logging contract: .context/test-failures.log format, stderr banner, summary file, wedge handling. - File taxonomy: *.test.ts / *.slow.test.ts / *.serial.test.ts / test/e2e/. Names the two currently-quarantined files and points at the intra-file P0 TODO for the proper fix. CHANGELOG.md `## [0.26.4]` entry per voice rules: - Two-line headline: "bun run test finishes in 85 seconds. Was 18 minutes." + failure-log directive. - Lead paragraph names what shipped and why. - Numbers-that-matter table: BEFORE / AFTER / Δ for wallclock, pre-test gates, failure visibility, shards, pipe-survival. - "What this means for you" closing tied to the inner-loop user. - "To take advantage of v0.26.4" block per the v0.13+ self-repair template (gbrain upgrade + contributor steps). - Itemized changes by area (new scripts, script extensions, package.json tier split, CI tightening, failure-first logging, quarantine, regression tests, bunfig). - "What did NOT ship" section names the intra-file project + E2E template-DB project as P0/P1 follow-ups with concrete acceptance criteria. - Process section names the codex review + scope-correction loop honestly: "snapped back to ship today once empirical measurement showed Bun's --max-concurrency does nothing on tests not marked test.concurrent()." - For-contributors note on portability + single-writer + fallback paths. TODOS.md adds two P-rated entries: - P0: intra-file parallelism via --concurrent flag. Sweep ~58 PGLite sites + ~40 env mutations + 2 mock.module sites. Target: bun run test < 30s. ~1-2 weeks. Detailed acceptance criteria. References Codex findings and plan-file rationale. - P1: E2E parallelism via Postgres template databases. CREATE DATABASE TEMPLATE gbrain_template per test file. ~1-2 days. llms.txt + llms-full.txt regenerated via `bun run build:llms` to absorb the CLAUDE.md changes (per CLAUDE.md's "After any release ship that touches the Key Files annotations in CLAUDE.md, run bun run build:llms" rule). The build-llms regression test was firing in shard 7 of the parallel pass — caught the drift, regeneration cleared it. Final measurement after fix: 94s wallclock, 3652 pass, 0 fail across 8 parallel shards + 34 serial tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |