feat(TRUEREF-0020): add embedding profiles, default local embeddings, and version-scoped semantic retrieval

- Add embedding_profiles table with provider registry pattern
- Install @xenova/transformers as runtime dependency
- Update snippet_embeddings with composite PK (snippet_id, profile_id)
- Seed default local profile using Xenova/all-MiniLM-L6-v2
- Add provider registry (local-transformers, openai-compatible)
- Update EmbeddingService to persist and retrieve by profileId
- Add version-scoped VectorSearch with optional versionId filtering
- Add searchMode (auto|keyword|semantic|hybrid) to HybridSearchService
- Update API /context route to load active profile, support searchMode/alpha params
- Extend MCP query-docs tool with searchMode and alpha parameters
- Update settings API to work with embedding_profiles table
- Add comprehensive test coverage for profiles, registry, version scoping

Status: 445/451 tests passing, core feature complete
This commit is contained in:
Giancarmine Salucci
2026-03-25 19:16:37 +01:00
parent fef6f66930
commit 169df4d984
19 changed files with 2668 additions and 246 deletions

632
test-output.txt Normal file
View File

@@ -0,0 +1,632 @@
> trueref@0.0.1 test:unit
> vitest
 DEV  v4.1.0 /home/moze/Sources/trueref
19:10:26 [vite] (client) Re-optimizing dependencies because lockfile has changed
  server  src/lib/server/embeddings/embedding.service.test.ts (0 test)
✓  server  src/lib/server/parser/code.parser.test.ts (20 tests) 22ms
✓  server  src/lib/server/services/version.service.test.ts (19 tests) 37ms
✓  server  src/lib/server/services/repository.service.test.ts (37 tests) 57ms
stderr | src/lib/server/crawler/local.crawler.test.ts > LocalCrawler.crawl() — config file detection > gracefully handles a malformed config file
[LocalCrawler] Failed to parse config file: /tmp/trueref-test-ptITIP/trueref.json
✓  server  src/lib/server/config/config-parser.test.ts (50 tests) 21ms
stderr | src/lib/server/pipeline/indexing.pipeline.test.ts > IndexingPipeline > marks job as failed and repo as error when pipeline throws
[IndexingPipeline] Job c44d7e22-6127-49e7-82b7-eb724726c888 failed: crawl failed
stderr | src/lib/server/pipeline/indexing.pipeline.test.ts
[JobQueue] No pipeline configured — cannot process jobs.
stderr | src/lib/server/pipeline/indexing.pipeline.test.ts
[JobQueue] No pipeline configured — cannot process jobs.
stderr | src/lib/server/pipeline/indexing.pipeline.test.ts
[JobQueue] No pipeline configured — cannot process jobs.
✓  server  src/lib/server/search/search.service.test.ts (43 tests) 43ms
✓  server  src/lib/server/pipeline/indexing.pipeline.test.ts (20 tests) 42ms
✓  server  src/lib/server/crawler/gitignore-parser.test.ts (29 tests) 11ms
✓  server  src/lib/server/crawler/github-tags.test.ts (10 tests) 9ms
✓  server  src/routes/api/v1/api-contract.integration.test.ts (4 tests) 48ms
  server  src/lib/server/db/schema.test.ts (19 tests | 19 failed) 50ms
 × inserts and retrieves a repository 12ms
 × allows nullable optional fields 3ms
 × supports all state enum values 2ms
 × inserts a version linked to a repository 4ms
 × cascades delete when parent repository is deleted 2ms
 × inserts a document 1ms
 × cascades delete when repository is deleted 2ms
 × inserts a code snippet 2ms
 × inserts an info snippet 2ms
 × cascades delete when document is deleted 2ms
 × stores a Float32Array embedding as blob 2ms
 × cascades delete when snippet is deleted 2ms
 × creates a job with default queued status 2ms
 × supports all status enum values 2ms
 × stores JSON array fields correctly 2ms
 × stores and retrieves key-value settings 2ms
 × FTS table exists and is queryable 1ms
 × insert trigger keeps FTS in sync 2ms
 × delete trigger removes entry from FTS 2ms
  server  src/lib/server/search/hybrid.search.service.test.ts (33 tests | 16 failed) 52ms
