fix(ROUTING-0001): repair repo routing and isolate MCP filtering

This commit is contained in:
Giancarmine Salucci
2026-03-27 19:01:47 +01:00
parent da661efc91
commit d1381f7fc0
10 changed files with 252 additions and 644 deletions

View File

@@ -165,3 +165,26 @@ Add subsequent research below this section.
- Risks / follow-ups:
- Base-aware navigation fixes must preserve internal app routing semantics and should not replace intentional external navigation, because SvelteKit `goto(...)` no longer accepts external URLs.
- Settings and search page lifecycle changes must avoid reintroducing SSR-triggered fetches or self-triggered URL loops; client-only bootstrap logic should remain mounted once and URL-sync effects must stay idempotent.
### 2026-03-27 — ROUTING-0001 planning research
- Task: Plan the repository-detail routing fix for slash-bearing repository IDs causing homepage SSR failures and invalid `/repos/[id]` navigation.
- Files inspected:
- `package.json`
- `src/lib/components/RepositoryCard.svelte`
- `src/routes/+page.svelte`
- `src/routes/+page.server.ts`
- `src/routes/repos/[id]/+page.server.ts`
- `src/routes/repos/[id]/+page.svelte`
- `src/routes/api/v1/api-contract.integration.test.ts`
- `src/lib/types.ts`
- Findings:
- The app is on SvelteKit `^2.50.2` and uses `$app/paths.resolve(...)` for internal navigation, including `resolveRoute('/repos/[id]', { id: repo.id })` in `RepositoryCard.svelte`.
- SvelteKits `[id]` route is a single-segment dynamic parameter. Context7 routing docs show slash-containing values belong to rest parameters like `[...param]`, so raw repository IDs containing `/` are invalid inputs for `resolveRoute('/repos/[id]', ...)`.
- The repository model intentionally stores slash-bearing IDs such as `/facebook/react`, and the existing API surface consistently treats those IDs as percent-encoded path segments. The integration contract already passes `params.id = encodeURIComponent('/facebook/react')` for `/api/v1/libs/[id]` handlers, which then call `decodeURIComponent(params.id)`.
- The homepage SSR failure is therefore rooted in UI link generation, not repository listing fetches: rendering `RepositoryCard.svelte` with a raw slash-bearing `repo.id` can throw before page load completes, which explains repeated `500` responses on `/`.
- The repo detail page currently forwards `params.id` directly into `encodeURIComponent(...)` for downstream API requests. Once detail links are generated as encoded single segments, the page loader and client-side refresh/delete/reindex flows need one normalization step so API calls continue targeting the stored repository ID instead of a doubly encoded value.
- No existing browser-facing test covers homepage card navigation or `/repos/[id]` loader behavior; the closest current evidence is the API contract test file, which already exercises encoded repository IDs on HTTP endpoints and provides reusable fixtures for slash-bearing IDs.
- Risks / follow-ups:
- The fix should preserve the existing `/repos/[id]` route shape instead of redesigning it to a rest route unless a broader navigation contract change is explicitly requested.
- Any normalization helper introduced for the repo detail page should be reused consistently across server load and client event handlers to avoid mixed encoded and decoded repository IDs during navigation and fetches.

79
scripts/build.mjs Normal file
View File

