watashi-db 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +36 -0
- package/LICENSE +1 -1
- package/README.md +33 -2
- package/cowork-plugin/skills/groom/SKILL.md +51 -15
- package/cowork-plugin/skills/recall/SKILL.md +5 -6
- package/cowork-plugin/skills/reflect/SKILL.md +4 -4
- package/cowork-plugin/skills/remember/SKILL.md +3 -3
- package/cowork-plugin/skills/session-start/SKILL.md +3 -3
- package/dist/config/schema.js +1 -1
- package/dist/constants.d.ts +5 -1
- package/dist/constants.js +19 -3
- package/dist/constants.js.map +1 -1
- package/dist/database/archive.js +6 -6
- package/dist/database/queries-core.d.ts +75 -1
- package/dist/database/queries-core.js +283 -12
- package/dist/database/queries-core.js.map +1 -1
- package/dist/database/queries.d.ts +71 -1
- package/dist/database/queries.js +29 -0
- package/dist/database/queries.js.map +1 -1
- package/dist/database/schema.d.ts +1 -0
- package/dist/database/schema.js +1915 -214
- package/dist/database/schema.js.map +1 -1
- package/dist/embedding/embed-on-write.d.ts +7 -1
- package/dist/embedding/embed-on-write.js +8 -3
- package/dist/embedding/embed-on-write.js.map +1 -1
- package/dist/hook.js +9 -6
- package/dist/hook.js.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/resources/config-guide-content.d.ts +1 -1
- package/dist/resources/config-guide-content.js +2 -2
- package/dist/server-instructions.js +16 -17
- package/dist/server-instructions.js.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.js +42 -18
- package/dist/server.js.map +1 -1
- package/dist/setup.js +5 -6
- package/dist/setup.js.map +1 -1
- package/dist/store/federation.d.ts +12 -1
- package/dist/store/federation.js +38 -0
- package/dist/store/federation.js.map +1 -1
- package/dist/store/sync-manager.d.ts +1 -1
- package/dist/store/sync-manager.js +9 -9
- package/dist/tools/claim-tools.d.ts +1 -1
- package/dist/tools/claim-tools.js +7 -7
- package/dist/tools/claim-tools.js.map +1 -1
- package/dist/tools/decision-tools.d.ts +1 -1
- package/dist/tools/decision-tools.js +2 -2
- package/dist/tools/decision-tools.js.map +1 -1
- package/dist/tools/episode-tools.d.ts +1 -1
- package/dist/tools/episode-tools.js +2 -2
- package/dist/tools/episode-tools.js.map +1 -1
- package/dist/tools/file-tools.d.ts +3 -0
- package/dist/tools/file-tools.js +347 -0
- package/dist/tools/file-tools.js.map +1 -0
- package/dist/tools/get-tools.d.ts +1 -1
- package/dist/tools/get-tools.js +39 -5
- package/dist/tools/get-tools.js.map +1 -1
- package/dist/tools/knowledge-tools.d.ts +1 -1
- package/dist/tools/knowledge-tools.js +2 -2
- package/dist/tools/knowledge-tools.js.map +1 -1
- package/dist/tools/maintenance-tools.d.ts +1 -1
- package/dist/tools/maintenance-tools.js +38 -6
- package/dist/tools/maintenance-tools.js.map +1 -1
- package/dist/tools/memo-tools.d.ts +7 -11
- package/dist/tools/memo-tools.js +499 -307
- package/dist/tools/memo-tools.js.map +1 -1
- package/dist/tools/query-tools.d.ts +1 -1
- package/dist/tools/query-tools.js +28 -5
- package/dist/tools/query-tools.js.map +1 -1
- package/dist/types.d.ts +370 -48
- package/dist/types.js +124 -16
- package/dist/types.js.map +1 -1
- package/misc/20260316_110841_groom-recipe.md +483 -0
- package/misc/20260316_xaml-testing-library-recipe.md +817 -0
- package/package.json +4 -2
- package/scripts/update-license-version.sh +7 -0
package/dist/database/schema.js
CHANGED
|
@@ -25,13 +25,28 @@ import fs from "node:fs";
|
|
|
25
25
|
// 2026-02-28 修正: V18追加(user_plans + user_issues テーブル新設 — Issue #57)
|
|
26
26
|
// 2026-03-01 修正: V19追加(Insight l1/l2 拡張 — Issue #53)
|
|
27
27
|
// 2026-03-01 修正: V20追加(全エンティティ l1/l2 カラム拡張 — Issue #59)
|
|
28
|
-
// 2026-03-03 修正: V21追加(audit_log に
|
|
28
|
+
// 2026-03-03 修正: V21追加(audit_log に device_id カラム追加 + backfill)
|
|
29
29
|
// 2026-03-03 修正: V22追加(entity_id インデックス追加 — Issue #60)
|
|
30
30
|
// 2026-03-03 修正: V23追加(Claim に l1_content 追加 + embedding → l1_embedding リネーム — Issue #64)
|
|
31
31
|
// 2026-03-04 修正: V24追加(user_memo/plan/issue の content → l1_content + user_memos に user_input 追加 — Issue #54)
|
|
32
32
|
// 2026-03-06 修正: V25追加(user_issues に kind カラム追加 — Issue #74)
|
|
33
33
|
// 2026-03-13 修正: V28追加(evidence → l2_evidence, falsifier → l2_falsifier リネーム)
|
|
34
|
-
|
|
34
|
+
// 2026-03-18 修正: V29追加(user_topics テーブル新設)
|
|
35
|
+
// 2026-03-19 squash: applyV1 を V30 相当のフルスキーマに書き直し。
|
|
36
|
+
// 新規DBでは applyV1 が全テーブルを作成し schema_version (1)〜(30) を一括挿入するため、
|
|
37
|
+
// V2〜V30 のマイグレーションは全てスキップされる。既存ユーザー向けに V2〜V30 はそのまま残す。
|
|
38
|
+
// 2026-03-24 修正: V31追加(File Vault — user_files テーブル + unified search統合)
|
|
39
|
+
const SCHEMA_VERSION = 31;
|
|
40
|
+
// devMode フラグ(startServer から schema 初期化経路に伝播)
|
|
41
|
+
// develop.db では released: false のマイグレーションも適用する
|
|
42
|
+
let _devMode = false;
|
|
43
|
+
export function setDevMode(enabled) {
|
|
44
|
+
_devMode = enabled;
|
|
45
|
+
}
|
|
46
|
+
// V31 以降で released フラグを使う場合:
|
|
47
|
+
// if (version < 31) {
|
|
48
|
+
// if (_devMode || V31_RELEASED) applyV31(db);
|
|
49
|
+
// }
|
|
35
50
|
/**
|
|
36
51
|
* データベーススキーマの初期化とマイグレーション
|
|
37
52
|
*
|
|
@@ -50,6 +65,11 @@ export function initializeSchema(db) {
|
|
|
50
65
|
`);
|
|
51
66
|
const currentVersion = db.prepare("SELECT MAX(version) as version FROM schema_version").get();
|
|
52
67
|
const version = currentVersion?.version ?? 0;
|
|
68
|
+
// 孤立した _new テーブルの掃除(マイグレーション部分適用の残骸)
|
|
69
|
+
const orphanedNewTables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%_new'").all();
|
|
70
|
+
for (const { name } of orphanedNewTables) {
|
|
71
|
+
db.exec(`DROP TABLE IF EXISTS "${name}"`);
|
|
72
|
+
}
|
|
53
73
|
if (version >= SCHEMA_VERSION) {
|
|
54
74
|
return; // 既に最新
|
|
55
75
|
}
|
|
@@ -119,6 +139,10 @@ export function initializeSchema(db) {
|
|
|
119
139
|
function _applyMigrations(db, version) {
|
|
120
140
|
if (version < 1) {
|
|
121
141
|
applyV1(db);
|
|
142
|
+
// squash 対応: applyV1 が V30 相当のフルスキーマを作成し schema_version (1)〜(30) を
|
|
143
|
+
// 一括挿入した場合、以降のマイグレーションをスキップするために version を再読み込みする
|
|
144
|
+
const row = db.prepare("SELECT MAX(version) as version FROM schema_version").get();
|
|
145
|
+
version = row?.version ?? 1;
|
|
122
146
|
}
|
|
123
147
|
if (version < 2) {
|
|
124
148
|
applyV2(db);
|
|
@@ -201,82 +225,414 @@ function _applyMigrations(db, version) {
|
|
|
201
225
|
if (version < 28) {
|
|
202
226
|
applyV28(db);
|
|
203
227
|
}
|
|
228
|
+
if (version < 29) {
|
|
229
|
+
applyV29(db);
|
|
230
|
+
}
|
|
231
|
+
if (version < 30) {
|
|
232
|
+
applyV30(db);
|
|
233
|
+
}
|
|
234
|
+
if (version < 31) {
|
|
235
|
+
applyV31(db);
|
|
236
|
+
}
|
|
204
237
|
}
|
|
205
238
|
/**
|
|
206
|
-
* V1スキーマ:
|
|
239
|
+
* V1スキーマ: V30相当のフルスキーマ(squash済み)
|
|
240
|
+
*
|
|
241
|
+
* 2026-03-19: applyV1 を V30 時点の最終テーブル定義で書き直し。
|
|
242
|
+
* 新規ユーザーが V1→V30 まで逐次マイグレーションする非効率を解消。
|
|
243
|
+
* V2〜V30 の関数は既存ユーザー向けにそのまま残してある。
|
|
244
|
+
* applyV1 の最後に schema_version (1)〜(30) を全挿入することで、
|
|
245
|
+
* _applyMigrations の各 `if (version < N)` ガードにより V2〜V30 が全てスキップされる。
|
|
207
246
|
*/
|
|
208
247
|
function applyV1(db) {
|
|
248
|
+
// JSON配列クリーニングヘルパー(FTSトリガー用)
|
|
249
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
250
|
+
// user_topics 用の JSON クリーニング(V29 パターン)
|
|
251
|
+
const jcTopics = (col) => `REPLACE(REPLACE(${col}, '[', ''), ']', '')`;
|
|
252
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
253
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
254
|
+
// ================================================================
|
|
255
|
+
// 1. テーブル作成(CREATE TABLE + インデックス)
|
|
256
|
+
// ================================================================
|
|
209
257
|
db.exec(`
|
|
210
|
-
--
|
|
258
|
+
-- --------------------------------------------------------
|
|
259
|
+
-- claims: 知識断片(L2 SPO三つ組)
|
|
260
|
+
-- --------------------------------------------------------
|
|
211
261
|
CREATE TABLE IF NOT EXISTS claims (
|
|
212
262
|
id TEXT PRIMARY KEY,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
263
|
+
l2_subject TEXT NOT NULL,
|
|
264
|
+
l2_predicate TEXT NOT NULL,
|
|
265
|
+
l2_object TEXT NOT NULL,
|
|
216
266
|
category TEXT NOT NULL CHECK(category IN ('preference','identity','skill','value','workflow','knowledge','custom')),
|
|
217
267
|
scope TEXT NOT NULL DEFAULT 'global',
|
|
218
268
|
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
219
|
-
|
|
269
|
+
l2_evidence TEXT,
|
|
270
|
+
l2_falsifier TEXT,
|
|
271
|
+
l1_content TEXT,
|
|
272
|
+
search_summary TEXT,
|
|
273
|
+
l1_embedding BLOB,
|
|
274
|
+
hit_count INTEGER NOT NULL DEFAULT 0,
|
|
275
|
+
last_hit_at TEXT,
|
|
276
|
+
promoted_from_store TEXT,
|
|
277
|
+
promoted_from_id TEXT,
|
|
278
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','retracted','superseded','promoted')),
|
|
220
279
|
source_tool TEXT,
|
|
221
280
|
source_session TEXT,
|
|
222
|
-
|
|
281
|
+
client_name TEXT,
|
|
282
|
+
client_version TEXT,
|
|
223
283
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
224
284
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
225
285
|
);
|
|
226
286
|
|
|
227
|
-
-- claims用インデックス
|
|
228
287
|
CREATE INDEX IF NOT EXISTS idx_claims_category ON claims(category);
|
|
229
288
|
CREATE INDEX IF NOT EXISTS idx_claims_scope ON claims(scope);
|
|
230
289
|
CREATE INDEX IF NOT EXISTS idx_claims_status ON claims(status);
|
|
231
|
-
CREATE INDEX IF NOT EXISTS
|
|
290
|
+
CREATE INDEX IF NOT EXISTS idx_claims_l2_subject ON claims(l2_subject);
|
|
232
291
|
CREATE INDEX IF NOT EXISTS idx_claims_updated ON claims(updated_at);
|
|
292
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_claims_promoted_from ON claims(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL;
|
|
233
293
|
|
|
234
|
-
--
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
294
|
+
-- --------------------------------------------------------
|
|
295
|
+
-- episodes: エピソード記録
|
|
296
|
+
-- --------------------------------------------------------
|
|
297
|
+
CREATE TABLE IF NOT EXISTS episodes (
|
|
298
|
+
id TEXT PRIMARY KEY,
|
|
299
|
+
title TEXT NOT NULL,
|
|
300
|
+
l1_content TEXT,
|
|
301
|
+
l2_context TEXT,
|
|
302
|
+
l2_trigger TEXT,
|
|
303
|
+
l2_problems TEXT NOT NULL DEFAULT '[]',
|
|
304
|
+
l2_desires TEXT NOT NULL DEFAULT '[]',
|
|
305
|
+
l2_decisions TEXT NOT NULL DEFAULT '[]',
|
|
306
|
+
l2_outcomes TEXT NOT NULL DEFAULT '[]',
|
|
307
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
308
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
309
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
310
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
311
|
+
search_summary TEXT,
|
|
312
|
+
l1_embedding BLOB,
|
|
313
|
+
promoted_from_store TEXT,
|
|
314
|
+
promoted_from_id TEXT,
|
|
315
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
316
|
+
groomed_at TEXT,
|
|
317
|
+
source_tool TEXT,
|
|
318
|
+
session_id TEXT,
|
|
319
|
+
client_name TEXT,
|
|
320
|
+
client_version TEXT,
|
|
321
|
+
user_input TEXT,
|
|
322
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
323
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
240
324
|
);
|
|
241
325
|
|
|
242
|
-
|
|
243
|
-
CREATE
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
-- FTS同期トリガー: UPDATE
|
|
249
|
-
CREATE TRIGGER IF NOT EXISTS claims_fts_update AFTER UPDATE ON claims BEGIN
|
|
250
|
-
INSERT INTO claims_fts(claims_fts, rowid, subject, predicate, object, evidence)
|
|
251
|
-
VALUES ('delete', old.rowid, old.subject, old.predicate, old.object, old.evidence);
|
|
252
|
-
INSERT INTO claims_fts(rowid, subject, predicate, object, evidence)
|
|
253
|
-
VALUES (new.rowid, new.subject, new.predicate, new.object, new.evidence);
|
|
254
|
-
END;
|
|
255
|
-
|
|
256
|
-
-- FTS同期トリガー: DELETE
|
|
257
|
-
CREATE TRIGGER IF NOT EXISTS claims_fts_delete AFTER DELETE ON claims BEGIN
|
|
258
|
-
INSERT INTO claims_fts(claims_fts, rowid, subject, predicate, object, evidence)
|
|
259
|
-
VALUES ('delete', old.rowid, old.subject, old.predicate, old.object, old.evidence);
|
|
260
|
-
END;
|
|
326
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_scope ON episodes(scope);
|
|
327
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_status ON episodes(status);
|
|
328
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_groomed ON episodes(groomed_at);
|
|
329
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_created ON episodes(created_at);
|
|
330
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_updated ON episodes(updated_at);
|
|
331
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_episodes_promoted_from ON episodes(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL;
|
|
261
332
|
|
|
333
|
+
-- --------------------------------------------------------
|
|
262
334
|
-- decisions: 意思決定ログ
|
|
335
|
+
-- --------------------------------------------------------
|
|
263
336
|
CREATE TABLE IF NOT EXISTS decisions (
|
|
264
337
|
id TEXT PRIMARY KEY,
|
|
265
338
|
title TEXT NOT NULL,
|
|
266
339
|
description TEXT NOT NULL,
|
|
267
|
-
|
|
268
|
-
|
|
340
|
+
l1_content TEXT,
|
|
341
|
+
l2_reasoning TEXT NOT NULL,
|
|
342
|
+
l2_alternatives TEXT NOT NULL DEFAULT '[]',
|
|
269
343
|
related_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
270
|
-
|
|
344
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
345
|
+
search_summary TEXT,
|
|
346
|
+
l1_embedding BLOB,
|
|
347
|
+
promoted_from_store TEXT,
|
|
348
|
+
promoted_from_id TEXT,
|
|
349
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','reversed','obsolete','promoted')),
|
|
271
350
|
source_tool TEXT,
|
|
272
|
-
|
|
273
|
-
|
|
351
|
+
client_name TEXT,
|
|
352
|
+
client_version TEXT,
|
|
353
|
+
user_input TEXT,
|
|
354
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
355
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
274
356
|
);
|
|
275
357
|
|
|
358
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_scope ON decisions(scope);
|
|
276
359
|
CREATE INDEX IF NOT EXISTS idx_decisions_status ON decisions(status);
|
|
277
360
|
CREATE INDEX IF NOT EXISTS idx_decisions_created ON decisions(created_at);
|
|
361
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_updated ON decisions(updated_at);
|
|
362
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_decisions_promoted_from ON decisions(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL;
|
|
363
|
+
|
|
364
|
+
-- --------------------------------------------------------
|
|
365
|
+
-- theories: 体系的知識・フレームワーク
|
|
366
|
+
-- --------------------------------------------------------
|
|
367
|
+
CREATE TABLE IF NOT EXISTS theories (
|
|
368
|
+
id TEXT PRIMARY KEY,
|
|
369
|
+
title TEXT NOT NULL,
|
|
370
|
+
description TEXT,
|
|
371
|
+
l1_content TEXT,
|
|
372
|
+
l2_core_thesis TEXT,
|
|
373
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
374
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
375
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
376
|
+
l2_applicable_context TEXT,
|
|
377
|
+
non_goals TEXT NOT NULL DEFAULT '[]',
|
|
378
|
+
open_questions TEXT NOT NULL DEFAULT '[]',
|
|
379
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
380
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
381
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
382
|
+
search_summary TEXT,
|
|
383
|
+
l1_embedding BLOB,
|
|
384
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
385
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
386
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
387
|
+
promoted_from_store TEXT,
|
|
388
|
+
promoted_from_id TEXT,
|
|
389
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
390
|
+
source_tool TEXT,
|
|
391
|
+
session_id TEXT,
|
|
392
|
+
client_name TEXT,
|
|
393
|
+
client_version TEXT,
|
|
394
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
395
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
CREATE INDEX IF NOT EXISTS idx_theories_scope ON theories(scope);
|
|
399
|
+
CREATE INDEX IF NOT EXISTS idx_theories_status ON theories(status);
|
|
400
|
+
CREATE INDEX IF NOT EXISTS idx_theories_confidence ON theories(confidence);
|
|
401
|
+
CREATE INDEX IF NOT EXISTS idx_theories_created ON theories(created_at);
|
|
402
|
+
CREATE INDEX IF NOT EXISTS idx_theories_updated ON theories(updated_at);
|
|
403
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_theories_promoted_from ON theories(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL;
|
|
404
|
+
|
|
405
|
+
-- --------------------------------------------------------
|
|
406
|
+
-- insights: 洞察(theories のサブセット、non_goals/open_questions なし)
|
|
407
|
+
-- --------------------------------------------------------
|
|
408
|
+
CREATE TABLE IF NOT EXISTS insights (
|
|
409
|
+
id TEXT PRIMARY KEY,
|
|
410
|
+
title TEXT NOT NULL,
|
|
411
|
+
description TEXT,
|
|
412
|
+
l1_content TEXT,
|
|
413
|
+
l2_core_thesis TEXT,
|
|
414
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
415
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
416
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
417
|
+
l2_applicable_context TEXT,
|
|
418
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
419
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
420
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
421
|
+
search_summary TEXT,
|
|
422
|
+
l1_embedding BLOB,
|
|
423
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
424
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
425
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
426
|
+
promoted_from_store TEXT,
|
|
427
|
+
promoted_from_id TEXT,
|
|
428
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
429
|
+
source_tool TEXT,
|
|
430
|
+
session_id TEXT,
|
|
431
|
+
client_name TEXT,
|
|
432
|
+
client_version TEXT,
|
|
433
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
434
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
CREATE INDEX IF NOT EXISTS idx_insights_scope ON insights(scope);
|
|
438
|
+
CREATE INDEX IF NOT EXISTS idx_insights_status ON insights(status);
|
|
439
|
+
CREATE INDEX IF NOT EXISTS idx_insights_confidence ON insights(confidence);
|
|
440
|
+
CREATE INDEX IF NOT EXISTS idx_insights_created ON insights(created_at);
|
|
441
|
+
CREATE INDEX IF NOT EXISTS idx_insights_updated ON insights(updated_at);
|
|
442
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_insights_promoted_from ON insights(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL;
|
|
443
|
+
|
|
444
|
+
-- --------------------------------------------------------
|
|
445
|
+
-- models: モデル(theories と同構造)
|
|
446
|
+
-- --------------------------------------------------------
|
|
447
|
+
CREATE TABLE IF NOT EXISTS models (
|
|
448
|
+
id TEXT PRIMARY KEY,
|
|
449
|
+
title TEXT NOT NULL,
|
|
450
|
+
description TEXT,
|
|
451
|
+
l1_content TEXT,
|
|
452
|
+
l2_core_thesis TEXT,
|
|
453
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
454
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
455
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
456
|
+
l2_applicable_context TEXT,
|
|
457
|
+
non_goals TEXT NOT NULL DEFAULT '[]',
|
|
458
|
+
open_questions TEXT NOT NULL DEFAULT '[]',
|
|
459
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
460
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
461
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
462
|
+
search_summary TEXT,
|
|
463
|
+
l1_embedding BLOB,
|
|
464
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
465
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
466
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
467
|
+
promoted_from_store TEXT,
|
|
468
|
+
promoted_from_id TEXT,
|
|
469
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
470
|
+
source_tool TEXT,
|
|
471
|
+
session_id TEXT,
|
|
472
|
+
client_name TEXT,
|
|
473
|
+
client_version TEXT,
|
|
474
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
475
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
CREATE INDEX IF NOT EXISTS idx_models_scope ON models(scope);
|
|
479
|
+
CREATE INDEX IF NOT EXISTS idx_models_status ON models(status);
|
|
480
|
+
CREATE INDEX IF NOT EXISTS idx_models_confidence ON models(confidence);
|
|
481
|
+
CREATE INDEX IF NOT EXISTS idx_models_created ON models(created_at);
|
|
482
|
+
CREATE INDEX IF NOT EXISTS idx_models_updated ON models(updated_at);
|
|
483
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_models_promoted_from ON models(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL;
|
|
484
|
+
|
|
485
|
+
-- --------------------------------------------------------
|
|
486
|
+
-- user_memos: ユーザーメモ
|
|
487
|
+
-- --------------------------------------------------------
|
|
488
|
+
CREATE TABLE IF NOT EXISTS user_memos (
|
|
489
|
+
id TEXT PRIMARY KEY,
|
|
490
|
+
title TEXT NOT NULL,
|
|
491
|
+
l1_content TEXT NOT NULL,
|
|
492
|
+
usage_policy TEXT NOT NULL DEFAULT 'on_request'
|
|
493
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
494
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
495
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
496
|
+
search_summary TEXT,
|
|
497
|
+
user_input TEXT,
|
|
498
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
499
|
+
CHECK(status IN ('active','archived')),
|
|
500
|
+
l1_embedding BLOB,
|
|
501
|
+
client_name TEXT,
|
|
502
|
+
client_version TEXT,
|
|
503
|
+
source_tool TEXT,
|
|
504
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
505
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
CREATE INDEX IF NOT EXISTS idx_user_memos_status ON user_memos(status);
|
|
509
|
+
CREATE INDEX IF NOT EXISTS idx_user_memos_scope ON user_memos(scope);
|
|
510
|
+
CREATE INDEX IF NOT EXISTS idx_user_memos_usage_policy ON user_memos(usage_policy);
|
|
511
|
+
CREATE INDEX IF NOT EXISTS idx_user_memos_updated ON user_memos(updated_at);
|
|
512
|
+
|
|
513
|
+
-- --------------------------------------------------------
|
|
514
|
+
-- user_plans: ユーザープラン
|
|
515
|
+
-- --------------------------------------------------------
|
|
516
|
+
CREATE TABLE IF NOT EXISTS user_plans (
|
|
517
|
+
id TEXT PRIMARY KEY,
|
|
518
|
+
title TEXT NOT NULL,
|
|
519
|
+
l1_content TEXT NOT NULL,
|
|
520
|
+
usage_policy TEXT NOT NULL DEFAULT 'auto'
|
|
521
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
522
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
523
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
524
|
+
search_summary TEXT,
|
|
525
|
+
user_input TEXT,
|
|
526
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
527
|
+
CHECK(status IN ('active','completed','archived')),
|
|
528
|
+
l1_embedding BLOB,
|
|
529
|
+
client_name TEXT,
|
|
530
|
+
client_version TEXT,
|
|
531
|
+
source_tool TEXT,
|
|
532
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
533
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
CREATE INDEX IF NOT EXISTS idx_user_plans_status ON user_plans(status);
|
|
537
|
+
CREATE INDEX IF NOT EXISTS idx_user_plans_scope ON user_plans(scope);
|
|
538
|
+
CREATE INDEX IF NOT EXISTS idx_user_plans_usage_policy ON user_plans(usage_policy);
|
|
539
|
+
CREATE INDEX IF NOT EXISTS idx_user_plans_updated ON user_plans(updated_at);
|
|
540
|
+
|
|
541
|
+
-- --------------------------------------------------------
|
|
542
|
+
-- user_issues: ユーザー課題
|
|
543
|
+
-- --------------------------------------------------------
|
|
544
|
+
CREATE TABLE IF NOT EXISTS user_issues (
|
|
545
|
+
id TEXT PRIMARY KEY,
|
|
546
|
+
title TEXT NOT NULL,
|
|
547
|
+
l1_content TEXT NOT NULL,
|
|
548
|
+
entries TEXT NOT NULL DEFAULT '[]',
|
|
549
|
+
kind TEXT NOT NULL DEFAULT 'issue'
|
|
550
|
+
CHECK(kind IN ('issue','discussion')),
|
|
551
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
552
|
+
CHECK(priority IN ('low','medium','high','critical')),
|
|
553
|
+
usage_policy TEXT NOT NULL DEFAULT 'on_request'
|
|
554
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
555
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
556
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
557
|
+
search_summary TEXT,
|
|
558
|
+
user_input TEXT,
|
|
559
|
+
status TEXT NOT NULL DEFAULT 'open'
|
|
560
|
+
CHECK(status IN ('open','closed','archived')),
|
|
561
|
+
l1_embedding BLOB,
|
|
562
|
+
client_name TEXT,
|
|
563
|
+
client_version TEXT,
|
|
564
|
+
source_tool TEXT,
|
|
565
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
566
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
CREATE INDEX IF NOT EXISTS idx_user_issues_status ON user_issues(status);
|
|
570
|
+
CREATE INDEX IF NOT EXISTS idx_user_issues_scope ON user_issues(scope);
|
|
571
|
+
CREATE INDEX IF NOT EXISTS idx_user_issues_priority ON user_issues(priority);
|
|
572
|
+
CREATE INDEX IF NOT EXISTS idx_user_issues_updated ON user_issues(updated_at);
|
|
573
|
+
|
|
574
|
+
-- --------------------------------------------------------
|
|
575
|
+
-- user_topics: 話題管理
|
|
576
|
+
-- --------------------------------------------------------
|
|
577
|
+
CREATE TABLE IF NOT EXISTS user_topics (
|
|
578
|
+
id TEXT PRIMARY KEY,
|
|
579
|
+
title TEXT NOT NULL,
|
|
580
|
+
summary TEXT NOT NULL,
|
|
581
|
+
device_id TEXT,
|
|
582
|
+
cwd TEXT,
|
|
583
|
+
conversation_id TEXT,
|
|
584
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
585
|
+
CHECK(priority IN ('low','medium','high','critical')),
|
|
586
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
587
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
588
|
+
search_summary TEXT,
|
|
589
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
590
|
+
CHECK(status IN ('active','closed','archived')),
|
|
591
|
+
l1_embedding BLOB,
|
|
592
|
+
client_name TEXT,
|
|
593
|
+
client_version TEXT,
|
|
594
|
+
source_tool TEXT,
|
|
595
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
596
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
CREATE INDEX IF NOT EXISTS idx_user_topics_status ON user_topics(status);
|
|
600
|
+
CREATE INDEX IF NOT EXISTS idx_user_topics_scope ON user_topics(scope);
|
|
601
|
+
CREATE INDEX IF NOT EXISTS idx_user_topics_priority ON user_topics(priority);
|
|
602
|
+
CREATE INDEX IF NOT EXISTS idx_user_topics_updated ON user_topics(updated_at);
|
|
603
|
+
|
|
604
|
+
-- --------------------------------------------------------
|
|
605
|
+
-- user_files: ファイル管理(File Vault機能, V31)
|
|
606
|
+
-- --------------------------------------------------------
|
|
607
|
+
CREATE TABLE IF NOT EXISTS user_files (
|
|
608
|
+
id TEXT PRIMARY KEY,
|
|
609
|
+
title TEXT NOT NULL,
|
|
610
|
+
description TEXT,
|
|
611
|
+
original_filename TEXT NOT NULL,
|
|
612
|
+
original_encoding TEXT NOT NULL DEFAULT 'utf-8',
|
|
613
|
+
file_data TEXT NOT NULL,
|
|
614
|
+
file_hash TEXT NOT NULL,
|
|
615
|
+
file_size INTEGER NOT NULL,
|
|
616
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
617
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
618
|
+
search_summary TEXT,
|
|
619
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
620
|
+
CHECK(status IN ('active','archived')),
|
|
621
|
+
l1_embedding BLOB,
|
|
622
|
+
client_name TEXT,
|
|
623
|
+
client_version TEXT,
|
|
624
|
+
source_tool TEXT,
|
|
625
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
626
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
CREATE INDEX IF NOT EXISTS idx_user_files_status ON user_files(status);
|
|
630
|
+
CREATE INDEX IF NOT EXISTS idx_user_files_scope ON user_files(scope);
|
|
631
|
+
CREATE INDEX IF NOT EXISTS idx_user_files_title ON user_files(title);
|
|
278
632
|
|
|
279
|
-
--
|
|
633
|
+
-- --------------------------------------------------------
|
|
634
|
+
-- claim_history: Claim変更履歴 (V12: created_at 追加)
|
|
635
|
+
-- --------------------------------------------------------
|
|
280
636
|
CREATE TABLE IF NOT EXISTS claim_history (
|
|
281
637
|
id TEXT PRIMARY KEY,
|
|
282
638
|
claim_id TEXT NOT NULL REFERENCES claims(id),
|
|
@@ -284,30 +640,16 @@ function applyV1(db) {
|
|
|
284
640
|
old_value TEXT,
|
|
285
641
|
new_value TEXT,
|
|
286
642
|
reason TEXT NOT NULL,
|
|
287
|
-
changed_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
643
|
+
changed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
644
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
288
645
|
);
|
|
289
646
|
|
|
290
647
|
CREATE INDEX IF NOT EXISTS idx_claim_history_claim_id ON claim_history(claim_id);
|
|
291
648
|
CREATE INDEX IF NOT EXISTS idx_claim_history_changed ON claim_history(changed_at);
|
|
292
649
|
|
|
293
|
-
--
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* V2スキーマ: L2 Core v0対応
|
|
299
|
-
* - claims に falsifier カラム追加
|
|
300
|
-
* - claim_relations テーブル追加(推論グラフ)
|
|
301
|
-
* - claim_evidence テーブル追加(構造化根拠参照)
|
|
302
|
-
* - claim_checks テーブル追加(検証ログ)
|
|
303
|
-
* 仕様: docs/specs/l2-core-v0.md
|
|
304
|
-
*/
|
|
305
|
-
function applyV2(db) {
|
|
306
|
-
db.exec(`
|
|
307
|
-
-- claims に反証条件カラム追加
|
|
308
|
-
ALTER TABLE claims ADD COLUMN falsifier TEXT;
|
|
309
|
-
|
|
310
|
-
-- claim_relations: Claim間の推論関係(正規化)
|
|
650
|
+
-- --------------------------------------------------------
|
|
651
|
+
-- claim_relations: Claim間の推論関係 (V12: updated_at 追加)
|
|
652
|
+
-- --------------------------------------------------------
|
|
311
653
|
CREATE TABLE IF NOT EXISTS claim_relations (
|
|
312
654
|
id TEXT PRIMARY KEY,
|
|
313
655
|
source_claim_id TEXT NOT NULL,
|
|
@@ -318,14 +660,17 @@ function applyV2(db) {
|
|
|
318
660
|
confidence REAL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
319
661
|
reasoning TEXT,
|
|
320
662
|
source_tool TEXT,
|
|
321
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
663
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
664
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
322
665
|
);
|
|
323
666
|
|
|
324
667
|
CREATE INDEX IF NOT EXISTS idx_claim_relations_source ON claim_relations(source_claim_id);
|
|
325
668
|
CREATE INDEX IF NOT EXISTS idx_claim_relations_target ON claim_relations(target_claim_id);
|
|
326
669
|
CREATE INDEX IF NOT EXISTS idx_claim_relations_type ON claim_relations(relation_type);
|
|
327
670
|
|
|
328
|
-
--
|
|
671
|
+
-- --------------------------------------------------------
|
|
672
|
+
-- claim_evidence: 構造化された根拠参照 (V12: updated_at 追加)
|
|
673
|
+
-- --------------------------------------------------------
|
|
329
674
|
CREATE TABLE IF NOT EXISTS claim_evidence (
|
|
330
675
|
id TEXT PRIMARY KEY,
|
|
331
676
|
claim_id TEXT NOT NULL REFERENCES claims(id),
|
|
@@ -338,13 +683,16 @@ function applyV2(db) {
|
|
|
338
683
|
description TEXT,
|
|
339
684
|
verified_at TEXT,
|
|
340
685
|
source_tool TEXT,
|
|
341
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
686
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
687
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
342
688
|
);
|
|
343
689
|
|
|
344
690
|
CREATE INDEX IF NOT EXISTS idx_claim_evidence_claim ON claim_evidence(claim_id);
|
|
345
691
|
CREATE INDEX IF NOT EXISTS idx_claim_evidence_type ON claim_evidence(evidence_type);
|
|
346
692
|
|
|
347
|
-
--
|
|
693
|
+
-- --------------------------------------------------------
|
|
694
|
+
-- claim_checks: 検証ログ (V12: created_at 追加)
|
|
695
|
+
-- --------------------------------------------------------
|
|
348
696
|
CREATE TABLE IF NOT EXISTS claim_checks (
|
|
349
697
|
id TEXT PRIMARY KEY,
|
|
350
698
|
claim_id TEXT NOT NULL REFERENCES claims(id),
|
|
@@ -354,41 +702,23 @@ function applyV2(db) {
|
|
|
354
702
|
result TEXT NOT NULL CHECK(result IN ('passed','failed','inconclusive','skipped')),
|
|
355
703
|
details TEXT,
|
|
356
704
|
source_tool TEXT,
|
|
357
|
-
checked_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
705
|
+
checked_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
706
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
358
707
|
);
|
|
359
708
|
|
|
360
709
|
CREATE INDEX IF NOT EXISTS idx_claim_checks_claim ON claim_checks(claim_id);
|
|
361
710
|
CREATE INDEX IF NOT EXISTS idx_claim_checks_type ON claim_checks(check_type);
|
|
362
711
|
CREATE INDEX IF NOT EXISTS idx_claim_checks_result ON claim_checks(result);
|
|
363
712
|
|
|
364
|
-
--
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* V3スキーマ: Provenance自動付与対応
|
|
370
|
-
* - claims に client_name, client_version カラム追加
|
|
371
|
-
* - decisions に client_name, client_version カラム追加
|
|
372
|
-
* Codexレビュー指摘: source_tool/source_sessionが自己申告値で真正性が担保されない問題への対応
|
|
373
|
-
* サーバー側でMCP接続元クライアント情報を自動付与する仕組み
|
|
374
|
-
*/
|
|
375
|
-
function applyV3(db) {
|
|
376
|
-
db.exec(`
|
|
377
|
-
-- claims にProvenance(来歴)カラム追加
|
|
378
|
-
ALTER TABLE claims ADD COLUMN client_name TEXT;
|
|
379
|
-
ALTER TABLE claims ADD COLUMN client_version TEXT;
|
|
380
|
-
|
|
381
|
-
-- decisions にProvenance(来歴)カラム追加
|
|
382
|
-
ALTER TABLE decisions ADD COLUMN client_name TEXT;
|
|
383
|
-
ALTER TABLE decisions ADD COLUMN client_version TEXT;
|
|
384
|
-
|
|
385
|
-
-- audit_log: 全操作の監査ログ(誰が・何を・なぜ)
|
|
713
|
+
-- --------------------------------------------------------
|
|
714
|
+
-- audit_log: 監査ログ
|
|
715
|
+
-- --------------------------------------------------------
|
|
386
716
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
387
717
|
id TEXT PRIMARY KEY,
|
|
388
718
|
operation TEXT NOT NULL CHECK(operation IN (
|
|
389
|
-
'create','update','retract','supersede','reverse','obsolete'
|
|
719
|
+
'create','update','retract','supersede','reverse','obsolete','archive','promote'
|
|
390
720
|
)),
|
|
391
|
-
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision')),
|
|
721
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode','theory','insight','model','user_memo','user_plan','user_issue','user_topic','user_file')),
|
|
392
722
|
entity_id TEXT NOT NULL,
|
|
393
723
|
summary TEXT NOT NULL,
|
|
394
724
|
details TEXT,
|
|
@@ -396,7 +726,8 @@ function applyV3(db) {
|
|
|
396
726
|
client_version TEXT,
|
|
397
727
|
session_id TEXT,
|
|
398
728
|
source_tool TEXT,
|
|
399
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
729
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
730
|
+
device_id TEXT
|
|
400
731
|
);
|
|
401
732
|
|
|
402
733
|
CREATE INDEX IF NOT EXISTS idx_audit_log_entity ON audit_log(entity_type, entity_id);
|
|
@@ -404,54 +735,752 @@ function applyV3(db) {
|
|
|
404
735
|
CREATE INDEX IF NOT EXISTS idx_audit_log_created ON audit_log(created_at);
|
|
405
736
|
CREATE INDEX IF NOT EXISTS idx_audit_log_session ON audit_log(session_id);
|
|
406
737
|
|
|
407
|
-
--
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
-- SQLiteはALTER TABLE DROP CONSTRAINTをサポートしないため、テーブル再作成で対応
|
|
421
|
-
CREATE TABLE IF NOT EXISTS audit_log_new (
|
|
738
|
+
-- --------------------------------------------------------
|
|
739
|
+
-- store_meta: Key-Valueストア
|
|
740
|
+
-- --------------------------------------------------------
|
|
741
|
+
CREATE TABLE IF NOT EXISTS store_meta (
|
|
742
|
+
key TEXT PRIMARY KEY,
|
|
743
|
+
value TEXT NOT NULL,
|
|
744
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
745
|
+
);
|
|
746
|
+
|
|
747
|
+
-- --------------------------------------------------------
|
|
748
|
+
-- tombstones: 削除の同期伝播
|
|
749
|
+
-- --------------------------------------------------------
|
|
750
|
+
CREATE TABLE IF NOT EXISTS tombstones (
|
|
422
751
|
id TEXT PRIMARY KEY,
|
|
423
|
-
|
|
424
|
-
'create','update','retract','supersede','reverse','obsolete','archive'
|
|
425
|
-
)),
|
|
426
|
-
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode')),
|
|
752
|
+
entity_type TEXT NOT NULL,
|
|
427
753
|
entity_id TEXT NOT NULL,
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
client_name TEXT,
|
|
431
|
-
client_version TEXT,
|
|
432
|
-
session_id TEXT,
|
|
433
|
-
source_tool TEXT,
|
|
434
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
754
|
+
deleted_at TEXT NOT NULL,
|
|
755
|
+
UNIQUE(entity_type, entity_id)
|
|
435
756
|
);
|
|
436
757
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
758
|
+
-- --------------------------------------------------------
|
|
759
|
+
-- unified_search_items: 横断検索テーブル
|
|
760
|
+
-- --------------------------------------------------------
|
|
761
|
+
CREATE TABLE IF NOT EXISTS unified_search_items (
|
|
762
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','episode','decision','theory','insight','model','user_memo','user_plan','user_issue','user_topic','user_file')),
|
|
763
|
+
entity_id TEXT NOT NULL,
|
|
764
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
765
|
+
category TEXT,
|
|
766
|
+
title_summary TEXT NOT NULL,
|
|
767
|
+
search_text TEXT NOT NULL,
|
|
768
|
+
search_summary TEXT,
|
|
769
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
770
|
+
l1_embedding BLOB,
|
|
771
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
772
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
773
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
774
|
+
);
|
|
445
775
|
|
|
446
|
-
|
|
447
|
-
CREATE
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
776
|
+
CREATE INDEX IF NOT EXISTS idx_unified_search_scope ON unified_search_items(scope);
|
|
777
|
+
CREATE INDEX IF NOT EXISTS idx_unified_search_entity_type ON unified_search_items(entity_type);
|
|
778
|
+
CREATE INDEX IF NOT EXISTS idx_unified_search_updated ON unified_search_items(updated_at);
|
|
779
|
+
CREATE INDEX IF NOT EXISTS idx_unified_search_entity_id ON unified_search_items(entity_id);
|
|
780
|
+
`);
|
|
781
|
+
// ================================================================
|
|
782
|
+
// 2. FTS テーブル作成
|
|
783
|
+
// ================================================================
|
|
784
|
+
// --- claims_fts (V28 形式: l2_subject, l2_predicate, l2_object, l2_evidence) ---
|
|
785
|
+
db.exec(`
|
|
786
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS claims_fts USING fts5(
|
|
787
|
+
l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary,
|
|
788
|
+
content='claims', content_rowid='rowid', tokenize='trigram'
|
|
789
|
+
)
|
|
790
|
+
`);
|
|
791
|
+
// --- episodes_fts (V20 形式) ---
|
|
792
|
+
db.exec(`
|
|
793
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS episodes_fts USING fts5(
|
|
794
|
+
title, l1_content, l2_context, l2_trigger, l2_principles, l2_problems, l2_desires, l2_decisions, l2_outcomes, search_summary, user_input,
|
|
795
|
+
content='episodes', content_rowid='rowid', tokenize='trigram'
|
|
796
|
+
)
|
|
797
|
+
`);
|
|
798
|
+
// --- decisions_fts (V20 形式) ---
|
|
799
|
+
db.exec(`
|
|
800
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS decisions_fts USING fts5(
|
|
801
|
+
title, description, l1_content, l2_reasoning, search_summary, user_input,
|
|
802
|
+
content='decisions', content_rowid='rowid', tokenize='trigram'
|
|
803
|
+
)
|
|
804
|
+
`);
|
|
805
|
+
// --- theories_fts (V20 形式) ---
|
|
806
|
+
db.exec(`
|
|
807
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS theories_fts USING fts5(
|
|
808
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
809
|
+
content='theories', content_rowid='rowid', tokenize='trigram'
|
|
810
|
+
)
|
|
811
|
+
`);
|
|
812
|
+
// --- models_fts (同上) ---
|
|
813
|
+
db.exec(`
|
|
814
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS models_fts USING fts5(
|
|
815
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
816
|
+
content='models', content_rowid='rowid', tokenize='trigram'
|
|
817
|
+
)
|
|
818
|
+
`);
|
|
819
|
+
// --- insights_fts (V19 形式) ---
|
|
820
|
+
db.exec(`
|
|
821
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS insights_fts USING fts5(
|
|
822
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
823
|
+
content='insights', content_rowid='rowid', tokenize='trigram'
|
|
824
|
+
)
|
|
825
|
+
`);
|
|
826
|
+
// --- user_memos_fts (V24 形式: l1_content, user_input) ---
|
|
827
|
+
db.exec(`
|
|
828
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS user_memos_fts USING fts5(
|
|
829
|
+
title, l1_content, tags, search_summary, user_input,
|
|
830
|
+
content='user_memos', content_rowid='rowid', tokenize='trigram'
|
|
831
|
+
)
|
|
832
|
+
`);
|
|
833
|
+
// --- user_plans_fts (V24 形式: l1_content) ---
|
|
834
|
+
db.exec(`
|
|
835
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS user_plans_fts USING fts5(
|
|
836
|
+
title, l1_content, tags, search_summary,
|
|
837
|
+
content='user_plans', content_rowid='rowid', tokenize='trigram'
|
|
838
|
+
)
|
|
839
|
+
`);
|
|
840
|
+
// --- user_issues_fts (V24 形式: l1_content) ---
|
|
841
|
+
db.exec(`
|
|
842
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS user_issues_fts USING fts5(
|
|
843
|
+
title, l1_content, entries, tags, search_summary,
|
|
844
|
+
content='user_issues', content_rowid='rowid', tokenize='trigram'
|
|
845
|
+
)
|
|
846
|
+
`);
|
|
847
|
+
// --- user_topics_fts (V29 形式) ---
|
|
848
|
+
db.exec(`
|
|
849
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS user_topics_fts USING fts5(
|
|
850
|
+
title, summary, tags, search_summary,
|
|
851
|
+
content='user_topics', content_rowid='rowid', tokenize='trigram'
|
|
852
|
+
)
|
|
853
|
+
`);
|
|
854
|
+
// --- unified_search_fts (V29 形式) ---
|
|
855
|
+
db.exec(`
|
|
856
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS unified_search_fts USING fts5(
|
|
857
|
+
search_text, title_summary, search_summary,
|
|
858
|
+
content='unified_search_items', content_rowid='rowid', tokenize='trigram'
|
|
859
|
+
)
|
|
860
|
+
`);
|
|
861
|
+
// ================================================================
|
|
862
|
+
// 3. FTS 同期トリガー作成
|
|
863
|
+
// ================================================================
|
|
864
|
+
// --- claims_fts triggers (V28 形式) ---
|
|
865
|
+
db.exec(`
|
|
866
|
+
CREATE TRIGGER claims_fts_insert AFTER INSERT ON claims BEGIN
|
|
867
|
+
INSERT INTO claims_fts(rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
868
|
+
VALUES (new.rowid, new.l2_subject, new.l2_predicate, new.l2_object, new.l2_evidence, new.l1_content, new.search_summary);
|
|
869
|
+
END
|
|
870
|
+
`);
|
|
871
|
+
db.exec(`
|
|
872
|
+
CREATE TRIGGER claims_fts_update AFTER UPDATE ON claims BEGIN
|
|
873
|
+
INSERT INTO claims_fts(claims_fts, rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
874
|
+
VALUES ('delete', old.rowid, old.l2_subject, old.l2_predicate, old.l2_object, old.l2_evidence, old.l1_content, old.search_summary);
|
|
875
|
+
INSERT INTO claims_fts(rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
876
|
+
VALUES (new.rowid, new.l2_subject, new.l2_predicate, new.l2_object, new.l2_evidence, new.l1_content, new.search_summary);
|
|
877
|
+
END
|
|
878
|
+
`);
|
|
879
|
+
db.exec(`
|
|
880
|
+
CREATE TRIGGER claims_fts_delete AFTER DELETE ON claims BEGIN
|
|
881
|
+
INSERT INTO claims_fts(claims_fts, rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
882
|
+
VALUES ('delete', old.rowid, old.l2_subject, old.l2_predicate, old.l2_object, old.l2_evidence, old.l1_content, old.search_summary);
|
|
883
|
+
END
|
|
884
|
+
`);
|
|
885
|
+
// --- episodes_fts triggers ---
|
|
886
|
+
db.exec(`
|
|
887
|
+
CREATE TRIGGER episodes_fts_insert AFTER INSERT ON episodes BEGIN
|
|
888
|
+
INSERT INTO episodes_fts(rowid, title, l1_content, l2_context, l2_trigger, l2_principles, l2_problems, l2_desires, l2_decisions, l2_outcomes, search_summary, user_input)
|
|
889
|
+
VALUES (new.rowid, new.title, new.l1_content, new.l2_context, new.l2_trigger,
|
|
890
|
+
${jc("new.l2_principles")}, ${jc("new.l2_problems")}, ${jc("new.l2_desires")},
|
|
891
|
+
${jc("new.l2_decisions")}, ${jc("new.l2_outcomes")},
|
|
892
|
+
new.search_summary, new.user_input);
|
|
893
|
+
END
|
|
894
|
+
`);
|
|
895
|
+
db.exec(`
|
|
896
|
+
CREATE TRIGGER episodes_fts_update AFTER UPDATE ON episodes BEGIN
|
|
897
|
+
INSERT INTO episodes_fts(episodes_fts, rowid, title, l1_content, l2_context, l2_trigger, l2_principles, l2_problems, l2_desires, l2_decisions, l2_outcomes, search_summary, user_input)
|
|
898
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, old.l2_context, old.l2_trigger,
|
|
899
|
+
${jc("old.l2_principles")}, ${jc("old.l2_problems")}, ${jc("old.l2_desires")},
|
|
900
|
+
${jc("old.l2_decisions")}, ${jc("old.l2_outcomes")},
|
|
901
|
+
old.search_summary, old.user_input);
|
|
902
|
+
INSERT INTO episodes_fts(rowid, title, l1_content, l2_context, l2_trigger, l2_principles, l2_problems, l2_desires, l2_decisions, l2_outcomes, search_summary, user_input)
|
|
903
|
+
VALUES (new.rowid, new.title, new.l1_content, new.l2_context, new.l2_trigger,
|
|
904
|
+
${jc("new.l2_principles")}, ${jc("new.l2_problems")}, ${jc("new.l2_desires")},
|
|
905
|
+
${jc("new.l2_decisions")}, ${jc("new.l2_outcomes")},
|
|
906
|
+
new.search_summary, new.user_input);
|
|
907
|
+
END
|
|
908
|
+
`);
|
|
909
|
+
db.exec(`
|
|
910
|
+
CREATE TRIGGER episodes_fts_delete AFTER DELETE ON episodes BEGIN
|
|
911
|
+
INSERT INTO episodes_fts(episodes_fts, rowid, title, l1_content, l2_context, l2_trigger, l2_principles, l2_problems, l2_desires, l2_decisions, l2_outcomes, search_summary, user_input)
|
|
912
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, old.l2_context, old.l2_trigger,
|
|
913
|
+
${jc("old.l2_principles")}, ${jc("old.l2_problems")}, ${jc("old.l2_desires")},
|
|
914
|
+
${jc("old.l2_decisions")}, ${jc("old.l2_outcomes")},
|
|
915
|
+
old.search_summary, old.user_input);
|
|
916
|
+
END
|
|
917
|
+
`);
|
|
918
|
+
// --- decisions_fts triggers ---
|
|
919
|
+
db.exec(`
|
|
920
|
+
CREATE TRIGGER decisions_fts_insert AFTER INSERT ON decisions BEGIN
|
|
921
|
+
INSERT INTO decisions_fts(rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
922
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_reasoning, new.search_summary, new.user_input);
|
|
923
|
+
END
|
|
924
|
+
`);
|
|
925
|
+
db.exec(`
|
|
926
|
+
CREATE TRIGGER decisions_fts_update AFTER UPDATE ON decisions BEGIN
|
|
927
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
928
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_reasoning, old.search_summary, old.user_input);
|
|
929
|
+
INSERT INTO decisions_fts(rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
930
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_reasoning, new.search_summary, new.user_input);
|
|
931
|
+
END
|
|
932
|
+
`);
|
|
933
|
+
db.exec(`
|
|
934
|
+
CREATE TRIGGER decisions_fts_delete AFTER DELETE ON decisions BEGIN
|
|
935
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
936
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_reasoning, old.search_summary, old.user_input);
|
|
937
|
+
END
|
|
938
|
+
`);
|
|
939
|
+
// --- theories_fts triggers ---
|
|
940
|
+
db.exec(`
|
|
941
|
+
CREATE TRIGGER theories_fts_insert AFTER INSERT ON theories BEGIN
|
|
942
|
+
INSERT INTO theories_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
943
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
944
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
945
|
+
END
|
|
946
|
+
`);
|
|
947
|
+
db.exec(`
|
|
948
|
+
CREATE TRIGGER theories_fts_update AFTER UPDATE ON theories BEGIN
|
|
949
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
950
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
951
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
952
|
+
INSERT INTO theories_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
953
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
954
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
955
|
+
END
|
|
956
|
+
`);
|
|
957
|
+
db.exec(`
|
|
958
|
+
CREATE TRIGGER theories_fts_delete AFTER DELETE ON theories BEGIN
|
|
959
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
960
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
961
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
962
|
+
END
|
|
963
|
+
`);
|
|
964
|
+
// --- models_fts triggers ---
|
|
965
|
+
db.exec(`
|
|
966
|
+
CREATE TRIGGER models_fts_insert AFTER INSERT ON models BEGIN
|
|
967
|
+
INSERT INTO models_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
968
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
969
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
970
|
+
END
|
|
971
|
+
`);
|
|
972
|
+
db.exec(`
|
|
973
|
+
CREATE TRIGGER models_fts_update AFTER UPDATE ON models BEGIN
|
|
974
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
975
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
976
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
977
|
+
INSERT INTO models_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
978
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
979
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
980
|
+
END
|
|
981
|
+
`);
|
|
982
|
+
db.exec(`
|
|
983
|
+
CREATE TRIGGER models_fts_delete AFTER DELETE ON models BEGIN
|
|
984
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
985
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
986
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
987
|
+
END
|
|
988
|
+
`);
|
|
989
|
+
// --- insights_fts triggers ---
|
|
990
|
+
db.exec(`
|
|
991
|
+
CREATE TRIGGER insights_fts_insert AFTER INSERT ON insights BEGIN
|
|
992
|
+
INSERT INTO insights_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
993
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
994
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
995
|
+
END
|
|
996
|
+
`);
|
|
997
|
+
db.exec(`
|
|
998
|
+
CREATE TRIGGER insights_fts_update AFTER UPDATE ON insights BEGIN
|
|
999
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
1000
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
1001
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
1002
|
+
INSERT INTO insights_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
1003
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
1004
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
1005
|
+
END
|
|
1006
|
+
`);
|
|
1007
|
+
db.exec(`
|
|
1008
|
+
CREATE TRIGGER insights_fts_delete AFTER DELETE ON insights BEGIN
|
|
1009
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
1010
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
1011
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
1012
|
+
END
|
|
1013
|
+
`);
|
|
1014
|
+
// --- user_memos_fts triggers (V24 形式: l1_content, user_input) ---
|
|
1015
|
+
db.exec(`
|
|
1016
|
+
CREATE TRIGGER user_memos_fts_insert AFTER INSERT ON user_memos BEGIN
|
|
1017
|
+
INSERT INTO user_memos_fts(rowid, title, l1_content, tags, search_summary, user_input)
|
|
1018
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.tags")}, new.search_summary, new.user_input);
|
|
1019
|
+
END
|
|
1020
|
+
`);
|
|
1021
|
+
db.exec(`
|
|
1022
|
+
CREATE TRIGGER user_memos_fts_update AFTER UPDATE ON user_memos BEGIN
|
|
1023
|
+
INSERT INTO user_memos_fts(user_memos_fts, rowid, title, l1_content, tags, search_summary, user_input)
|
|
1024
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.tags")}, old.search_summary, old.user_input);
|
|
1025
|
+
INSERT INTO user_memos_fts(rowid, title, l1_content, tags, search_summary, user_input)
|
|
1026
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.tags")}, new.search_summary, new.user_input);
|
|
1027
|
+
END
|
|
1028
|
+
`);
|
|
1029
|
+
db.exec(`
|
|
1030
|
+
CREATE TRIGGER user_memos_fts_delete AFTER DELETE ON user_memos BEGIN
|
|
1031
|
+
INSERT INTO user_memos_fts(user_memos_fts, rowid, title, l1_content, tags, search_summary, user_input)
|
|
1032
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.tags")}, old.search_summary, old.user_input);
|
|
1033
|
+
END
|
|
1034
|
+
`);
|
|
1035
|
+
// --- user_plans_fts triggers (V24 形式: l1_content) ---
|
|
1036
|
+
db.exec(`
|
|
1037
|
+
CREATE TRIGGER user_plans_fts_insert AFTER INSERT ON user_plans BEGIN
|
|
1038
|
+
INSERT INTO user_plans_fts(rowid, title, l1_content, tags, search_summary)
|
|
1039
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.tags")}, new.search_summary);
|
|
1040
|
+
END
|
|
1041
|
+
`);
|
|
1042
|
+
db.exec(`
|
|
1043
|
+
CREATE TRIGGER user_plans_fts_update AFTER UPDATE ON user_plans BEGIN
|
|
1044
|
+
INSERT INTO user_plans_fts(user_plans_fts, rowid, title, l1_content, tags, search_summary)
|
|
1045
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.tags")}, old.search_summary);
|
|
1046
|
+
INSERT INTO user_plans_fts(rowid, title, l1_content, tags, search_summary)
|
|
1047
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.tags")}, new.search_summary);
|
|
1048
|
+
END
|
|
1049
|
+
`);
|
|
1050
|
+
db.exec(`
|
|
1051
|
+
CREATE TRIGGER user_plans_fts_delete AFTER DELETE ON user_plans BEGIN
|
|
1052
|
+
INSERT INTO user_plans_fts(user_plans_fts, rowid, title, l1_content, tags, search_summary)
|
|
1053
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.tags")}, old.search_summary);
|
|
1054
|
+
END
|
|
1055
|
+
`);
|
|
1056
|
+
// --- user_issues_fts triggers (V24 形式: l1_content) ---
|
|
1057
|
+
db.exec(`
|
|
1058
|
+
CREATE TRIGGER user_issues_fts_insert AFTER INSERT ON user_issues BEGIN
|
|
1059
|
+
INSERT INTO user_issues_fts(rowid, title, l1_content, entries, tags, search_summary)
|
|
1060
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.entries")}, ${jc("new.tags")}, new.search_summary);
|
|
1061
|
+
END
|
|
1062
|
+
`);
|
|
1063
|
+
db.exec(`
|
|
1064
|
+
CREATE TRIGGER user_issues_fts_update AFTER UPDATE ON user_issues BEGIN
|
|
1065
|
+
INSERT INTO user_issues_fts(user_issues_fts, rowid, title, l1_content, entries, tags, search_summary)
|
|
1066
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.entries")}, ${jc("old.tags")}, old.search_summary);
|
|
1067
|
+
INSERT INTO user_issues_fts(rowid, title, l1_content, entries, tags, search_summary)
|
|
1068
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.entries")}, ${jc("new.tags")}, new.search_summary);
|
|
1069
|
+
END
|
|
1070
|
+
`);
|
|
1071
|
+
db.exec(`
|
|
1072
|
+
CREATE TRIGGER user_issues_fts_delete AFTER DELETE ON user_issues BEGIN
|
|
1073
|
+
INSERT INTO user_issues_fts(user_issues_fts, rowid, title, l1_content, entries, tags, search_summary)
|
|
1074
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.entries")}, ${jc("old.tags")}, old.search_summary);
|
|
1075
|
+
END
|
|
1076
|
+
`);
|
|
1077
|
+
// --- user_topics_fts triggers (V29 形式: REPLACE パターン) ---
|
|
1078
|
+
db.exec(`
|
|
1079
|
+
CREATE TRIGGER user_topics_fts_insert AFTER INSERT ON user_topics BEGIN
|
|
1080
|
+
INSERT INTO user_topics_fts(rowid, title, summary, tags, search_summary)
|
|
1081
|
+
VALUES (new.rowid, new.title, new.summary, ${jcTopics("new.tags")}, new.search_summary);
|
|
1082
|
+
END
|
|
1083
|
+
`);
|
|
1084
|
+
db.exec(`
|
|
1085
|
+
CREATE TRIGGER user_topics_fts_update AFTER UPDATE ON user_topics BEGIN
|
|
1086
|
+
INSERT INTO user_topics_fts(user_topics_fts, rowid, title, summary, tags, search_summary)
|
|
1087
|
+
VALUES ('delete', old.rowid, old.title, old.summary, ${jcTopics("old.tags")}, old.search_summary);
|
|
1088
|
+
INSERT INTO user_topics_fts(rowid, title, summary, tags, search_summary)
|
|
1089
|
+
VALUES (new.rowid, new.title, new.summary, ${jcTopics("new.tags")}, new.search_summary);
|
|
1090
|
+
END
|
|
1091
|
+
`);
|
|
1092
|
+
db.exec(`
|
|
1093
|
+
CREATE TRIGGER user_topics_fts_delete AFTER DELETE ON user_topics BEGIN
|
|
1094
|
+
INSERT INTO user_topics_fts(user_topics_fts, rowid, title, summary, tags, search_summary)
|
|
1095
|
+
VALUES ('delete', old.rowid, old.title, old.summary, ${jcTopics("old.tags")}, old.search_summary);
|
|
1096
|
+
END
|
|
1097
|
+
`);
|
|
1098
|
+
// --- unified_search_fts triggers ---
|
|
1099
|
+
db.exec(`
|
|
1100
|
+
CREATE TRIGGER unified_search_fts_insert AFTER INSERT ON unified_search_items BEGIN
|
|
1101
|
+
INSERT INTO unified_search_fts(rowid, search_text, title_summary, search_summary)
|
|
1102
|
+
VALUES (new.rowid, new.search_text, new.title_summary, new.search_summary);
|
|
1103
|
+
END
|
|
1104
|
+
`);
|
|
1105
|
+
db.exec(`
|
|
1106
|
+
CREATE TRIGGER unified_search_fts_update AFTER UPDATE ON unified_search_items BEGIN
|
|
1107
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, search_text, title_summary, search_summary)
|
|
1108
|
+
VALUES ('delete', old.rowid, old.search_text, old.title_summary, old.search_summary);
|
|
1109
|
+
INSERT INTO unified_search_fts(rowid, search_text, title_summary, search_summary)
|
|
1110
|
+
VALUES (new.rowid, new.search_text, new.title_summary, new.search_summary);
|
|
1111
|
+
END
|
|
1112
|
+
`);
|
|
1113
|
+
db.exec(`
|
|
1114
|
+
CREATE TRIGGER unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
1115
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, search_text, title_summary, search_summary)
|
|
1116
|
+
VALUES ('delete', old.rowid, old.search_text, old.title_summary, old.search_summary);
|
|
1117
|
+
END
|
|
1118
|
+
`);
|
|
1119
|
+
// ================================================================
|
|
1120
|
+
// 4. unified_search_items の source table sync triggers (V29 形式)
|
|
1121
|
+
// ================================================================
|
|
1122
|
+
// --- claims (V28 形式: l2_evidence/l2_falsifier) ---
|
|
1123
|
+
const claimTitle = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object`;
|
|
1124
|
+
const claimSearch = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object || ' ' || ${coal(`${p}l2_evidence`)} || ' ' || ${coal(`${p}l2_falsifier`)} || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
1125
|
+
db.exec(`
|
|
1126
|
+
CREATE TRIGGER claims_unified_insert AFTER INSERT ON claims WHEN new.status = 'active' BEGIN
|
|
1127
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1128
|
+
VALUES ('claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at);
|
|
1129
|
+
END
|
|
1130
|
+
`);
|
|
1131
|
+
db.exec(`
|
|
1132
|
+
CREATE TRIGGER claims_unified_update AFTER UPDATE ON claims BEGIN
|
|
1133
|
+
DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id;
|
|
1134
|
+
INSERT INTO unified_search_items(${columns})
|
|
1135
|
+
SELECT 'claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at WHERE new.status = 'active';
|
|
1136
|
+
END
|
|
1137
|
+
`);
|
|
1138
|
+
db.exec(`
|
|
1139
|
+
CREATE TRIGGER claims_unified_delete AFTER DELETE ON claims BEGIN
|
|
1140
|
+
DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id;
|
|
1141
|
+
END
|
|
1142
|
+
`);
|
|
1143
|
+
// --- episodes ---
|
|
1144
|
+
db.exec(`
|
|
1145
|
+
CREATE TRIGGER episodes_unified_insert AFTER INSERT ON episodes WHEN new.status = 'active' BEGIN
|
|
1146
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1147
|
+
VALUES ('episode', new.id, new.scope, NULL, new.title, new.title || ' ' || ${coal("new.l1_content")} || ' ' || ${coal("new.l2_context")} || ' ' || ${coal("new.l2_trigger")} || ' ' || ${jc("new.l2_problems")} || ' ' || ${jc("new.l2_outcomes")} || ' ' || ${jc("new.l2_principles")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
1148
|
+
END
|
|
1149
|
+
`);
|
|
1150
|
+
db.exec(`
|
|
1151
|
+
CREATE TRIGGER episodes_unified_update AFTER UPDATE ON episodes BEGIN
|
|
1152
|
+
DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id;
|
|
1153
|
+
INSERT INTO unified_search_items(${columns})
|
|
1154
|
+
SELECT 'episode', new.id, new.scope, NULL, new.title, new.title || ' ' || ${coal("new.l1_content")} || ' ' || ${coal("new.l2_context")} || ' ' || ${coal("new.l2_trigger")} || ' ' || ${jc("new.l2_problems")} || ' ' || ${jc("new.l2_outcomes")} || ' ' || ${jc("new.l2_principles")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active';
|
|
1155
|
+
END
|
|
1156
|
+
`);
|
|
1157
|
+
db.exec(`
|
|
1158
|
+
CREATE TRIGGER episodes_unified_delete AFTER DELETE ON episodes BEGIN
|
|
1159
|
+
DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id;
|
|
1160
|
+
END
|
|
1161
|
+
`);
|
|
1162
|
+
// --- decisions ---
|
|
1163
|
+
db.exec(`
|
|
1164
|
+
CREATE TRIGGER decisions_unified_insert AFTER INSERT ON decisions WHEN new.status = 'active' BEGIN
|
|
1165
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1166
|
+
VALUES ('decision', new.id, new.scope, NULL, new.title, new.title || ' ' || new.description || ' ' || ${coal("new.l1_content")} || ' ' || new.l2_reasoning || ' ' || ${jc("new.l2_alternatives")} || ' ' || ${coal("new.search_summary")}, new.search_summary, '[]', new.created_at, new.updated_at);
|
|
1167
|
+
END
|
|
1168
|
+
`);
|
|
1169
|
+
db.exec(`
|
|
1170
|
+
CREATE TRIGGER decisions_unified_update AFTER UPDATE ON decisions BEGIN
|
|
1171
|
+
DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id;
|
|
1172
|
+
INSERT INTO unified_search_items(${columns})
|
|
1173
|
+
SELECT 'decision', new.id, new.scope, NULL, new.title, new.title || ' ' || new.description || ' ' || ${coal("new.l1_content")} || ' ' || new.l2_reasoning || ' ' || ${jc("new.l2_alternatives")} || ' ' || ${coal("new.search_summary")}, new.search_summary, '[]', new.created_at, new.updated_at WHERE new.status = 'active';
|
|
1174
|
+
END
|
|
1175
|
+
`);
|
|
1176
|
+
db.exec(`
|
|
1177
|
+
CREATE TRIGGER decisions_unified_delete AFTER DELETE ON decisions BEGIN
|
|
1178
|
+
DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id;
|
|
1179
|
+
END
|
|
1180
|
+
`);
|
|
1181
|
+
// --- theories, insights, models: 共通パターン ---
|
|
1182
|
+
for (const e of [
|
|
1183
|
+
{ type: "theory", table: "theories" },
|
|
1184
|
+
{ type: "insight", table: "insights" },
|
|
1185
|
+
{ type: "model", table: "models" },
|
|
1186
|
+
]) {
|
|
1187
|
+
const searchExpr = (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}l2_core_thesis`)} || ' ' || ${jc(`${p}l2_principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
1188
|
+
db.exec(`
|
|
1189
|
+
CREATE TRIGGER ${e.table}_unified_insert AFTER INSERT ON ${e.table} WHEN new.status = 'active' BEGIN
|
|
1190
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1191
|
+
VALUES ('${e.type}', new.id, new.scope, NULL, new.title, ${searchExpr("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
1192
|
+
END
|
|
1193
|
+
`);
|
|
1194
|
+
db.exec(`
|
|
1195
|
+
CREATE TRIGGER ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN
|
|
1196
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
1197
|
+
INSERT INTO unified_search_items(${columns})
|
|
1198
|
+
SELECT '${e.type}', new.id, new.scope, NULL, new.title, ${searchExpr("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active';
|
|
1199
|
+
END
|
|
1200
|
+
`);
|
|
1201
|
+
db.exec(`
|
|
1202
|
+
CREATE TRIGGER ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN
|
|
1203
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
1204
|
+
END
|
|
1205
|
+
`);
|
|
1206
|
+
}
|
|
1207
|
+
// --- user_memos (V24 形式: l1_content, user_input) ---
|
|
1208
|
+
db.exec(`
|
|
1209
|
+
CREATE TRIGGER user_memos_unified_insert AFTER INSERT ON user_memos
|
|
1210
|
+
WHEN new.status = 'active' AND new.usage_policy != 'human_directed' BEGIN
|
|
1211
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1212
|
+
VALUES ('user_memo', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${coal("new.user_input")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
1213
|
+
END
|
|
1214
|
+
`);
|
|
1215
|
+
db.exec(`
|
|
1216
|
+
CREATE TRIGGER user_memos_unified_update AFTER UPDATE ON user_memos BEGIN
|
|
1217
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_memo' AND entity_id = old.id;
|
|
1218
|
+
INSERT INTO unified_search_items(${columns})
|
|
1219
|
+
SELECT 'user_memo', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${coal("new.user_input")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
1220
|
+
WHERE new.status = 'active' AND new.usage_policy != 'human_directed';
|
|
1221
|
+
END
|
|
1222
|
+
`);
|
|
1223
|
+
db.exec(`
|
|
1224
|
+
CREATE TRIGGER user_memos_unified_delete AFTER DELETE ON user_memos BEGIN
|
|
1225
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_memo' AND entity_id = old.id;
|
|
1226
|
+
END
|
|
1227
|
+
`);
|
|
1228
|
+
// --- user_plans (V24 形式: l1_content) ---
|
|
1229
|
+
db.exec(`
|
|
1230
|
+
CREATE TRIGGER user_plans_unified_insert AFTER INSERT ON user_plans
|
|
1231
|
+
WHEN new.status = 'active' AND new.usage_policy != 'human_directed' BEGIN
|
|
1232
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1233
|
+
VALUES ('user_plan', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
1234
|
+
END
|
|
1235
|
+
`);
|
|
1236
|
+
db.exec(`
|
|
1237
|
+
CREATE TRIGGER user_plans_unified_update AFTER UPDATE ON user_plans BEGIN
|
|
1238
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_plan' AND entity_id = old.id;
|
|
1239
|
+
INSERT INTO unified_search_items(${columns})
|
|
1240
|
+
SELECT 'user_plan', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
1241
|
+
WHERE new.status = 'active' AND new.usage_policy != 'human_directed';
|
|
1242
|
+
END
|
|
1243
|
+
`);
|
|
1244
|
+
db.exec(`
|
|
1245
|
+
CREATE TRIGGER user_plans_unified_delete AFTER DELETE ON user_plans BEGIN
|
|
1246
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_plan' AND entity_id = old.id;
|
|
1247
|
+
END
|
|
1248
|
+
`);
|
|
1249
|
+
// --- user_issues (V24 形式: l1_content, entries) ---
|
|
1250
|
+
db.exec(`
|
|
1251
|
+
CREATE TRIGGER user_issues_unified_insert AFTER INSERT ON user_issues WHEN new.status = 'open' BEGIN
|
|
1252
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1253
|
+
VALUES ('user_issue', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${jc("new.entries")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
1254
|
+
END
|
|
1255
|
+
`);
|
|
1256
|
+
db.exec(`
|
|
1257
|
+
CREATE TRIGGER user_issues_unified_update AFTER UPDATE ON user_issues BEGIN
|
|
1258
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_issue' AND entity_id = old.id;
|
|
1259
|
+
INSERT INTO unified_search_items(${columns})
|
|
1260
|
+
SELECT 'user_issue', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${jc("new.entries")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
1261
|
+
WHERE new.status = 'open';
|
|
1262
|
+
END
|
|
1263
|
+
`);
|
|
1264
|
+
db.exec(`
|
|
1265
|
+
CREATE TRIGGER user_issues_unified_delete AFTER DELETE ON user_issues BEGIN
|
|
1266
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_issue' AND entity_id = old.id;
|
|
1267
|
+
END
|
|
1268
|
+
`);
|
|
1269
|
+
// --- user_topics (V29 形式) ---
|
|
1270
|
+
db.exec(`
|
|
1271
|
+
CREATE TRIGGER user_topics_unified_insert AFTER INSERT ON user_topics WHEN new.status = 'active' BEGIN
|
|
1272
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1273
|
+
VALUES ('user_topic', new.id, new.scope, NULL, new.title, new.title || ' ' || new.summary || ' ' || ${jcTopics("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
1274
|
+
END
|
|
1275
|
+
`);
|
|
1276
|
+
db.exec(`
|
|
1277
|
+
CREATE TRIGGER user_topics_unified_update AFTER UPDATE ON user_topics BEGIN
|
|
1278
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_topic' AND entity_id = old.id;
|
|
1279
|
+
INSERT INTO unified_search_items(${columns})
|
|
1280
|
+
SELECT 'user_topic', new.id, new.scope, NULL, new.title, new.title || ' ' || new.summary || ' ' || ${jcTopics("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
1281
|
+
WHERE new.status = 'active';
|
|
1282
|
+
END
|
|
1283
|
+
`);
|
|
1284
|
+
db.exec(`
|
|
1285
|
+
CREATE TRIGGER user_topics_unified_delete AFTER DELETE ON user_topics BEGIN
|
|
1286
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_topic' AND entity_id = old.id;
|
|
1287
|
+
END
|
|
1288
|
+
`);
|
|
1289
|
+
// --- user_files (V31 File Vault) ---
|
|
1290
|
+
db.exec(`
|
|
1291
|
+
CREATE TRIGGER user_files_unified_insert AFTER INSERT ON user_files WHEN new.status = 'active' BEGIN
|
|
1292
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1293
|
+
VALUES ('user_file', new.id, new.scope, NULL, new.title, new.title || ' ' || COALESCE(new.description, '') || ' ' || new.original_filename || ' ' || ${jcTopics("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
1294
|
+
END
|
|
1295
|
+
`);
|
|
1296
|
+
db.exec(`
|
|
1297
|
+
CREATE TRIGGER user_files_unified_update AFTER UPDATE ON user_files BEGIN
|
|
1298
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_file' AND entity_id = old.id;
|
|
1299
|
+
INSERT INTO unified_search_items(${columns})
|
|
1300
|
+
SELECT 'user_file', new.id, new.scope, NULL, new.title, new.title || ' ' || COALESCE(new.description, '') || ' ' || new.original_filename || ' ' || ${jcTopics("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
1301
|
+
WHERE new.status = 'active';
|
|
1302
|
+
END
|
|
1303
|
+
`);
|
|
1304
|
+
db.exec(`
|
|
1305
|
+
CREATE TRIGGER user_files_unified_delete AFTER DELETE ON user_files BEGIN
|
|
1306
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_file' AND entity_id = old.id;
|
|
1307
|
+
END
|
|
1308
|
+
`);
|
|
1309
|
+
// ================================================================
|
|
1310
|
+
// 5. store_meta 初期値
|
|
1311
|
+
// ================================================================
|
|
1312
|
+
const deviceId = generateClientId();
|
|
1313
|
+
db.prepare("INSERT OR IGNORE INTO store_meta (key, value) VALUES ('device_id', ?)").run(deviceId);
|
|
1314
|
+
db.prepare("INSERT OR IGNORE INTO store_meta (key, value) VALUES ('last_synced_at', '')").run();
|
|
1315
|
+
// ================================================================
|
|
1316
|
+
// 6. schema_version 全バージョン記録(V2〜V31 をスキップさせる)
|
|
1317
|
+
// ================================================================
|
|
1318
|
+
db.exec(`
|
|
1319
|
+
INSERT INTO schema_version (version) VALUES
|
|
1320
|
+
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),
|
|
1321
|
+
(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),
|
|
1322
|
+
(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),
|
|
1323
|
+
(31);
|
|
1324
|
+
`);
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* V2スキーマ: L2 Core v0対応
|
|
1328
|
+
* - claims に falsifier カラム追加
|
|
1329
|
+
* - claim_relations テーブル追加(推論グラフ)
|
|
1330
|
+
* - claim_evidence テーブル追加(構造化根拠参照)
|
|
1331
|
+
* - claim_checks テーブル追加(検証ログ)
|
|
1332
|
+
* 仕様: docs/specs/l2-core-v0.md
|
|
1333
|
+
*/
|
|
1334
|
+
function applyV2(db) {
|
|
1335
|
+
db.exec(`
|
|
1336
|
+
-- claims に反証条件カラム追加
|
|
1337
|
+
ALTER TABLE claims ADD COLUMN falsifier TEXT;
|
|
1338
|
+
|
|
1339
|
+
-- claim_relations: Claim間の推論関係(正規化)
|
|
1340
|
+
CREATE TABLE IF NOT EXISTS claim_relations (
|
|
1341
|
+
id TEXT PRIMARY KEY,
|
|
1342
|
+
source_claim_id TEXT NOT NULL,
|
|
1343
|
+
target_claim_id TEXT NOT NULL,
|
|
1344
|
+
relation_type TEXT NOT NULL CHECK(relation_type IN (
|
|
1345
|
+
'supports','contradicts','derives','induces','analogizes','supersedes','depends_on'
|
|
1346
|
+
)),
|
|
1347
|
+
confidence REAL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
1348
|
+
reasoning TEXT,
|
|
1349
|
+
source_tool TEXT,
|
|
1350
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1351
|
+
);
|
|
1352
|
+
|
|
1353
|
+
CREATE INDEX IF NOT EXISTS idx_claim_relations_source ON claim_relations(source_claim_id);
|
|
1354
|
+
CREATE INDEX IF NOT EXISTS idx_claim_relations_target ON claim_relations(target_claim_id);
|
|
1355
|
+
CREATE INDEX IF NOT EXISTS idx_claim_relations_type ON claim_relations(relation_type);
|
|
1356
|
+
|
|
1357
|
+
-- claim_evidence: 構造化された根拠参照
|
|
1358
|
+
CREATE TABLE IF NOT EXISTS claim_evidence (
|
|
1359
|
+
id TEXT PRIMARY KEY,
|
|
1360
|
+
claim_id TEXT NOT NULL REFERENCES claims(id),
|
|
1361
|
+
evidence_type TEXT NOT NULL CHECK(evidence_type IN (
|
|
1362
|
+
'url','file','claim','decision','session_log','user_statement','external'
|
|
1363
|
+
)),
|
|
1364
|
+
uri TEXT,
|
|
1365
|
+
span TEXT,
|
|
1366
|
+
content_hash TEXT,
|
|
1367
|
+
description TEXT,
|
|
1368
|
+
verified_at TEXT,
|
|
1369
|
+
source_tool TEXT,
|
|
1370
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1371
|
+
);
|
|
1372
|
+
|
|
1373
|
+
CREATE INDEX IF NOT EXISTS idx_claim_evidence_claim ON claim_evidence(claim_id);
|
|
1374
|
+
CREATE INDEX IF NOT EXISTS idx_claim_evidence_type ON claim_evidence(evidence_type);
|
|
1375
|
+
|
|
1376
|
+
-- claim_checks: 検証ログ
|
|
1377
|
+
CREATE TABLE IF NOT EXISTS claim_checks (
|
|
1378
|
+
id TEXT PRIMARY KEY,
|
|
1379
|
+
claim_id TEXT NOT NULL REFERENCES claims(id),
|
|
1380
|
+
check_type TEXT NOT NULL CHECK(check_type IN (
|
|
1381
|
+
'fact_check','consistency','counter_example','source_verify','user_confirm','auto_expire','falsifier_eval'
|
|
1382
|
+
)),
|
|
1383
|
+
result TEXT NOT NULL CHECK(result IN ('passed','failed','inconclusive','skipped')),
|
|
1384
|
+
details TEXT,
|
|
1385
|
+
source_tool TEXT,
|
|
1386
|
+
checked_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1387
|
+
);
|
|
1388
|
+
|
|
1389
|
+
CREATE INDEX IF NOT EXISTS idx_claim_checks_claim ON claim_checks(claim_id);
|
|
1390
|
+
CREATE INDEX IF NOT EXISTS idx_claim_checks_type ON claim_checks(check_type);
|
|
1391
|
+
CREATE INDEX IF NOT EXISTS idx_claim_checks_result ON claim_checks(result);
|
|
1392
|
+
|
|
1393
|
+
-- スキーマバージョン記録
|
|
1394
|
+
INSERT INTO schema_version (version) VALUES (2);
|
|
1395
|
+
`);
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* V3スキーマ: Provenance自動付与対応
|
|
1399
|
+
* - claims に client_name, client_version カラム追加
|
|
1400
|
+
* - decisions に client_name, client_version カラム追加
|
|
1401
|
+
* Codexレビュー指摘: source_tool/source_sessionが自己申告値で真正性が担保されない問題への対応
|
|
1402
|
+
* サーバー側でMCP接続元クライアント情報を自動付与する仕組み
|
|
1403
|
+
*/
|
|
1404
|
+
function applyV3(db) {
|
|
1405
|
+
db.exec(`
|
|
1406
|
+
-- claims にProvenance(来歴)カラム追加
|
|
1407
|
+
ALTER TABLE claims ADD COLUMN client_name TEXT;
|
|
1408
|
+
ALTER TABLE claims ADD COLUMN client_version TEXT;
|
|
1409
|
+
|
|
1410
|
+
-- decisions にProvenance(来歴)カラム追加
|
|
1411
|
+
ALTER TABLE decisions ADD COLUMN client_name TEXT;
|
|
1412
|
+
ALTER TABLE decisions ADD COLUMN client_version TEXT;
|
|
1413
|
+
|
|
1414
|
+
-- audit_log: 全操作の監査ログ(誰が・何を・なぜ)
|
|
1415
|
+
CREATE TABLE IF NOT EXISTS audit_log (
|
|
1416
|
+
id TEXT PRIMARY KEY,
|
|
1417
|
+
operation TEXT NOT NULL CHECK(operation IN (
|
|
1418
|
+
'create','update','retract','supersede','reverse','obsolete'
|
|
1419
|
+
)),
|
|
1420
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision')),
|
|
1421
|
+
entity_id TEXT NOT NULL,
|
|
1422
|
+
summary TEXT NOT NULL,
|
|
1423
|
+
details TEXT,
|
|
1424
|
+
client_name TEXT,
|
|
1425
|
+
client_version TEXT,
|
|
1426
|
+
session_id TEXT,
|
|
1427
|
+
source_tool TEXT,
|
|
1428
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1429
|
+
);
|
|
1430
|
+
|
|
1431
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_entity ON audit_log(entity_type, entity_id);
|
|
1432
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_operation ON audit_log(operation);
|
|
1433
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_created ON audit_log(created_at);
|
|
1434
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_session ON audit_log(session_id);
|
|
1435
|
+
|
|
1436
|
+
-- スキーマバージョン記録
|
|
1437
|
+
INSERT INTO schema_version (version) VALUES (3);
|
|
1438
|
+
`);
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* V4スキーマ: Episodeモデル追加
|
|
1442
|
+
* ユーザーの思考パターン・意思決定フレームワークを記録する上位構造。
|
|
1443
|
+
* 原子的事実(Claims)ではなく、文脈→問題→欲求→決定→結果→原則 の流れを格納。
|
|
1444
|
+
* ChatGPT提案のEpisode構造をベースに、~20件の実データ収集でカラム妥当性を検証する。
|
|
1445
|
+
*/
|
|
1446
|
+
function applyV4(db) {
|
|
1447
|
+
db.exec(`
|
|
1448
|
+
-- audit_log のentity_type CHECK制約を拡張('episode'を追加)
|
|
1449
|
+
-- SQLiteはALTER TABLE DROP CONSTRAINTをサポートしないため、テーブル再作成で対応
|
|
1450
|
+
CREATE TABLE IF NOT EXISTS audit_log_new (
|
|
1451
|
+
id TEXT PRIMARY KEY,
|
|
1452
|
+
operation TEXT NOT NULL CHECK(operation IN (
|
|
1453
|
+
'create','update','retract','supersede','reverse','obsolete','archive'
|
|
1454
|
+
)),
|
|
1455
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode')),
|
|
1456
|
+
entity_id TEXT NOT NULL,
|
|
1457
|
+
summary TEXT NOT NULL,
|
|
1458
|
+
details TEXT,
|
|
1459
|
+
client_name TEXT,
|
|
1460
|
+
client_version TEXT,
|
|
1461
|
+
session_id TEXT,
|
|
1462
|
+
source_tool TEXT,
|
|
1463
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1464
|
+
);
|
|
1465
|
+
|
|
1466
|
+
INSERT INTO audit_log_new SELECT * FROM audit_log;
|
|
1467
|
+
DROP TABLE audit_log;
|
|
1468
|
+
ALTER TABLE audit_log_new RENAME TO audit_log;
|
|
1469
|
+
|
|
1470
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_entity ON audit_log(entity_type, entity_id);
|
|
1471
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_operation ON audit_log(operation);
|
|
1472
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_created ON audit_log(created_at);
|
|
1473
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_session ON audit_log(session_id);
|
|
1474
|
+
|
|
1475
|
+
-- episodes: 思考パターン・意思決定エピソード
|
|
1476
|
+
CREATE TABLE IF NOT EXISTS episodes (
|
|
1477
|
+
id TEXT PRIMARY KEY,
|
|
1478
|
+
title TEXT NOT NULL,
|
|
1479
|
+
context TEXT,
|
|
1480
|
+
trigger TEXT,
|
|
1481
|
+
problems TEXT NOT NULL DEFAULT '[]',
|
|
1482
|
+
desires TEXT NOT NULL DEFAULT '[]',
|
|
1483
|
+
decisions TEXT NOT NULL DEFAULT '[]',
|
|
455
1484
|
outcomes TEXT NOT NULL DEFAULT '[]',
|
|
456
1485
|
principles TEXT NOT NULL DEFAULT '[]',
|
|
457
1486
|
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
@@ -569,7 +1598,7 @@ function applyV5(db) {
|
|
|
569
1598
|
/**
|
|
570
1599
|
* V6スキーマ: store_meta テーブル追加(バトンリレー用)
|
|
571
1600
|
* デバイス間でDBを引き継ぐためのメタデータ管理。
|
|
572
|
-
*
|
|
1601
|
+
* device_id でデバイスを識別し、last_synced_at で最終同期日時を追跡する。
|
|
573
1602
|
*/
|
|
574
1603
|
function applyV6(db) {
|
|
575
1604
|
db.exec(`
|
|
@@ -583,9 +1612,9 @@ function applyV6(db) {
|
|
|
583
1612
|
-- スキーマバージョン記録
|
|
584
1613
|
INSERT INTO schema_version (version) VALUES (6);
|
|
585
1614
|
`);
|
|
586
|
-
//
|
|
587
|
-
const
|
|
588
|
-
db.prepare("INSERT OR IGNORE INTO store_meta (key, value) VALUES ('
|
|
1615
|
+
// device_id を初期生成(UUIDv4相当のランダムID)
|
|
1616
|
+
const deviceId = generateClientId();
|
|
1617
|
+
db.prepare("INSERT OR IGNORE INTO store_meta (key, value) VALUES ('device_id', ?)").run(deviceId);
|
|
589
1618
|
db.prepare("INSERT OR IGNORE INTO store_meta (key, value) VALUES ('last_synced_at', '')").run();
|
|
590
1619
|
}
|
|
591
1620
|
/**
|
|
@@ -4004,21 +5033,25 @@ function applyV20(db) {
|
|
|
4004
5033
|
}
|
|
4005
5034
|
}
|
|
4006
5035
|
/**
|
|
4007
|
-
* V21: audit_log に
|
|
5036
|
+
* V21: audit_log に device_id カラム追加 + 既存レコードの backfill
|
|
4008
5037
|
* どのデバイスで作られたレコードか追跡するため。
|
|
4009
5038
|
* sync で複数デバイスのデータが混在する前に backfill することで、
|
|
4010
5039
|
* 既存データにもデバイス情報を付与する。
|
|
4011
5040
|
*/
|
|
4012
5041
|
function applyV21(db) {
|
|
4013
|
-
// audit_log に
|
|
5042
|
+
// audit_log に device_id カラムを追加
|
|
5043
|
+
// 注: 既存DBとの互換性のためカラム名は client_id のまま ALTER する
|
|
5044
|
+
// V29 で audit_log 再作成時に device_id にリネームされる
|
|
4014
5045
|
if (!hasColumn(db, "audit_log", "client_id")) {
|
|
4015
5046
|
db.exec(`ALTER TABLE audit_log ADD COLUMN client_id TEXT`);
|
|
4016
5047
|
}
|
|
4017
|
-
// 既存レコードに現在のデバイスの
|
|
5048
|
+
// 既存レコードに現在のデバイスの device_id を backfill
|
|
4018
5049
|
// (まだマージされていない = すべて自分のデバイスで作ったレコード)
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
db.prepare("
|
|
5050
|
+
// 注: V6 が 'client_id' キーで保存した既存DBのためフォールバック読み取り
|
|
5051
|
+
const deviceIdRow = (db.prepare("SELECT value FROM store_meta WHERE key = 'device_id'").get() ??
|
|
5052
|
+
db.prepare("SELECT value FROM store_meta WHERE key = 'client_id'").get());
|
|
5053
|
+
if (deviceIdRow) {
|
|
5054
|
+
db.prepare("UPDATE audit_log SET client_id = ? WHERE client_id IS NULL").run(deviceIdRow.value);
|
|
4022
5055
|
}
|
|
4023
5056
|
db.exec(`INSERT INTO schema_version (version) VALUES (21)`);
|
|
4024
5057
|
}
|
|
@@ -5549,15 +6582,305 @@ function applyV27(db) {
|
|
|
5549
6582
|
|
|
5550
6583
|
INSERT INTO schema_version (version) VALUES (27);
|
|
5551
6584
|
`);
|
|
5552
|
-
// unified_search_items triggers for claims(新カラム名)
|
|
5553
|
-
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
6585
|
+
// unified_search_items triggers for claims(新カラム名)
|
|
6586
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
6587
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
6588
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
6589
|
+
const claimTitle = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object`;
|
|
6590
|
+
const claimSearch = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object || ' ' || ${coal(`${p}evidence`)} || ' ' || ${coal(`${p}falsifier`)} || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
6591
|
+
db.exec(`
|
|
6592
|
+
CREATE TRIGGER claims_unified_insert AFTER INSERT ON claims
|
|
6593
|
+
WHEN new.status = 'active' BEGIN
|
|
6594
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
6595
|
+
VALUES ('claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at);
|
|
6596
|
+
END
|
|
6597
|
+
`);
|
|
6598
|
+
db.exec(`
|
|
6599
|
+
CREATE TRIGGER claims_unified_update AFTER UPDATE ON claims BEGIN
|
|
6600
|
+
DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id;
|
|
6601
|
+
INSERT INTO unified_search_items(${columns})
|
|
6602
|
+
SELECT 'claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at
|
|
6603
|
+
WHERE new.status = 'active';
|
|
6604
|
+
END
|
|
6605
|
+
`);
|
|
6606
|
+
db.exec(`
|
|
6607
|
+
CREATE TRIGGER claims_unified_delete AFTER DELETE ON claims BEGIN
|
|
6608
|
+
DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id;
|
|
6609
|
+
END
|
|
6610
|
+
`);
|
|
6611
|
+
}
|
|
6612
|
+
/**
|
|
6613
|
+
* V28: evidence → l2_evidence, falsifier → l2_falsifier リネーム + claims_fts 再構築
|
|
6614
|
+
* 冪等: 既に l2_evidence が存在する場合はリネームをスキップ
|
|
6615
|
+
*/
|
|
6616
|
+
function applyV28(db) {
|
|
6617
|
+
// カラムリネーム(冪等: 既にリネーム済みならスキップ)
|
|
6618
|
+
if (hasColumn(db, "claims", "evidence") && !hasColumn(db, "claims", "l2_evidence")) {
|
|
6619
|
+
db.exec(`ALTER TABLE claims RENAME COLUMN evidence TO l2_evidence`);
|
|
6620
|
+
}
|
|
6621
|
+
if (hasColumn(db, "claims", "falsifier") && !hasColumn(db, "claims", "l2_falsifier")) {
|
|
6622
|
+
db.exec(`ALTER TABLE claims RENAME COLUMN falsifier TO l2_falsifier`);
|
|
6623
|
+
}
|
|
6624
|
+
// claims_fts を再構築(l2_evidence/l2_falsifier 対応)
|
|
6625
|
+
db.exec(`
|
|
6626
|
+
DROP TRIGGER IF EXISTS claims_fts_insert;
|
|
6627
|
+
DROP TRIGGER IF EXISTS claims_fts_update;
|
|
6628
|
+
DROP TRIGGER IF EXISTS claims_fts_delete;
|
|
6629
|
+
DROP TABLE IF EXISTS claims_fts;
|
|
6630
|
+
|
|
6631
|
+
CREATE VIRTUAL TABLE claims_fts USING fts5(
|
|
6632
|
+
l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary,
|
|
6633
|
+
content='claims', content_rowid='rowid', tokenize='trigram'
|
|
6634
|
+
);
|
|
6635
|
+
|
|
6636
|
+
-- FTS同期トリガー: INSERT
|
|
6637
|
+
CREATE TRIGGER claims_fts_insert AFTER INSERT ON claims BEGIN
|
|
6638
|
+
INSERT INTO claims_fts(rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
6639
|
+
VALUES (new.rowid, new.l2_subject, new.l2_predicate, new.l2_object, new.l2_evidence, new.l1_content, new.search_summary);
|
|
6640
|
+
END;
|
|
6641
|
+
|
|
6642
|
+
-- FTS同期トリガー: UPDATE
|
|
6643
|
+
CREATE TRIGGER claims_fts_update AFTER UPDATE ON claims BEGIN
|
|
6644
|
+
INSERT INTO claims_fts(claims_fts, rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
6645
|
+
VALUES ('delete', old.rowid, old.l2_subject, old.l2_predicate, old.l2_object, old.l2_evidence, old.l1_content, old.search_summary);
|
|
6646
|
+
INSERT INTO claims_fts(rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
6647
|
+
VALUES (new.rowid, new.l2_subject, new.l2_predicate, new.l2_object, new.l2_evidence, new.l1_content, new.search_summary);
|
|
6648
|
+
END;
|
|
6649
|
+
|
|
6650
|
+
-- FTS同期トリガー: DELETE
|
|
6651
|
+
CREATE TRIGGER claims_fts_delete AFTER DELETE ON claims BEGIN
|
|
6652
|
+
INSERT INTO claims_fts(claims_fts, rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
6653
|
+
VALUES ('delete', old.rowid, old.l2_subject, old.l2_predicate, old.l2_object, old.l2_evidence, old.l1_content, old.search_summary);
|
|
6654
|
+
END;
|
|
6655
|
+
|
|
6656
|
+
-- FTS rebuild
|
|
6657
|
+
INSERT INTO claims_fts(claims_fts) VALUES('rebuild');
|
|
6658
|
+
|
|
6659
|
+
INSERT INTO schema_version (version) VALUES (28);
|
|
6660
|
+
`);
|
|
6661
|
+
// unified_search_items の claims トリガー再作成(l2_evidence/l2_falsifier 対応)
|
|
6662
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
6663
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
6664
|
+
const claimTitle = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object`;
|
|
6665
|
+
const claimSearch = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object || ' ' || ${coal(`${p}l2_evidence`)} || ' ' || ${coal(`${p}l2_falsifier`)} || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
6666
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_unified_insert`);
|
|
6667
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_unified_update`);
|
|
6668
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_unified_delete`);
|
|
6669
|
+
db.exec(`
|
|
6670
|
+
CREATE TRIGGER claims_unified_insert AFTER INSERT ON claims
|
|
6671
|
+
WHEN new.status = 'active' BEGIN
|
|
6672
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
6673
|
+
VALUES ('claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at);
|
|
6674
|
+
END
|
|
6675
|
+
`);
|
|
6676
|
+
db.exec(`
|
|
6677
|
+
CREATE TRIGGER claims_unified_update AFTER UPDATE ON claims BEGIN
|
|
6678
|
+
DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id;
|
|
6679
|
+
INSERT INTO unified_search_items(${columns})
|
|
6680
|
+
SELECT 'claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at
|
|
6681
|
+
WHERE new.status = 'active';
|
|
6682
|
+
END
|
|
6683
|
+
`);
|
|
6684
|
+
db.exec(`
|
|
6685
|
+
CREATE TRIGGER claims_unified_delete AFTER DELETE ON claims BEGIN
|
|
6686
|
+
DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id;
|
|
6687
|
+
END
|
|
6688
|
+
`);
|
|
6689
|
+
}
|
|
6690
|
+
/**
|
|
6691
|
+
* V29: user_topics テーブル新設(話題管理)
|
|
6692
|
+
* セッション横断で議論トピックを追跡する
|
|
6693
|
+
*/
|
|
6694
|
+
function applyV29(db) {
|
|
6695
|
+
db.exec(`
|
|
6696
|
+
CREATE TABLE IF NOT EXISTS user_topics (
|
|
6697
|
+
id TEXT PRIMARY KEY,
|
|
6698
|
+
title TEXT NOT NULL,
|
|
6699
|
+
summary TEXT NOT NULL,
|
|
6700
|
+
device_id TEXT,
|
|
6701
|
+
cwd TEXT,
|
|
6702
|
+
conversation_id TEXT,
|
|
6703
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
6704
|
+
CHECK(priority IN ('low','medium','high','critical')),
|
|
6705
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
6706
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
6707
|
+
search_summary TEXT,
|
|
6708
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
6709
|
+
CHECK(status IN ('active','closed','archived')),
|
|
6710
|
+
l1_embedding BLOB,
|
|
6711
|
+
client_name TEXT,
|
|
6712
|
+
client_version TEXT,
|
|
6713
|
+
source_tool TEXT,
|
|
6714
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
6715
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
6716
|
+
)
|
|
6717
|
+
`);
|
|
6718
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_topics_status ON user_topics(status)`);
|
|
6719
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_topics_scope ON user_topics(scope)`);
|
|
6720
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_topics_priority ON user_topics(priority)`);
|
|
6721
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_topics_updated ON user_topics(updated_at)`);
|
|
6722
|
+
// FTS5
|
|
6723
|
+
db.exec(`
|
|
6724
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS user_topics_fts USING fts5(
|
|
6725
|
+
title, summary, tags, search_summary,
|
|
6726
|
+
content='user_topics', content_rowid='rowid', tokenize='trigram'
|
|
6727
|
+
)
|
|
6728
|
+
`);
|
|
6729
|
+
const jc = (col) => `REPLACE(REPLACE(${col}, '[', ''), ']', '')`;
|
|
6730
|
+
// 冪等性: 部分適用からの再実行に備え、既存トリガーを先に削除
|
|
6731
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_fts_insert`);
|
|
6732
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_fts_update`);
|
|
6733
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_fts_delete`);
|
|
6734
|
+
db.exec(`
|
|
6735
|
+
CREATE TRIGGER user_topics_fts_insert AFTER INSERT ON user_topics BEGIN
|
|
6736
|
+
INSERT INTO user_topics_fts(rowid, title, summary, tags, search_summary)
|
|
6737
|
+
VALUES (new.rowid, new.title, new.summary, ${jc("new.tags")}, new.search_summary);
|
|
6738
|
+
END
|
|
6739
|
+
`);
|
|
6740
|
+
db.exec(`
|
|
6741
|
+
CREATE TRIGGER user_topics_fts_update AFTER UPDATE ON user_topics BEGIN
|
|
6742
|
+
INSERT INTO user_topics_fts(user_topics_fts, rowid, title, summary, tags, search_summary)
|
|
6743
|
+
VALUES ('delete', old.rowid, old.title, old.summary, ${jc("old.tags")}, old.search_summary);
|
|
6744
|
+
INSERT INTO user_topics_fts(rowid, title, summary, tags, search_summary)
|
|
6745
|
+
VALUES (new.rowid, new.title, new.summary, ${jc("new.tags")}, new.search_summary);
|
|
6746
|
+
END
|
|
6747
|
+
`);
|
|
6748
|
+
db.exec(`
|
|
6749
|
+
CREATE TRIGGER user_topics_fts_delete AFTER DELETE ON user_topics BEGIN
|
|
6750
|
+
INSERT INTO user_topics_fts(user_topics_fts, rowid, title, summary, tags, search_summary)
|
|
6751
|
+
VALUES ('delete', old.rowid, old.title, old.summary, ${jc("old.tags")}, old.search_summary);
|
|
6752
|
+
END
|
|
6753
|
+
`);
|
|
6754
|
+
// FTS rebuild
|
|
6755
|
+
db.exec(`INSERT INTO user_topics_fts(user_topics_fts) VALUES('rebuild')`);
|
|
6756
|
+
// audit_log の CHECK 制約に user_topic を追加
|
|
6757
|
+
// カラム順序を元のテーブル(V18 + V21 ALTER ADD COLUMN)と一致させる
|
|
6758
|
+
// 冪等性: 前回の部分適用で残存した _new テーブルを掃除
|
|
6759
|
+
db.exec(`DROP TABLE IF EXISTS audit_log_new`);
|
|
6760
|
+
// 冪等性: 前回の部分適用で既に完了している場合はスキップ
|
|
6761
|
+
if (!hasColumn(db, "audit_log", "device_id")) {
|
|
6762
|
+
// client_id → device_id リネーム + CHECK 制約更新
|
|
6763
|
+
db.exec(`
|
|
6764
|
+
CREATE TABLE audit_log_new (
|
|
6765
|
+
id TEXT PRIMARY KEY,
|
|
6766
|
+
operation TEXT NOT NULL CHECK(operation IN (
|
|
6767
|
+
'create','update','retract','supersede','reverse','obsolete','archive','promote'
|
|
6768
|
+
)),
|
|
6769
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode','theory','insight','model','user_memo','user_plan','user_issue','user_topic')),
|
|
6770
|
+
entity_id TEXT NOT NULL,
|
|
6771
|
+
summary TEXT NOT NULL,
|
|
6772
|
+
details TEXT,
|
|
6773
|
+
client_name TEXT,
|
|
6774
|
+
client_version TEXT,
|
|
6775
|
+
session_id TEXT,
|
|
6776
|
+
source_tool TEXT,
|
|
6777
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
6778
|
+
device_id TEXT
|
|
6779
|
+
)
|
|
6780
|
+
`);
|
|
6781
|
+
db.exec(`
|
|
6782
|
+
INSERT INTO audit_log_new (id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool, created_at, device_id)
|
|
6783
|
+
SELECT id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool, created_at, client_id
|
|
6784
|
+
FROM audit_log
|
|
6785
|
+
`);
|
|
6786
|
+
db.exec(`DROP TABLE audit_log`);
|
|
6787
|
+
db.exec(`ALTER TABLE audit_log_new RENAME TO audit_log`);
|
|
6788
|
+
}
|
|
6789
|
+
// unified_search_items の CHECK 制約に user_topic を追加
|
|
6790
|
+
// SQLite では CHECK 制約の変更に ALTER TABLE が使えないため、テーブル再作成
|
|
6791
|
+
// 先に全ソーステーブルのunifiedトリガーを削除(DROP TABLE時の参照エラー防止)
|
|
6792
|
+
// user_topics も含める(部分適用からの再実行で既に作成済みの場合に備える)
|
|
6793
|
+
// 旧命名 (unified_search_<table>_<op>) と新命名 (<table>_unified_<op>) の両方を削除
|
|
6794
|
+
for (const table of ["claims", "episodes", "decisions", "theories", "insights", "models", "user_memos", "user_plans", "user_issues", "user_topics"]) {
|
|
6795
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_insert`);
|
|
6796
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_update`);
|
|
6797
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_delete`);
|
|
6798
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_${table}_insert`);
|
|
6799
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_${table}_update`);
|
|
6800
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_${table}_delete`);
|
|
6801
|
+
}
|
|
6802
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_insert`);
|
|
6803
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_update`);
|
|
6804
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_delete`);
|
|
6805
|
+
// 冪等性: 部分適用からの再実行に対応
|
|
6806
|
+
// unified_search_items_new が残存している場合、前回の実行で unified_search_items は
|
|
6807
|
+
// 既に DROP 済みの可能性がある。その場合は _new をそのまま RENAME する。
|
|
6808
|
+
const usiExists = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='unified_search_items'").get();
|
|
6809
|
+
const usiNewExists = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='unified_search_items_new'").get();
|
|
6810
|
+
if (!usiNewExists) {
|
|
6811
|
+
// 通常パス: _new テーブルを作成してデータコピー
|
|
6812
|
+
db.exec(`
|
|
6813
|
+
CREATE TABLE unified_search_items_new (
|
|
6814
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','episode','decision','theory','insight','model','user_memo','user_plan','user_issue','user_topic')),
|
|
6815
|
+
entity_id TEXT NOT NULL,
|
|
6816
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
6817
|
+
category TEXT,
|
|
6818
|
+
title_summary TEXT NOT NULL,
|
|
6819
|
+
search_text TEXT NOT NULL,
|
|
6820
|
+
search_summary TEXT,
|
|
6821
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
6822
|
+
l1_embedding BLOB,
|
|
6823
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
6824
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
6825
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
6826
|
+
)
|
|
6827
|
+
`);
|
|
6828
|
+
if (usiExists) {
|
|
6829
|
+
db.exec(`
|
|
6830
|
+
INSERT INTO unified_search_items_new (entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, l1_embedding, created_at, updated_at)
|
|
6831
|
+
SELECT entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, l1_embedding, created_at, updated_at
|
|
6832
|
+
FROM unified_search_items
|
|
6833
|
+
`);
|
|
6834
|
+
}
|
|
6835
|
+
}
|
|
6836
|
+
// _new が存在する状態(新規作成 or 前回残存)→ 旧テーブル削除して RENAME
|
|
6837
|
+
if (usiExists) {
|
|
6838
|
+
db.exec(`DROP TABLE unified_search_items`);
|
|
6839
|
+
}
|
|
6840
|
+
// RENAME が既に完了しているケース(_new も旧も存在しない = 既に unified_search_items になっている)はスキップ
|
|
6841
|
+
const usiNewStillExists = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='unified_search_items_new'").get();
|
|
6842
|
+
if (usiNewStillExists) {
|
|
6843
|
+
db.exec(`ALTER TABLE unified_search_items_new RENAME TO unified_search_items`);
|
|
6844
|
+
}
|
|
6845
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_scope ON unified_search_items(scope)`);
|
|
6846
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_entity_type ON unified_search_items(entity_type)`);
|
|
6847
|
+
// unified_search_fts 再構築
|
|
6848
|
+
db.exec(`DROP TABLE IF EXISTS unified_search_fts`);
|
|
6849
|
+
db.exec(`
|
|
6850
|
+
CREATE VIRTUAL TABLE unified_search_fts USING fts5(
|
|
6851
|
+
search_text, title_summary, search_summary,
|
|
6852
|
+
content='unified_search_items', content_rowid='rowid', tokenize='trigram'
|
|
6853
|
+
)
|
|
6854
|
+
`);
|
|
6855
|
+
db.exec(`
|
|
6856
|
+
CREATE TRIGGER unified_search_fts_insert AFTER INSERT ON unified_search_items BEGIN
|
|
6857
|
+
INSERT INTO unified_search_fts(rowid, search_text, title_summary, search_summary)
|
|
6858
|
+
VALUES (new.rowid, new.search_text, new.title_summary, new.search_summary);
|
|
6859
|
+
END
|
|
6860
|
+
`);
|
|
6861
|
+
db.exec(`
|
|
6862
|
+
CREATE TRIGGER unified_search_fts_update AFTER UPDATE ON unified_search_items BEGIN
|
|
6863
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, search_text, title_summary, search_summary)
|
|
6864
|
+
VALUES ('delete', old.rowid, old.search_text, old.title_summary, old.search_summary);
|
|
6865
|
+
INSERT INTO unified_search_fts(rowid, search_text, title_summary, search_summary)
|
|
6866
|
+
VALUES (new.rowid, new.search_text, new.title_summary, new.search_summary);
|
|
6867
|
+
END
|
|
6868
|
+
`);
|
|
6869
|
+
db.exec(`
|
|
6870
|
+
CREATE TRIGGER unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
6871
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, search_text, title_summary, search_summary)
|
|
6872
|
+
VALUES ('delete', old.rowid, old.search_text, old.title_summary, old.search_summary);
|
|
6873
|
+
END
|
|
6874
|
+
`);
|
|
6875
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
6876
|
+
// 全ソーステーブルの unified トリガーを再作成
|
|
5554
6877
|
const coal = (col) => `COALESCE(${col}, '')`;
|
|
5555
6878
|
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
6879
|
+
// claims (V28 形式: l2_evidence/l2_falsifier)
|
|
5556
6880
|
const claimTitle = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object`;
|
|
5557
|
-
const claimSearch = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object || ' ' || ${coal(`${p}
|
|
6881
|
+
const claimSearch = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object || ' ' || ${coal(`${p}l2_evidence`)} || ' ' || ${coal(`${p}l2_falsifier`)} || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
5558
6882
|
db.exec(`
|
|
5559
|
-
CREATE TRIGGER claims_unified_insert AFTER INSERT ON claims
|
|
5560
|
-
WHEN new.status = 'active' BEGIN
|
|
6883
|
+
CREATE TRIGGER claims_unified_insert AFTER INSERT ON claims WHEN new.status = 'active' BEGIN
|
|
5561
6884
|
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
5562
6885
|
VALUES ('claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at);
|
|
5563
6886
|
END
|
|
@@ -5566,8 +6889,7 @@ function applyV27(db) {
|
|
|
5566
6889
|
CREATE TRIGGER claims_unified_update AFTER UPDATE ON claims BEGIN
|
|
5567
6890
|
DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id;
|
|
5568
6891
|
INSERT INTO unified_search_items(${columns})
|
|
5569
|
-
SELECT 'claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at
|
|
5570
|
-
WHERE new.status = 'active';
|
|
6892
|
+
SELECT 'claim', new.id, new.scope, new.category, ${claimTitle("new.")}, ${claimSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at WHERE new.status = 'active';
|
|
5571
6893
|
END
|
|
5572
6894
|
`);
|
|
5573
6895
|
db.exec(`
|
|
@@ -5575,84 +6897,463 @@ function applyV27(db) {
|
|
|
5575
6897
|
DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id;
|
|
5576
6898
|
END
|
|
5577
6899
|
`);
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
6900
|
+
// episodes (V20 形式: l2_ columns)
|
|
6901
|
+
db.exec(`
|
|
6902
|
+
CREATE TRIGGER episodes_unified_insert AFTER INSERT ON episodes WHEN new.status = 'active' BEGIN
|
|
6903
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
6904
|
+
VALUES ('episode', new.id, new.scope, NULL, new.title, new.title || ' ' || ${coal("new.l1_content")} || ' ' || ${coal("new.l2_context")} || ' ' || ${coal("new.l2_trigger")} || ' ' || ${jc("new.l2_problems")} || ' ' || ${jc("new.l2_outcomes")} || ' ' || ${jc("new.l2_principles")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
6905
|
+
END
|
|
6906
|
+
`);
|
|
6907
|
+
db.exec(`
|
|
6908
|
+
CREATE TRIGGER episodes_unified_update AFTER UPDATE ON episodes BEGIN
|
|
6909
|
+
DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id;
|
|
6910
|
+
INSERT INTO unified_search_items(${columns})
|
|
6911
|
+
SELECT 'episode', new.id, new.scope, NULL, new.title, new.title || ' ' || ${coal("new.l1_content")} || ' ' || ${coal("new.l2_context")} || ' ' || ${coal("new.l2_trigger")} || ' ' || ${jc("new.l2_problems")} || ' ' || ${jc("new.l2_outcomes")} || ' ' || ${jc("new.l2_principles")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active';
|
|
6912
|
+
END
|
|
6913
|
+
`);
|
|
6914
|
+
db.exec(`
|
|
6915
|
+
CREATE TRIGGER episodes_unified_delete AFTER DELETE ON episodes BEGIN
|
|
6916
|
+
DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id;
|
|
6917
|
+
END
|
|
6918
|
+
`);
|
|
6919
|
+
// decisions (V20 形式: l2_ columns)
|
|
6920
|
+
db.exec(`
|
|
6921
|
+
CREATE TRIGGER decisions_unified_insert AFTER INSERT ON decisions WHEN new.status = 'active' BEGIN
|
|
6922
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
6923
|
+
VALUES ('decision', new.id, new.scope, NULL, new.title, new.title || ' ' || new.description || ' ' || ${coal("new.l1_content")} || ' ' || new.l2_reasoning || ' ' || ${jc("new.l2_alternatives")} || ' ' || ${coal("new.search_summary")}, new.search_summary, '[]', new.created_at, new.updated_at);
|
|
6924
|
+
END
|
|
6925
|
+
`);
|
|
6926
|
+
db.exec(`
|
|
6927
|
+
CREATE TRIGGER decisions_unified_update AFTER UPDATE ON decisions BEGIN
|
|
6928
|
+
DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id;
|
|
6929
|
+
INSERT INTO unified_search_items(${columns})
|
|
6930
|
+
SELECT 'decision', new.id, new.scope, NULL, new.title, new.title || ' ' || new.description || ' ' || ${coal("new.l1_content")} || ' ' || new.l2_reasoning || ' ' || ${jc("new.l2_alternatives")} || ' ' || ${coal("new.search_summary")}, new.search_summary, '[]', new.created_at, new.updated_at WHERE new.status = 'active';
|
|
6931
|
+
END
|
|
6932
|
+
`);
|
|
6933
|
+
db.exec(`
|
|
6934
|
+
CREATE TRIGGER decisions_unified_delete AFTER DELETE ON decisions BEGIN
|
|
6935
|
+
DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id;
|
|
6936
|
+
END
|
|
6937
|
+
`);
|
|
6938
|
+
// theories, insights, models: 共通パターン(l2_ columns)
|
|
6939
|
+
for (const e of [
|
|
6940
|
+
{ type: "theory", table: "theories" },
|
|
6941
|
+
{ type: "insight", table: "insights" },
|
|
6942
|
+
{ type: "model", table: "models" },
|
|
6943
|
+
]) {
|
|
6944
|
+
const searchExpr = (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}l2_core_thesis`)} || ' ' || ${jc(`${p}l2_principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
6945
|
+
db.exec(`
|
|
6946
|
+
CREATE TRIGGER ${e.table}_unified_insert AFTER INSERT ON ${e.table} WHEN new.status = 'active' BEGIN
|
|
6947
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
6948
|
+
VALUES ('${e.type}', new.id, new.scope, NULL, new.title, ${searchExpr("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
6949
|
+
END
|
|
6950
|
+
`);
|
|
6951
|
+
db.exec(`
|
|
6952
|
+
CREATE TRIGGER ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN
|
|
6953
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
6954
|
+
INSERT INTO unified_search_items(${columns})
|
|
6955
|
+
SELECT '${e.type}', new.id, new.scope, NULL, new.title, ${searchExpr("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active';
|
|
6956
|
+
END
|
|
6957
|
+
`);
|
|
6958
|
+
db.exec(`
|
|
6959
|
+
CREATE TRIGGER ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN
|
|
6960
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
6961
|
+
END
|
|
6962
|
+
`);
|
|
5590
6963
|
}
|
|
5591
|
-
//
|
|
6964
|
+
// user_memos (V24 形式: l1_content, user_input)
|
|
5592
6965
|
db.exec(`
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
CREATE VIRTUAL TABLE claims_fts USING fts5(
|
|
5599
|
-
l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary,
|
|
5600
|
-
content='claims', content_rowid='rowid', tokenize='trigram'
|
|
5601
|
-
);
|
|
5602
|
-
|
|
5603
|
-
-- FTS同期トリガー: INSERT
|
|
5604
|
-
CREATE TRIGGER claims_fts_insert AFTER INSERT ON claims BEGIN
|
|
5605
|
-
INSERT INTO claims_fts(rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
5606
|
-
VALUES (new.rowid, new.l2_subject, new.l2_predicate, new.l2_object, new.l2_evidence, new.l1_content, new.search_summary);
|
|
5607
|
-
END;
|
|
5608
|
-
|
|
5609
|
-
-- FTS同期トリガー: UPDATE
|
|
5610
|
-
CREATE TRIGGER claims_fts_update AFTER UPDATE ON claims BEGIN
|
|
5611
|
-
INSERT INTO claims_fts(claims_fts, rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
5612
|
-
VALUES ('delete', old.rowid, old.l2_subject, old.l2_predicate, old.l2_object, old.l2_evidence, old.l1_content, old.search_summary);
|
|
5613
|
-
INSERT INTO claims_fts(rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
5614
|
-
VALUES (new.rowid, new.l2_subject, new.l2_predicate, new.l2_object, new.l2_evidence, new.l1_content, new.search_summary);
|
|
5615
|
-
END;
|
|
5616
|
-
|
|
5617
|
-
-- FTS同期トリガー: DELETE
|
|
5618
|
-
CREATE TRIGGER claims_fts_delete AFTER DELETE ON claims BEGIN
|
|
5619
|
-
INSERT INTO claims_fts(claims_fts, rowid, l2_subject, l2_predicate, l2_object, l2_evidence, l1_content, search_summary)
|
|
5620
|
-
VALUES ('delete', old.rowid, old.l2_subject, old.l2_predicate, old.l2_object, old.l2_evidence, old.l1_content, old.search_summary);
|
|
5621
|
-
END;
|
|
5622
|
-
|
|
5623
|
-
-- FTS rebuild
|
|
5624
|
-
INSERT INTO claims_fts(claims_fts) VALUES('rebuild');
|
|
5625
|
-
|
|
5626
|
-
INSERT INTO schema_version (version) VALUES (28);
|
|
6966
|
+
CREATE TRIGGER user_memos_unified_insert AFTER INSERT ON user_memos
|
|
6967
|
+
WHEN new.status = 'active' AND new.usage_policy != 'human_directed' BEGIN
|
|
6968
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
6969
|
+
VALUES ('user_memo', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${coal("new.user_input")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
6970
|
+
END
|
|
5627
6971
|
`);
|
|
5628
|
-
// unified_search_items の claims トリガー再作成(l2_evidence/l2_falsifier 対応)
|
|
5629
|
-
const coal = (col) => `COALESCE(${col}, '')`;
|
|
5630
|
-
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
5631
|
-
const claimTitle = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object`;
|
|
5632
|
-
const claimSearch = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object || ' ' || ${coal(`${p}l2_evidence`)} || ' ' || ${coal(`${p}l2_falsifier`)} || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
5633
|
-
db.exec(`DROP TRIGGER IF EXISTS claims_unified_insert`);
|
|
5634
|
-
db.exec(`DROP TRIGGER IF EXISTS claims_unified_update`);
|
|
5635
|
-
db.exec(`DROP TRIGGER IF EXISTS claims_unified_delete`);
|
|
5636
6972
|
db.exec(`
|
|
5637
|
-
CREATE TRIGGER
|
|
5638
|
-
|
|
6973
|
+
CREATE TRIGGER user_memos_unified_update AFTER UPDATE ON user_memos BEGIN
|
|
6974
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_memo' AND entity_id = old.id;
|
|
6975
|
+
INSERT INTO unified_search_items(${columns})
|
|
6976
|
+
SELECT 'user_memo', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${coal("new.user_input")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
6977
|
+
WHERE new.status = 'active' AND new.usage_policy != 'human_directed';
|
|
6978
|
+
END
|
|
6979
|
+
`);
|
|
6980
|
+
db.exec(`
|
|
6981
|
+
CREATE TRIGGER user_memos_unified_delete AFTER DELETE ON user_memos BEGIN
|
|
6982
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_memo' AND entity_id = old.id;
|
|
6983
|
+
END
|
|
6984
|
+
`);
|
|
6985
|
+
// user_plans (V24 形式: l1_content)
|
|
6986
|
+
db.exec(`
|
|
6987
|
+
CREATE TRIGGER user_plans_unified_insert AFTER INSERT ON user_plans
|
|
6988
|
+
WHEN new.status = 'active' AND new.usage_policy != 'human_directed' BEGIN
|
|
5639
6989
|
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
5640
|
-
VALUES ('
|
|
6990
|
+
VALUES ('user_plan', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
5641
6991
|
END
|
|
5642
6992
|
`);
|
|
5643
6993
|
db.exec(`
|
|
5644
|
-
CREATE TRIGGER
|
|
5645
|
-
DELETE FROM unified_search_items WHERE entity_type = '
|
|
6994
|
+
CREATE TRIGGER user_plans_unified_update AFTER UPDATE ON user_plans BEGIN
|
|
6995
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_plan' AND entity_id = old.id;
|
|
5646
6996
|
INSERT INTO unified_search_items(${columns})
|
|
5647
|
-
SELECT '
|
|
6997
|
+
SELECT 'user_plan', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
6998
|
+
WHERE new.status = 'active' AND new.usage_policy != 'human_directed';
|
|
6999
|
+
END
|
|
7000
|
+
`);
|
|
7001
|
+
db.exec(`
|
|
7002
|
+
CREATE TRIGGER user_plans_unified_delete AFTER DELETE ON user_plans BEGIN
|
|
7003
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_plan' AND entity_id = old.id;
|
|
7004
|
+
END
|
|
7005
|
+
`);
|
|
7006
|
+
// user_issues (V24 形式: l1_content, entries)
|
|
7007
|
+
db.exec(`
|
|
7008
|
+
CREATE TRIGGER user_issues_unified_insert AFTER INSERT ON user_issues WHEN new.status = 'open' BEGIN
|
|
7009
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
7010
|
+
VALUES ('user_issue', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${jc("new.entries")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
7011
|
+
END
|
|
7012
|
+
`);
|
|
7013
|
+
db.exec(`
|
|
7014
|
+
CREATE TRIGGER user_issues_unified_update AFTER UPDATE ON user_issues BEGIN
|
|
7015
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_issue' AND entity_id = old.id;
|
|
7016
|
+
INSERT INTO unified_search_items(${columns})
|
|
7017
|
+
SELECT 'user_issue', new.id, new.scope, NULL, new.title, new.title || ' ' || new.l1_content || ' ' || ${jc("new.entries")} || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
7018
|
+
WHERE new.status = 'open';
|
|
7019
|
+
END
|
|
7020
|
+
`);
|
|
7021
|
+
db.exec(`
|
|
7022
|
+
CREATE TRIGGER user_issues_unified_delete AFTER DELETE ON user_issues BEGIN
|
|
7023
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_issue' AND entity_id = old.id;
|
|
7024
|
+
END
|
|
7025
|
+
`);
|
|
7026
|
+
// user_topics
|
|
7027
|
+
db.exec(`
|
|
7028
|
+
CREATE TRIGGER user_topics_unified_insert AFTER INSERT ON user_topics WHEN new.status = 'active' BEGIN
|
|
7029
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
7030
|
+
VALUES ('user_topic', new.id, new.scope, NULL, new.title, new.title || ' ' || new.summary || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
7031
|
+
END
|
|
7032
|
+
`);
|
|
7033
|
+
db.exec(`
|
|
7034
|
+
CREATE TRIGGER user_topics_unified_update AFTER UPDATE ON user_topics BEGIN
|
|
7035
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_topic' AND entity_id = old.id;
|
|
7036
|
+
INSERT INTO unified_search_items(${columns})
|
|
7037
|
+
SELECT 'user_topic', new.id, new.scope, NULL, new.title, new.title || ' ' || new.summary || ' ' || ${jc("new.tags")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
5648
7038
|
WHERE new.status = 'active';
|
|
5649
7039
|
END
|
|
5650
7040
|
`);
|
|
5651
7041
|
db.exec(`
|
|
5652
|
-
CREATE TRIGGER
|
|
5653
|
-
DELETE FROM unified_search_items WHERE entity_type = '
|
|
7042
|
+
CREATE TRIGGER user_topics_unified_delete AFTER DELETE ON user_topics BEGIN
|
|
7043
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_topic' AND entity_id = old.id;
|
|
5654
7044
|
END
|
|
5655
7045
|
`);
|
|
7046
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (29)`);
|
|
7047
|
+
}
|
|
7048
|
+
/**
|
|
7049
|
+
* V30: client_id → device_id リネーム
|
|
7050
|
+
* audit_log, user_topics の client_id カラムを device_id に変更。
|
|
7051
|
+
* store_meta のキーも client_id → device_id に変更。
|
|
7052
|
+
* 既存DBで V29 が旧コードで適用済みの場合にカラムをリネームする。
|
|
7053
|
+
*/
|
|
7054
|
+
function applyV30(db) {
|
|
7055
|
+
// store_meta の key リネーム(V29 で既に実施済みの場合は no-op)
|
|
7056
|
+
db.exec(`UPDATE store_meta SET key = 'device_id' WHERE key = 'client_id'`);
|
|
7057
|
+
// audit_log: client_id → device_id(テーブル再作成)
|
|
7058
|
+
if (hasColumn(db, "audit_log", "client_id")) {
|
|
7059
|
+
db.exec(`
|
|
7060
|
+
CREATE TABLE audit_log_v30 (
|
|
7061
|
+
id TEXT PRIMARY KEY,
|
|
7062
|
+
operation TEXT NOT NULL CHECK(operation IN (
|
|
7063
|
+
'create','update','retract','supersede','reverse','obsolete','archive','promote'
|
|
7064
|
+
)),
|
|
7065
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode','theory','insight','model','user_memo','user_plan','user_issue','user_topic')),
|
|
7066
|
+
entity_id TEXT NOT NULL,
|
|
7067
|
+
summary TEXT NOT NULL,
|
|
7068
|
+
details TEXT,
|
|
7069
|
+
client_name TEXT,
|
|
7070
|
+
client_version TEXT,
|
|
7071
|
+
session_id TEXT,
|
|
7072
|
+
source_tool TEXT,
|
|
7073
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
7074
|
+
device_id TEXT
|
|
7075
|
+
)
|
|
7076
|
+
`);
|
|
7077
|
+
db.exec(`
|
|
7078
|
+
INSERT INTO audit_log_v30 (id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool, created_at, device_id)
|
|
7079
|
+
SELECT id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool, created_at, client_id
|
|
7080
|
+
FROM audit_log
|
|
7081
|
+
`);
|
|
7082
|
+
db.exec(`DROP TABLE audit_log`);
|
|
7083
|
+
db.exec(`ALTER TABLE audit_log_v30 RENAME TO audit_log`);
|
|
7084
|
+
}
|
|
7085
|
+
// user_topics: client_id → device_id(テーブル再作成)
|
|
7086
|
+
// client_id がある場合はリネーム、device_id が欠落している場合は追加
|
|
7087
|
+
const hasUserTopics = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='user_topics'").get();
|
|
7088
|
+
if (hasUserTopics && hasColumn(db, "user_topics", "client_id")) {
|
|
7089
|
+
// FTSトリガーを削除(DROP TABLE前)
|
|
7090
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_fts_insert`);
|
|
7091
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_fts_update`);
|
|
7092
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_fts_delete`);
|
|
7093
|
+
// unified トリガーも削除
|
|
7094
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_unified_insert`);
|
|
7095
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_unified_update`);
|
|
7096
|
+
db.exec(`DROP TRIGGER IF EXISTS user_topics_unified_delete`);
|
|
7097
|
+
db.exec(`
|
|
7098
|
+
CREATE TABLE user_topics_v30 (
|
|
7099
|
+
id TEXT PRIMARY KEY,
|
|
7100
|
+
title TEXT NOT NULL,
|
|
7101
|
+
summary TEXT NOT NULL,
|
|
7102
|
+
device_id TEXT,
|
|
7103
|
+
cwd TEXT,
|
|
7104
|
+
conversation_id TEXT,
|
|
7105
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
7106
|
+
CHECK(priority IN ('low','medium','high','critical')),
|
|
7107
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
7108
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
7109
|
+
search_summary TEXT,
|
|
7110
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
7111
|
+
CHECK(status IN ('active','closed','archived')),
|
|
7112
|
+
l1_embedding BLOB,
|
|
7113
|
+
client_name TEXT,
|
|
7114
|
+
client_version TEXT,
|
|
7115
|
+
source_tool TEXT,
|
|
7116
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
7117
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
7118
|
+
)
|
|
7119
|
+
`);
|
|
7120
|
+
db.exec(`
|
|
7121
|
+
INSERT INTO user_topics_v30 (id, title, summary, device_id, cwd, conversation_id, priority, tags, scope, search_summary, status, l1_embedding, client_name, client_version, source_tool, created_at, updated_at)
|
|
7122
|
+
SELECT id, title, summary, client_id, cwd, conversation_id, priority, tags, scope, search_summary, status, l1_embedding, client_name, client_version, source_tool, created_at, updated_at
|
|
7123
|
+
FROM user_topics
|
|
7124
|
+
`);
|
|
7125
|
+
db.exec(`DROP TABLE user_topics`);
|
|
7126
|
+
db.exec(`ALTER TABLE user_topics_v30 RENAME TO user_topics`);
|
|
7127
|
+
// インデックス再作成
|
|
7128
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_topics_status ON user_topics(status)`);
|
|
7129
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_topics_scope ON user_topics(scope)`);
|
|
7130
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_topics_priority ON user_topics(priority)`);
|
|
7131
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_topics_updated ON user_topics(updated_at)`);
|
|
7132
|
+
// FTS再構築
|
|
7133
|
+
db.exec(`DROP TABLE IF EXISTS user_topics_fts`);
|
|
7134
|
+
db.exec(`
|
|
7135
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS user_topics_fts USING fts5(
|
|
7136
|
+
title, summary, tags, search_summary,
|
|
7137
|
+
content='user_topics', content_rowid='rowid', tokenize='trigram'
|
|
7138
|
+
)
|
|
7139
|
+
`);
|
|
7140
|
+
const jc = (col) => `REPLACE(REPLACE(${col}, '[', ''), ']', '')`;
|
|
7141
|
+
db.exec(`
|
|
7142
|
+
CREATE TRIGGER user_topics_fts_insert AFTER INSERT ON user_topics BEGIN
|
|
7143
|
+
INSERT INTO user_topics_fts(rowid, title, summary, tags, search_summary)
|
|
7144
|
+
VALUES (new.rowid, new.title, new.summary, ${jc("new.tags")}, new.search_summary);
|
|
7145
|
+
END
|
|
7146
|
+
`);
|
|
7147
|
+
db.exec(`
|
|
7148
|
+
CREATE TRIGGER user_topics_fts_update AFTER UPDATE ON user_topics BEGIN
|
|
7149
|
+
INSERT INTO user_topics_fts(user_topics_fts, rowid, title, summary, tags, search_summary)
|
|
7150
|
+
VALUES ('delete', old.rowid, old.title, old.summary, ${jc("old.tags")}, old.search_summary);
|
|
7151
|
+
INSERT INTO user_topics_fts(rowid, title, summary, tags, search_summary)
|
|
7152
|
+
VALUES (new.rowid, new.title, new.summary, ${jc("new.tags")}, new.search_summary);
|
|
7153
|
+
END
|
|
7154
|
+
`);
|
|
7155
|
+
db.exec(`
|
|
7156
|
+
CREATE TRIGGER user_topics_fts_delete AFTER DELETE ON user_topics BEGIN
|
|
7157
|
+
INSERT INTO user_topics_fts(user_topics_fts, rowid, title, summary, tags, search_summary)
|
|
7158
|
+
VALUES ('delete', old.rowid, old.title, old.summary, ${jc("old.tags")}, old.search_summary);
|
|
7159
|
+
END
|
|
7160
|
+
`);
|
|
7161
|
+
db.exec(`INSERT INTO user_topics_fts(user_topics_fts) VALUES('rebuild')`);
|
|
7162
|
+
// unified トリガー再作成
|
|
7163
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
7164
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
7165
|
+
db.exec(`
|
|
7166
|
+
CREATE TRIGGER user_topics_unified_insert AFTER INSERT ON user_topics WHEN new.status = 'active' BEGIN
|
|
7167
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
7168
|
+
VALUES ('user_topic', new.id, new.scope, NULL, new.title, new.title || ' ' || ${coal("new.summary")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
7169
|
+
END
|
|
7170
|
+
`);
|
|
7171
|
+
db.exec(`
|
|
7172
|
+
CREATE TRIGGER user_topics_unified_update AFTER UPDATE ON user_topics BEGIN
|
|
7173
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_topic' AND entity_id = old.id;
|
|
7174
|
+
INSERT INTO unified_search_items(${columns})
|
|
7175
|
+
SELECT 'user_topic', new.id, new.scope, NULL, new.title, new.title || ' ' || ${coal("new.summary")} || ' ' || ${coal("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active';
|
|
7176
|
+
END
|
|
7177
|
+
`);
|
|
7178
|
+
db.exec(`
|
|
7179
|
+
CREATE TRIGGER user_topics_unified_delete AFTER DELETE ON user_topics BEGIN
|
|
7180
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_topic' AND entity_id = old.id;
|
|
7181
|
+
END
|
|
7182
|
+
`);
|
|
7183
|
+
}
|
|
7184
|
+
else if (hasUserTopics && !hasColumn(db, "user_topics", "device_id")) {
|
|
7185
|
+
// device_id カラムが欠落している場合(中間バージョンのV29で作成されたDB)
|
|
7186
|
+
db.exec(`ALTER TABLE user_topics ADD COLUMN device_id TEXT`);
|
|
7187
|
+
const deviceIdRow = db.prepare("SELECT value FROM store_meta WHERE key = 'device_id'").get();
|
|
7188
|
+
if (deviceIdRow) {
|
|
7189
|
+
db.prepare("UPDATE user_topics SET device_id = ? WHERE device_id IS NULL").run(deviceIdRow.value);
|
|
7190
|
+
}
|
|
7191
|
+
}
|
|
7192
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (30)`);
|
|
7193
|
+
}
|
|
7194
|
+
// ============================================================
|
|
7195
|
+
// V31: user_files テーブル新設(File Vault機能)
|
|
7196
|
+
// ============================================================
|
|
7197
|
+
function applyV31(db) {
|
|
7198
|
+
const transaction = db.transaction(() => {
|
|
7199
|
+
// user_files テーブル
|
|
7200
|
+
db.exec(`
|
|
7201
|
+
CREATE TABLE IF NOT EXISTS user_files (
|
|
7202
|
+
id TEXT PRIMARY KEY,
|
|
7203
|
+
title TEXT NOT NULL,
|
|
7204
|
+
description TEXT,
|
|
7205
|
+
original_filename TEXT NOT NULL,
|
|
7206
|
+
original_encoding TEXT NOT NULL DEFAULT 'utf-8',
|
|
7207
|
+
file_data TEXT NOT NULL,
|
|
7208
|
+
file_hash TEXT NOT NULL,
|
|
7209
|
+
file_size INTEGER NOT NULL,
|
|
7210
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
7211
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
7212
|
+
search_summary TEXT,
|
|
7213
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
7214
|
+
CHECK(status IN ('active','archived')),
|
|
7215
|
+
l1_embedding BLOB,
|
|
7216
|
+
client_name TEXT,
|
|
7217
|
+
client_version TEXT,
|
|
7218
|
+
source_tool TEXT,
|
|
7219
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
7220
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
7221
|
+
)
|
|
7222
|
+
`);
|
|
7223
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_files_status ON user_files(status)`);
|
|
7224
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_files_scope ON user_files(scope)`);
|
|
7225
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_files_title ON user_files(title)`);
|
|
7226
|
+
// audit_log: entity_type CHECK に 'user_file' 追加(テーブル再作成)
|
|
7227
|
+
// 一時テーブル名は audit_log_new に統一(残骸掃除パターンと一致させる)
|
|
7228
|
+
db.exec(`DROP TABLE IF EXISTS audit_log_new`);
|
|
7229
|
+
db.exec(`
|
|
7230
|
+
CREATE TABLE audit_log_new (
|
|
7231
|
+
id TEXT PRIMARY KEY,
|
|
7232
|
+
operation TEXT NOT NULL CHECK(operation IN (
|
|
7233
|
+
'create','update','retract','supersede','reverse','obsolete','archive','promote'
|
|
7234
|
+
)),
|
|
7235
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode','theory','insight','model','user_memo','user_plan','user_issue','user_topic','user_file')),
|
|
7236
|
+
entity_id TEXT NOT NULL,
|
|
7237
|
+
summary TEXT NOT NULL,
|
|
7238
|
+
details TEXT,
|
|
7239
|
+
client_name TEXT,
|
|
7240
|
+
client_version TEXT,
|
|
7241
|
+
session_id TEXT,
|
|
7242
|
+
source_tool TEXT,
|
|
7243
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
7244
|
+
device_id TEXT
|
|
7245
|
+
)
|
|
7246
|
+
`);
|
|
7247
|
+
db.exec(`
|
|
7248
|
+
INSERT INTO audit_log_new (id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool, created_at, device_id)
|
|
7249
|
+
SELECT id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool, created_at, device_id
|
|
7250
|
+
FROM audit_log
|
|
7251
|
+
`);
|
|
7252
|
+
db.exec(`DROP TABLE audit_log`);
|
|
7253
|
+
db.exec(`ALTER TABLE audit_log_new RENAME TO audit_log`);
|
|
7254
|
+
// audit_log インデックス再作成(4本すべて)
|
|
7255
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_entity ON audit_log(entity_type, entity_id)`);
|
|
7256
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_operation ON audit_log(operation)`);
|
|
7257
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_created ON audit_log(created_at)`);
|
|
7258
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_session ON audit_log(session_id)`);
|
|
7259
|
+
// unified search統合: CHECK制約更新 + 全トリガー再作成 + user_filesトリガー追加
|
|
7260
|
+
// 全トリガーを削除
|
|
7261
|
+
const triggerTables = ["claims", "episodes", "decisions", "theories", "insights", "models", "user_memos", "user_plans", "user_issues", "user_topics"];
|
|
7262
|
+
for (const t of triggerTables) {
|
|
7263
|
+
db.exec(`DROP TRIGGER IF EXISTS ${t}_unified_insert`);
|
|
7264
|
+
db.exec(`DROP TRIGGER IF EXISTS ${t}_unified_update`);
|
|
7265
|
+
db.exec(`DROP TRIGGER IF EXISTS ${t}_unified_delete`);
|
|
7266
|
+
}
|
|
7267
|
+
// FTS5 削除(content テーブル参照のため先に)
|
|
7268
|
+
db.exec(`DROP TABLE IF EXISTS unified_search_fts`);
|
|
7269
|
+
// unified_search_items 再作成(CHECK に 'user_file' 追加)
|
|
7270
|
+
db.exec(`DROP TABLE IF EXISTS unified_search_items_new`);
|
|
7271
|
+
db.exec(`
|
|
7272
|
+
CREATE TABLE unified_search_items_new (
|
|
7273
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','episode','decision','theory','insight','model','user_memo','user_plan','user_issue','user_topic','user_file')),
|
|
7274
|
+
entity_id TEXT NOT NULL,
|
|
7275
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
7276
|
+
category TEXT,
|
|
7277
|
+
title_summary TEXT NOT NULL,
|
|
7278
|
+
search_text TEXT NOT NULL,
|
|
7279
|
+
search_summary TEXT,
|
|
7280
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
7281
|
+
l1_embedding BLOB,
|
|
7282
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
7283
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
7284
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
7285
|
+
)
|
|
7286
|
+
`);
|
|
7287
|
+
db.exec(`INSERT INTO unified_search_items_new SELECT * FROM unified_search_items`);
|
|
7288
|
+
db.exec(`DROP TABLE unified_search_items`);
|
|
7289
|
+
db.exec(`ALTER TABLE unified_search_items_new RENAME TO unified_search_items`);
|
|
7290
|
+
// インデックス再作成
|
|
7291
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_scope ON unified_search_items(scope)`);
|
|
7292
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_entity_type ON unified_search_items(entity_type)`);
|
|
7293
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_updated ON unified_search_items(updated_at)`);
|
|
7294
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_entity_id ON unified_search_items(entity_id)`);
|
|
7295
|
+
// FTS5 再構築
|
|
7296
|
+
db.exec(`
|
|
7297
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS unified_search_fts USING fts5(
|
|
7298
|
+
search_text, title_summary, search_summary,
|
|
7299
|
+
content='unified_search_items', content_rowid='rowid', tokenize='trigram'
|
|
7300
|
+
)
|
|
7301
|
+
`);
|
|
7302
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
7303
|
+
// 全トリガー再作成
|
|
7304
|
+
const jc2 = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
7305
|
+
const jcT = (col) => `REPLACE(REPLACE(${col}, '[', ''), ']', '')`;
|
|
7306
|
+
const coal2 = (col) => `COALESCE(${col}, '')`;
|
|
7307
|
+
const cols = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
7308
|
+
// claims
|
|
7309
|
+
const ct = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object`;
|
|
7310
|
+
const cs = (p) => `${p}l2_subject || ' ' || ${p}l2_predicate || ' ' || ${p}l2_object || ' ' || ${coal2(`${p}l2_evidence`)} || ' ' || ${coal2(`${p}l2_falsifier`)} || ' ' || ${coal2(`${p}l1_content`)} || ' ' || ${coal2(`${p}search_summary`)}`;
|
|
7311
|
+
db.exec(`CREATE TRIGGER claims_unified_insert AFTER INSERT ON claims WHEN new.status = 'active' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('claim', new.id, new.scope, new.category, ${ct("new.")}, ${cs("new.")}, new.search_summary, '[]', new.created_at, new.updated_at); END`);
|
|
7312
|
+
db.exec(`CREATE TRIGGER claims_unified_update AFTER UPDATE ON claims BEGIN DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT 'claim', new.id, new.scope, new.category, ${ct("new.")}, ${cs("new.")}, new.search_summary, '[]', new.created_at, new.updated_at WHERE new.status = 'active'; END`);
|
|
7313
|
+
db.exec(`CREATE TRIGGER claims_unified_delete AFTER DELETE ON claims BEGIN DELETE FROM unified_search_items WHERE entity_type = 'claim' AND entity_id = old.id; END`);
|
|
7314
|
+
// episodes
|
|
7315
|
+
const es = (p) => `${p}title || ' ' || ${coal2(`${p}l1_content`)} || ' ' || ${coal2(`${p}l2_context`)} || ' ' || ${coal2(`${p}l2_trigger`)} || ' ' || ${jc2(`${p}l2_problems`)} || ' ' || ${jc2(`${p}l2_outcomes`)} || ' ' || ${jc2(`${p}l2_principles`)} || ' ' || ${jc2(`${p}tags`)} || ' ' || ${coal2(`${p}search_summary`)}`;
|
|
7316
|
+
db.exec(`CREATE TRIGGER episodes_unified_insert AFTER INSERT ON episodes WHEN new.status = 'active' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('episode', new.id, new.scope, NULL, new.title, ${es("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at); END`);
|
|
7317
|
+
db.exec(`CREATE TRIGGER episodes_unified_update AFTER UPDATE ON episodes BEGIN DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT 'episode', new.id, new.scope, NULL, new.title, ${es("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active'; END`);
|
|
7318
|
+
db.exec(`CREATE TRIGGER episodes_unified_delete AFTER DELETE ON episodes BEGIN DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id; END`);
|
|
7319
|
+
// decisions
|
|
7320
|
+
const ds = (p) => `${p}title || ' ' || ${p}description || ' ' || ${coal2(`${p}l1_content`)} || ' ' || ${p}l2_reasoning || ' ' || ${jc2(`${p}l2_alternatives`)} || ' ' || ${coal2(`${p}search_summary`)}`;
|
|
7321
|
+
db.exec(`CREATE TRIGGER decisions_unified_insert AFTER INSERT ON decisions WHEN new.status = 'active' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('decision', new.id, new.scope, NULL, new.title, ${ds("new.")}, new.search_summary, '[]', new.created_at, new.updated_at); END`);
|
|
7322
|
+
db.exec(`CREATE TRIGGER decisions_unified_update AFTER UPDATE ON decisions BEGIN DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT 'decision', new.id, new.scope, NULL, new.title, ${ds("new.")}, new.search_summary, '[]', new.created_at, new.updated_at WHERE new.status = 'active'; END`);
|
|
7323
|
+
db.exec(`CREATE TRIGGER decisions_unified_delete AFTER DELETE ON decisions BEGIN DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id; END`);
|
|
7324
|
+
// theories, insights, models
|
|
7325
|
+
for (const e of [{ type: "theory", table: "theories" }, { type: "insight", table: "insights" }, { type: "model", table: "models" }]) {
|
|
7326
|
+
const s = (p) => `${p}title || ' ' || ${coal2(`${p}description`)} || ' ' || ${coal2(`${p}l1_content`)} || ' ' || ${coal2(`${p}l2_core_thesis`)} || ' ' || ${jc2(`${p}l2_principles`)} || ' ' || ${jc2(`${p}evidence_refs`)} || ' ' || ${jc2(`${p}tags`)} || ' ' || ${coal2(`${p}search_summary`)}`;
|
|
7327
|
+
db.exec(`CREATE TRIGGER ${e.table}_unified_insert AFTER INSERT ON ${e.table} WHEN new.status = 'active' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('${e.type}', new.id, new.scope, NULL, new.title, ${s("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at); END`);
|
|
7328
|
+
db.exec(`CREATE TRIGGER ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT '${e.type}', new.id, new.scope, NULL, new.title, ${s("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active'; END`);
|
|
7329
|
+
db.exec(`CREATE TRIGGER ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id; END`);
|
|
7330
|
+
}
|
|
7331
|
+
// user_memos
|
|
7332
|
+
const ms = (p) => `${p}title || ' ' || ${p}l1_content || ' ' || ${coal2(`${p}user_input`)} || ' ' || ${jc2(`${p}tags`)} || ' ' || ${coal2(`${p}search_summary`)}`;
|
|
7333
|
+
db.exec(`CREATE TRIGGER user_memos_unified_insert AFTER INSERT ON user_memos WHEN new.status = 'active' AND new.usage_policy != 'human_directed' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('user_memo', new.id, new.scope, NULL, new.title, ${ms("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at); END`);
|
|
7334
|
+
db.exec(`CREATE TRIGGER user_memos_unified_update AFTER UPDATE ON user_memos BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_memo' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT 'user_memo', new.id, new.scope, NULL, new.title, ${ms("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active' AND new.usage_policy != 'human_directed'; END`);
|
|
7335
|
+
db.exec(`CREATE TRIGGER user_memos_unified_delete AFTER DELETE ON user_memos BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_memo' AND entity_id = old.id; END`);
|
|
7336
|
+
// user_plans
|
|
7337
|
+
const ps = (p) => `${p}title || ' ' || ${p}l1_content || ' ' || ${jc2(`${p}tags`)} || ' ' || ${coal2(`${p}search_summary`)}`;
|
|
7338
|
+
db.exec(`CREATE TRIGGER user_plans_unified_insert AFTER INSERT ON user_plans WHEN new.status = 'active' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('user_plan', new.id, new.scope, NULL, new.title, ${ps("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at); END`);
|
|
7339
|
+
db.exec(`CREATE TRIGGER user_plans_unified_update AFTER UPDATE ON user_plans BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_plan' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT 'user_plan', new.id, new.scope, NULL, new.title, ${ps("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active'; END`);
|
|
7340
|
+
db.exec(`CREATE TRIGGER user_plans_unified_delete AFTER DELETE ON user_plans BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_plan' AND entity_id = old.id; END`);
|
|
7341
|
+
// user_issues
|
|
7342
|
+
const is2 = (p) => `${p}title || ' ' || ${p}l1_content || ' ' || ${jc2(`${p}entries`)} || ' ' || ${jc2(`${p}tags`)} || ' ' || ${coal2(`${p}search_summary`)}`;
|
|
7343
|
+
db.exec(`CREATE TRIGGER user_issues_unified_insert AFTER INSERT ON user_issues WHEN new.status = 'open' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('user_issue', new.id, new.scope, NULL, new.title, ${is2("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at); END`);
|
|
7344
|
+
db.exec(`CREATE TRIGGER user_issues_unified_update AFTER UPDATE ON user_issues BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_issue' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT 'user_issue', new.id, new.scope, NULL, new.title, ${is2("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'open'; END`);
|
|
7345
|
+
db.exec(`CREATE TRIGGER user_issues_unified_delete AFTER DELETE ON user_issues BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_issue' AND entity_id = old.id; END`);
|
|
7346
|
+
// user_topics
|
|
7347
|
+
db.exec(`CREATE TRIGGER user_topics_unified_insert AFTER INSERT ON user_topics WHEN new.status = 'active' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('user_topic', new.id, new.scope, NULL, new.title, new.title || ' ' || new.summary || ' ' || ${jcT("new.tags")} || ' ' || ${coal2("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at); END`);
|
|
7348
|
+
db.exec(`CREATE TRIGGER user_topics_unified_update AFTER UPDATE ON user_topics BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_topic' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT 'user_topic', new.id, new.scope, NULL, new.title, new.title || ' ' || new.summary || ' ' || ${jcT("new.tags")} || ' ' || ${coal2("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active'; END`);
|
|
7349
|
+
db.exec(`CREATE TRIGGER user_topics_unified_delete AFTER DELETE ON user_topics BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_topic' AND entity_id = old.id; END`);
|
|
7350
|
+
// user_files
|
|
7351
|
+
db.exec(`CREATE TRIGGER user_files_unified_insert AFTER INSERT ON user_files WHEN new.status = 'active' BEGIN INSERT OR REPLACE INTO unified_search_items(${cols}) VALUES ('user_file', new.id, new.scope, NULL, new.title, new.title || ' ' || COALESCE(new.description, '') || ' ' || new.original_filename || ' ' || ${jcT("new.tags")} || ' ' || ${coal2("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at); END`);
|
|
7352
|
+
db.exec(`CREATE TRIGGER user_files_unified_update AFTER UPDATE ON user_files BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_file' AND entity_id = old.id; INSERT INTO unified_search_items(${cols}) SELECT 'user_file', new.id, new.scope, NULL, new.title, new.title || ' ' || COALESCE(new.description, '') || ' ' || new.original_filename || ' ' || ${jcT("new.tags")} || ' ' || ${coal2("new.search_summary")}, new.search_summary, new.tags, new.created_at, new.updated_at WHERE new.status = 'active'; END`);
|
|
7353
|
+
db.exec(`CREATE TRIGGER user_files_unified_delete AFTER DELETE ON user_files BEGIN DELETE FROM unified_search_items WHERE entity_type = 'user_file' AND entity_id = old.id; END`);
|
|
7354
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (31)`);
|
|
7355
|
+
});
|
|
7356
|
+
transaction();
|
|
5656
7357
|
}
|
|
5657
7358
|
/** テーブルに指定カラムが存在するかチェック */
|
|
5658
7359
|
function hasColumn(db, table, column) {
|