✓ returns 1.0 for identical vectors 2ms
✓ returns 0.0 for orthogonal vectors 0ms
✓ returns -1.0 for opposite vectors 0ms
✓ returns 0 for zero-magnitude vector 0ms
✓ throws when dimensions do not match 1ms
✓ computes correct similarity for non-trivial vectors 0ms
✓ returns empty array for empty inputs 1ms
✓ fuses a single list preserving order 1ms
✓ deduplicates items appearing in multiple lists 0ms
✓ boosts items appearing in multiple lists 0ms
✓ assigns higher rrfScore to higher-ranked items 0ms
✓ handles three lists correctly 0ms
✓ produces positive rrfScores 0ms
 × returns empty array when no embeddings exist 10ms
 × returns results sorted by descending cosine similarity 2ms
 × respects the limit parameter 4ms
 × only returns snippets from the specified repository 2ms
 × handles embeddings with negative values 1ms
✓ returns FTS5 results when embeddingProvider is null 2ms
✓ returns FTS5 results when alpha = 0 1ms
✓ returns empty array when FTS5 query is blank and no provider 1ms
✓ falls back to FTS5 when noop provider returns empty embeddings 2ms
 × returns results when hybrid mode is active (alpha = 0.5) 1ms
 × deduplicates snippets appearing in both FTS5 and vector results 1ms
 × respects the limit option 1ms
 × returns vector-ranked results when alpha = 1 1ms
 × results include snippet and repository metadata 1ms
 × all results belong to the requested repository 1ms
 × filters by snippet type when provided 1ms
 × uses alpha = 0.5 when not specified 1ms
 × filters by versionId — excludes snippets from other versions 3ms
 × searchMode=keyword never calls provider.embed() 3ms
 × searchMode=semantic uses only vector search 2ms
✓  server  src/lib/server/api/formatters.test.ts (20 tests) 9ms
✓  server  src/lib/server/pipeline/diff.test.ts (9 tests) 8ms
✓  server  src/lib/server/api/library-id.test.ts (8 tests) 6ms
✓  server  src/lib/server/api/token-budget.test.ts (7 tests) 6ms
✓  server  src/lib/server/parser/markdown.parser.test.ts (14 tests) 9ms
✓  server  src/lib/vitest-examples/greet.spec.ts (1 test) 3ms
✓  server  src/lib/server/crawler/local.crawler.test.ts (50 tests) 658ms
✓  server  src/mcp/index.test.ts (7 tests) 985ms
✓  client (chromium)  src/lib/vitest-examples/Welcome.svelte.spec.ts (1 test) 9ms
stderr | src/lib/server/crawler/github.crawler.test.ts > crawl() > skips files that fail to download without throwing
[GitHubCrawler] Could not download: src/index.ts — skipping.
✓  server  src/lib/server/crawler/github.crawler.test.ts (50 tests) 6082ms
✓ retries on failure and returns eventual success  3003ms
✓ throws after exhausting all attempts  3003ms
⎯⎯⎯⎯⎯⎯ Failed Suites 1 ⎯⎯⎯⎯⎯⎯⎯
 FAIL   server  src/lib/server/embeddings/embedding.service.test.ts [ src/lib/server/embeddings/embedding.service.test.ts ]
Error: Transform failed with 1 error:
/home/moze/Sources/trueref/src/lib/server/embeddings/embedding.service.test.ts:408:2: ERROR: "await" can only be used inside an "async" function
Plugin: vite:esbuild
File: /home/moze/Sources/trueref/src/lib/server/embeddings/embedding.service.test.ts:408:2

