From 40fe36248918a29394f0471122d74700d2da11aa Mon Sep 17 00:00:00 2001 From: john Date: Tue, 21 Jul 2026 22:55:19 +0800 Subject: [PATCH] fix(memory): initialize candidate schema on admin bootstrap --- server/bootstrap.mjs | 4 +- server/personal-memory-candidate-store.mjs | 14 ++++++ .../personal-memory-candidate-store.test.mjs | 47 +++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 server/personal-memory-candidate-store.mjs create mode 100644 server/personal-memory-candidate-store.test.mjs diff --git a/server/bootstrap.mjs b/server/bootstrap.mjs index 707c830..18005d4 100644 --- a/server/bootstrap.mjs +++ b/server/bootstrap.mjs @@ -2,6 +2,7 @@ import path from 'node:path'; import { createDbPool, isDatabaseConfigured } from './db.mjs'; import { projectRoot } from './load-env.mjs'; import { importMemind, resolveMemindLib } from './lib-path.mjs'; +import { initializePersonalMemoryCandidateStore } from './personal-memory-candidate-store.mjs'; import { createSystemTestAccountService, ensureSystemTestAccountSchema } from './system-test-accounts.mjs'; export async function bootstrapAdminServices() { @@ -30,7 +31,6 @@ export async function bootstrapAdminServices() { } = await importMemind('mindspace-config.mjs'); const { createMemoryV2AdminConfigService } = await importMemind('memory-v2-admin-config.mjs'); const { createMindSearchConfigService } = await importMemind('mindsearch-config.mjs'); - const { createPersonalMemoryCandidateStore } = await importMemind('memory-v2-personal-store.mjs'); const { createSkillRuntimeAdminConfigService } = await importMemind('skill-runtime-admin-config.mjs'); const { createAssetGatewayConfigService } = await importMemind('asset-gateway.mjs'); const { createImageMakeAdminConfigService } = await importMemind('image-make-admin-config.mjs'); @@ -104,7 +104,7 @@ export async function bootstrapAdminServices() { }); const mindSearchConfigService = createMindSearchConfigService(pool, { env: process.env }); await mindSearchConfigService.ensureSchema(); - const personalMemoryCandidateStore = createPersonalMemoryCandidateStore(pool); + const personalMemoryCandidateStore = await initializePersonalMemoryCandidateStore(pool); const skillRuntimeConfigService = createSkillRuntimeAdminConfigService(pool, { env: process.env, h5Root, diff --git a/server/personal-memory-candidate-store.mjs b/server/personal-memory-candidate-store.mjs new file mode 100644 index 0000000..92034bc --- /dev/null +++ b/server/personal-memory-candidate-store.mjs @@ -0,0 +1,14 @@ +import { importMemind } from './lib-path.mjs'; + +export async function initializePersonalMemoryCandidateStore( + pool, + { importMemindModule = importMemind } = {}, +) { + const { + createPersonalMemoryCandidateStore, + ensurePersonalMemoryCandidateSchema, + } = await importMemindModule('memory-v2-personal-store.mjs'); + + await ensurePersonalMemoryCandidateSchema(pool); + return createPersonalMemoryCandidateStore(pool); +} diff --git a/server/personal-memory-candidate-store.test.mjs b/server/personal-memory-candidate-store.test.mjs new file mode 100644 index 0000000..ca90f46 --- /dev/null +++ b/server/personal-memory-candidate-store.test.mjs @@ -0,0 +1,47 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { initializePersonalMemoryCandidateStore } from './personal-memory-candidate-store.mjs'; + +test('admin initializes the candidate schema before creating its store', async () => { + const calls = []; + const pool = { query() {} }; + const store = { listCandidates() {} }; + + const result = await initializePersonalMemoryCandidateStore(pool, { + async importMemindModule(subpath) { + assert.equal(subpath, 'memory-v2-personal-store.mjs'); + return { + async ensurePersonalMemoryCandidateSchema(receivedPool) { + assert.equal(receivedPool, pool); + calls.push('ensure'); + }, + createPersonalMemoryCandidateStore(receivedPool) { + assert.equal(receivedPool, pool); + calls.push('create'); + return store; + }, + }; + }, + }); + + assert.equal(result, store); + assert.deepEqual(calls, ['ensure', 'create']); +}); + +test('admin fails bootstrap when the candidate schema cannot be initialized', async () => { + await assert.rejects( + initializePersonalMemoryCandidateStore({}, { + async importMemindModule() { + return { + async ensurePersonalMemoryCandidateSchema() { + throw new Error('candidate ddl failed'); + }, + createPersonalMemoryCandidateStore() { + throw new Error('store must not be created'); + }, + }; + }, + }), + /candidate ddl failed/, + ); +});