* feat(migrate): provider-agnostic embedding migration service — the path off ZeroEntropy (#3390)
- gbrain migrate embeddings --to <provider:model> (alias: retrieval-upgrade):
plan + cost preflight, consent gate (--yes / TTY confirm / non-TTY exit 2),
live probe against the target provider before any mutation, env-override
gate, schema dimension transition via the shared runSchemaTransition,
dual-plane config write, NULL-signature-inclusive invalidation, query-cache
purge, resumable re-embed through the standard embed pipeline (single-flight
locks, backoff, pacing, stderr progress). Killed runs resume by re-running
the same command; the NULL-embedding column is the checkpoint.
- #3391 root-cause fix (both engines): countStaleChunks / sumStaleChunkChars /
invalidateStaleSignatureEmbeddings accept includeNullSignature to lift the
v108 grandfather clause; embed --stale warns loudly when a model swap
leaves NULL-signature pages in the old embedding space, and
--include-null-signature re-embeds them. Default sweep behavior unchanged.
- knobs_hash v=12 → v=13 (prov=default legacy callers must not be served
pre-migration cache rows).
- migrate_embeddings op: scope admin, localOnly, hidden cliHints, hard
remote refusal, needs_confirmation without yes=true.
- One-shot post-upgrade ZE-sunset banner (ze_sunset_notice_shown) for brains
resolving to a zeroentropyai:* embedding model or reranker.
- doctor's dimension-mismatch repair hint now names the real command.
- Docs: docs/guides/embedding-migration.md, KEY_FILES entries, spend-controls
gate row. Tests: PGLite unit + full-lifecycle flow (interrupted-run resume),
real-Postgres e2e (pgvector DDL path + #3391 predicate parity).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(test): satisfy check:test-isolation + bump the remaining knobs_hash pins
- test/migrate-embeddings-flow.test.ts → .serial.test.ts: the file holds a
temp GBRAIN_HOME + an installed fake embed transport for its whole
lifecycle (beforeAll → afterAll), which withEnv() can't wrap. This also
fixes the CI shard-pollution failure in
test/ai/recipes-existing-regression.test.ts (that file passes solo on both
master and this branch; the flow test's configureGateway + provider-key
deletion was leaking into it inside the same shard process).
- test/embedding-migration.test.ts: env-override case now uses withEnv().
- Bump the three remaining KNOBS_HASH_VERSION pins to 13
(cross-modal-phase1, search-alias-resolved-boost, search/knobs-hash-reranker).
- Docs + llms bundles follow the test rename.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(test): wire the new Postgres e2e into the smart e2e selector map
Changes to embed.ts / embedding-migration.ts / retrieval-upgrade-planner.ts /
postgres-engine.ts now trigger test/e2e/migrate-embeddings-postgres.test.ts —
the #3391 stale predicates and runSchemaTransition's DDL path behave
differently on real pgvector than on PGLite, so the smart selector has to know.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(migrate): consult spend.posture in the embedding-migration consent gate
The brief asked the gate to honor spend.posture; it previously didn't read it
at all. Now it does — but deliberately does NOT bypass on tokenmax: posture
waives the spend CEILING, and this gate also guards a destructive schema
rebuild (existing vectors dropped, retrieval degraded until the re-embed
finishes). Under tokenmax the dollar figure is marked informational on stderr
and the confirmation is still asked; --yes stays the single scripted bypass.
Pinned by a new case in the flow test so a later refactor can't quietly turn
posture into a bypass. Guide + spend-controls table updated to match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* wip: blocker fixes
---------
Co-authored-by: Garry Tan <garrytan@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>