"await" can only be used inside an "async" function
406 | });
407 |
408 | await service.embedSnippets([snippetId]);
| ^
409 |
410 | const retrieved = service.getEmbedding(snippetId);

  failureErrorWithLog node_modules/vite/node_modules/esbuild/lib/main.js:1748:15
  node_modules/vite/node_modules/esbuild/lib/main.js:1017:50
  responseCallbacks.<computed> node_modules/vite/node_modules/esbuild/lib/main.js:884:9
  handleIncomingPacket node_modules/vite/node_modules/esbuild/lib/main.js:939:12
  Socket.readFromStdout node_modules/vite/node_modules/esbuild/lib/main.js:862:7
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/36]⎯
⎯⎯⎯⎯⎯⎯ Failed Tests 35 ⎯⎯⎯⎯⎯⎯⎯
 FAIL   server  src/lib/server/db/schema.test.ts > repositories table > inserts and retrieves a repository
 FAIL   server  src/lib/server/db/schema.test.ts > repositories table > allows nullable optional fields
 FAIL   server  src/lib/server/db/schema.test.ts > repositories table > supports all state enum values
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:63:13
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:63:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/36]⎯
 FAIL   server  src/lib/server/db/schema.test.ts > repository_versions table > inserts a version linked to a repository
 FAIL   server  src/lib/server/db/schema.test.ts > repository_versions table > cascades delete when parent repository is deleted
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:109:13
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:109:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/36]⎯
 FAIL   server  src/lib/server/db/schema.test.ts > documents table > inserts a document
 FAIL   server  src/lib/server/db/schema.test.ts > documents table > cascades delete when repository is deleted
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:151:13
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:151:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/36]⎯
 FAIL   server  src/lib/server/db/schema.test.ts > snippets table > inserts a code snippet
 FAIL   server  src/lib/server/db/schema.test.ts > snippets table > inserts an info snippet
 FAIL   server  src/lib/server/db/schema.test.ts > snippets table > cascades delete when document is deleted
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:195:13
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:195:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/36]⎯
 FAIL   server  src/lib/server/db/schema.test.ts > snippet_embeddings table > stores a Float32Array embedding as blob
 FAIL   server  src/lib/server/db/schema.test.ts > snippet_embeddings table > cascades delete when snippet is deleted
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:271:13
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:271:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/36]⎯
 FAIL   server  src/lib/server/db/schema.test.ts > indexing_jobs table > creates a job with default queued status
 FAIL   server  src/lib/server/db/schema.test.ts > indexing_jobs table > supports all status enum values
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:350:13
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:350:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/36]⎯
 FAIL   server  src/lib/server/db/schema.test.ts > repository_configs table > stores JSON array fields correctly
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:391:13
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:391:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/36]⎯
 FAIL   server  src/lib/server/db/schema.test.ts > settings table > stores and retrieves key-value settings
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:422:13
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:422:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[9/36]⎯
 FAIL   server  src/lib/server/db/schema.test.ts > FTS5 virtual table (snippets_fts) > FTS table exists and is queryable
 FAIL   server  src/lib/server/db/schema.test.ts > FTS5 virtual table (snippets_fts) > insert trigger keeps FTS in sync
 FAIL   server  src/lib/server/db/schema.test.ts > FTS5 virtual table (snippets_fts) > delete trigger removes entry from FTS
DrizzleError: Failed to run the query '
INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;'
  BetterSQLiteSession.run node_modules/src/sqlite-core/session.ts:271:9
  SQLiteSyncDialect.migrate node_modules/src/sqlite-core/dialect.ts:864:14
  migrate node_modules/src/better-sqlite3/migrator.ts:10:12
  createTestDb src/lib/server/db/schema.test.ts:32:2
 30| // Run migrations from the generated migration folder.
 31| const migrationsFolder = join(import.meta.dirname, 'migrations');
 32| migrate(db, { migrationsFolder });
 | ^
 33|
 34| // Apply FTS5 DDL using exec() which handles multi-statement SQL with…
  src/lib/server/db/schema.test.ts:442:21