@@ -0,0 +1,79 @@
import { mkdir, readdir, rename } from 'node:fs/promises';
import { basename, dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawn } from 'node:child_process';
const rootDir = dirname(dirname(fileURLToPath(import.meta.url)));
const routesDir = join(rootDir, 'src', 'routes');
const renamedRouteTestFiles = [];
async function collectReservedRouteTestFiles(directory) {
const entries = await readdir(directory, { withFileTypes: true });
const files = [];
for (const entry of entries) {
const entryPath = join(directory, entry.name);
if (entry.isDirectory()) {
files.push(...(await collectReservedRouteTestFiles(entryPath)));
continue;
}
if (!entry.name.startsWith('+')) {
continue;
}
if (!entry.name.includes('.test.') && !entry.name.includes('.spec.')) {
continue;
}
files.push(entryPath);
}
return files;
}
async function renameReservedRouteTests() {
const reservedRouteTestFiles = await collectReservedRouteTestFiles(routesDir);
for (const sourcePath of reservedRouteTestFiles) {
const targetPath = join(dirname(sourcePath), basename(sourcePath).slice(1));
await rename(sourcePath, targetPath);
renamedRouteTestFiles.push({ sourcePath, targetPath });
}
}
async function restoreReservedRouteTests() {
for (const { sourcePath, targetPath } of renamedRouteTestFiles.reverse()) {
await mkdir(dirname(sourcePath), { recursive: true });
await rename(targetPath, sourcePath);
}
}
function runViteBuild() {
const viteBinPath = join(rootDir, 'node_modules', 'vite', 'bin', 'vite.js');
return new Promise((resolve, reject) => {
const child = spawn(process.execPath, [viteBinPath, 'build'], {
cwd: rootDir,
stdio: 'inherit'
});
child.once('error', reject);
child.once('exit', (code) => {
if (code === 0) {
resolve();
return;
}
reject(new Error(`vite build exited with code ${code ?? 'unknown'}`));
});
});
}
try {
await renameReservedRouteTests();
await runViteBuild();
} finally {
await restoreReservedRouteTests();
}

View File

@@ -26,6 +26,10 @@
error: 'Error'
};
const detailsHref = $derived(
resolveRoute('/repos/[id]', { id: encodeURIComponent(repo.id) })
);
const totalSnippets = $derived(repo.totalSnippets ?? 0);
const trustScore = $derived(repo.trustScore ?? 0);
</script>
@@ -77,7 +81,7 @@
{repo.state === 'indexing' ? 'Indexing...' : 'Re-index'}
</button>
<a
href={resolveRoute('/repos/[id]', { id: repo.id })}
href={detailsHref}
class="rounded-lg border border-gray-200 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50"
>
Details

View File

@@ -0,0 +1,27 @@
import { page } from 'vitest/browser';
import { describe, expect, it, vi } from 'vitest';
import { render } from 'vitest-browser-svelte';
import RepositoryCard from './RepositoryCard.svelte';
describe('RepositoryCard.svelte', () => {
it('encodes slash-bearing repository ids in the details href', async () => {
render(RepositoryCard, {
repo: {
id: '/facebook/react',
title: 'React',
description: 'A JavaScript library for building user interfaces',
state: 'indexed',
totalSnippets: 1234,
trustScore: 9.7,
stars: 230000,
lastIndexedAt: null
} as never,
onReindex: vi.fn(),
onDelete: vi.fn()
});
await expect
.element(page.getByRole('link', { name: 'Details' }))
.toHaveAttribute('href', '/repos/%2Ffacebook%2Freact');
});
});

View File