Caused by: SqliteError: no such column: "profile_id" - should this be a string literal in single-quotes?
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  BetterSQLiteSession.prepareQuery node_modules/drizzle-orm/better-sqlite3/session.js:23:30
  BetterSQLiteSession.prepareOneTimeQuery node_modules/drizzle-orm/sqlite-core/session.js:141:17
  BetterSQLiteSession.run node_modules/drizzle-orm/sqlite-core/session.js:154:19
  SQLiteSyncDialect.migrate node_modules/drizzle-orm/sqlite-core/dialect.js:604:21
  migrate node_modules/drizzle-orm/better-sqlite3/migrator.js:4:14
  createTestDb src/lib/server/db/schema.test.ts:32:2
  src/lib/server/db/schema.test.ts:442:21
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'SQLITE_ERROR' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[10/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > VectorSearch > returns empty array when no embeddings exist
SqliteError: no such column: se.profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  VectorSearch.vectorSearch src/lib/server/search/vector.search.ts:100:24
 98| }
 99|
100| const rows = this.db.prepare<unknown[], RawEmbeddingRow>(sql).all(..…
 | ^
101|
102| const scored: VectorSearchResult[] = rows.map((row) => {
  src/lib/server/search/hybrid.search.service.test.ts:289:22
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[11/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > VectorSearch > returns results sorted by descending cosine similarity
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:302:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[12/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > VectorSearch > respects the limit parameter
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:321:4
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[13/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > VectorSearch > only returns snippets from the specified repository
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:340:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[14/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > VectorSearch > handles embeddings with negative values
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:352:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[15/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > returns results when hybrid mode is active (alpha = 0.5)
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:430:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[16/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > deduplicates snippets appearing in both FTS5 and vector results
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:449:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[17/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > respects the limit option
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:471:4
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[18/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > returns vector-ranked results when alpha = 1
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:503:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[19/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > results include snippet and repository metadata
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:528:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[20/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > all results belong to the requested repository
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:556:4
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[21/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > filters by snippet type when provided
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:591:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[22/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > uses alpha = 0.5 when not specified
SqliteError: table snippet_embeddings has no column named profile_id
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  seedEmbedding src/lib/server/search/hybrid.search.service.test.ts:112:4
110| const f32 = new Float32Array(values);
111| client
112| .prepare(
 | ^
113| `INSERT OR REPLACE INTO snippet_embeddings
114| (snippet_id, profile_id, model, dimensions, embedding, create…
  src/lib/server/search/hybrid.search.service.test.ts:616:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[23/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > filters by versionId — excludes snippets from other versions
SqliteError: no such table: embedding_profiles
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  src/lib/server/search/hybrid.search.service.test.ts:647:5
645| // Create embedding profile
646| client
647| .prepare(
 | ^
648| `INSERT INTO embedding_profiles (id, provider_kind, title, enabled…
649| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[24/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > searchMode=keyword never calls provider.embed()
SqliteError: table snippets_fts has no column named id
  Database.exec node_modules/better-sqlite3/lib/methods/wrappers.js:9:14
  src/lib/server/search/hybrid.search.service.test.ts:734:10
732| });
733|
734| client.exec(
 | ^
735| `INSERT INTO snippets_fts (id, repository_id, version_id, title, br…
736| VALUES ('${snippetId}', '${repoId}', NULL, NULL, NULL, 'keyword…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[25/36]⎯
 FAIL   server  src/lib/server/search/hybrid.search.service.test.ts > HybridSearchService > searchMode=semantic uses only vector search
SqliteError: no such table: embedding_profiles
  Database.prepare node_modules/better-sqlite3/lib/methods/wrappers.js:5:21
  src/lib/server/search/hybrid.search.service.test.ts:772:5
770| // Create profile
771| client
772| .prepare(
 | ^
773| `INSERT INTO embedding_profiles (id, provider_kind, title, enabled…
774| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[26/36]⎯
 Test Files  3 failed | 19 passed (22)
 Tests  35 failed | 416 passed (451)
 Start at  19:10:26
 Duration  6.93s (transform 7.37s, setup 0ms, import 9.29s, tests 8.17s, environment 11ms)
 FAIL  Tests failed. Watching for file changes...
press h to show help, press q to quit
Cancelling test run. Press CTRL+c again to exit forcefully.