@@ -39,6 +39,7 @@ import { GET as getJobs } from './jobs/+server.js';
import { GET as getJob } from './jobs/[id]/+server.js';
import { GET as getVersions, POST as postVersions } from './libs/[id]/versions/+server.js';
import { GET as getContext } from './context/+server.js';
import { DEFAULT_TOKEN_BUDGET } from '$lib/server/api/token-budget.js';
const NOW_S = Math.floor(Date.now() / 1000);
@@ -325,6 +326,40 @@ describe('API contract integration', () => {
expect(body).toContain('Result count: 0');
});
it('GET /api/v1/context does not token-filter default JSON responses for the UI', async () => {
const repositoryId = seedRepo(db);
const documentId = seedDocument(db, repositoryId);
seedSnippet(db, {
documentId,
repositoryId,
type: 'info',
title: 'Large result',
content: 'Large result body',
tokenCount: DEFAULT_TOKEN_BUDGET + 1
});
seedSnippet(db, {
documentId,
repositoryId,
type: 'info',
title: 'Small result',
content: 'Small result body',
tokenCount: 5
});
const response = await getContext({
url: new URL(
`http://test/api/v1/context?libraryId=${encodeURIComponent(repositoryId)}&query=${encodeURIComponent('result')}`
)
} as never);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.snippets).toHaveLength(2);
expect(body.resultCount).toBe(2);
});
it('GET /api/v1/context returns additive repository and version metadata for versioned results', async () => {
const repositoryId = seedRepo(db);
const versionId = seedVersion(db, repositoryId, 'v18.3.0');

View File

@@ -124,6 +124,7 @@ export const GET: RequestHandler = async ({ url }) => {
}
const responseType = url.searchParams.get('type') ?? 'json';
const applyTokenBudget = responseType === 'txt' || url.searchParams.has('tokens');
const tokensRaw = parseInt(url.searchParams.get('tokens') ?? String(DEFAULT_TOKEN_BUDGET), 10);
const maxTokens = isNaN(tokensRaw) || tokensRaw < 1 ? DEFAULT_TOKEN_BUDGET : tokensRaw;
@@ -212,15 +213,17 @@ export const GET: RequestHandler = async ({ url }) => {
profileId
});
// Apply token budget.
const selectedResults = applyTokenBudget
? (() => {
const snippets = searchResults.map((r) => r.snippet);
const selected = selectSnippetsWithinBudget(snippets, maxTokens);
// Re-wrap selected snippets as SnippetSearchResult for formatters.
const selectedResults = selected.map((snippet) => {
return selected.map((snippet) => {
const found = searchResults.find((r) => r.snippet.id === snippet.id)!;
return found;
});
})()
: searchResults;
const snippetVersionIds = Array.from(
new Set(

View File

@@ -2,8 +2,8 @@ import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ fetch, params }) => {
const id = params.id;
const res = await fetch(`/api/v1/libs/${encodeURIComponent(id)}`);
const repositoryId = decodeURIComponent(params.id);
const res = await fetch(`/api/v1/libs/${encodeURIComponent(repositoryId)}`);
if (res.status === 404) {
error(404, 'Repository not found');
@@ -16,7 +16,9 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
const repo = await res.json();
// Fetch recent jobs
const jobsRes = await fetch(`/api/v1/jobs?repositoryId=${encodeURIComponent(id)}&limit=5`);
const jobsRes = await fetch(
`/api/v1/jobs?repositoryId=${encodeURIComponent(repositoryId)}&limit=5`
);
const jobsData = jobsRes.ok ? await jobsRes.json() : { jobs: [] };
return {

View File

@@ -0,0 +1,34 @@
import { describe, expect, it, vi } from 'vitest';
import { load } from './+page.server';
describe('/repos/[id] page server load', () => {
it('decodes the route param once before calling downstream APIs', async () => {
const fetch = vi
.fn()
.mockResolvedValueOnce({
ok: true,
status: 200,
json: async () => ({ id: '/facebook/react', title: 'React' })
})
.mockResolvedValueOnce({
ok: true,
status: 200,
json: async () => ({ jobs: [{ id: 'job-1', repositoryId: '/facebook/react' }] })
});
const result = await load({
fetch,
params: { id: encodeURIComponent('/facebook/react') }
} as never);
expect(fetch).toHaveBeenNthCalledWith(1, '/api/v1/libs/%2Ffacebook%2Freact');
expect(fetch).toHaveBeenNthCalledWith(
2,
'/api/v1/jobs?repositoryId=%2Ffacebook%2Freact&limit=5'
);
expect(result).toEqual({
repo: { id: '/facebook/react', title: 'React' },
recentJobs: [{ id: 'job-1', repositoryId: '/facebook/react' }]
});
});
});

View File

@@ -0,0 +1,33 @@
import { readdirSync } from 'node:fs';
import { join } from 'node:path';
import { describe, expect, it } from 'vitest';
function collectReservedRouteTestFiles(directory: string): string[] {
const entries = readdirSync(directory, { withFileTypes: true });
const reservedTestFiles: string[] = [];
for (const entry of entries) {
const entryPath = join(directory, entry.name);
if (entry.isDirectory()) {
reservedTestFiles.push(...collectReservedRouteTestFiles(entryPath));
continue;
}
if (!entry.name.startsWith('+')) continue;
if (!entry.name.includes('.test.') && !entry.name.includes('.spec.')) continue;
reservedTestFiles.push(entryPath);
}
return reservedTestFiles;
}
describe('SvelteKit route file conventions', () => {
it('does not place test files in reserved +prefixed route filenames', () => {
const routeDirectory = import.meta.dirname;
const reservedTestFiles = collectReservedRouteTestFiles(routeDirectory);
expect(reservedTestFiles).toEqual([]);
});
});

View File

@@ -1,632 +0,0 @@
> 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.