watashi-db 0.0.7 → 0.0.8
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/README.md +120 -63
- package/Usersj.miyamoto.watashi-dbdefault.db +0 -0
- package/cowork-plugin/.claude-plugin/plugin.json +1 -1
- package/cowork-plugin/.mcp.json +1 -1
- package/cowork-plugin/hooks/hooks.json +7 -0
- package/cowork-plugin/skills/recall/SKILL.md +6 -3
- package/dist/config/loader.js +25 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +53 -0
- package/dist/config/schema.js +9 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/config/validator.js +11 -0
- package/dist/config/validator.js.map +1 -1
- package/dist/constants.d.ts +9 -1
- package/dist/constants.js +51 -3
- package/dist/constants.js.map +1 -1
- package/dist/context-pack.d.ts +7 -2
- package/dist/context-pack.js +137 -31
- package/dist/context-pack.js.map +1 -1
- package/dist/database/archive.d.ts +39 -0
- package/dist/database/archive.js +180 -0
- package/dist/database/archive.js.map +1 -0
- package/dist/database/groom.d.ts +39 -0
- package/dist/database/groom.js +246 -0
- package/dist/database/groom.js.map +1 -0
- package/dist/database/maintain.d.ts +39 -0
- package/dist/database/maintain.js +62 -0
- package/dist/database/maintain.js.map +1 -0
- package/dist/database/queries-core.d.ts +320 -12
- package/dist/database/queries-core.js +1834 -56
- package/dist/database/queries-core.js.map +1 -1
- package/dist/database/queries.d.ts +213 -12
- package/dist/database/queries.js +109 -0
- package/dist/database/queries.js.map +1 -1
- package/dist/database/schema.js +4199 -1
- package/dist/database/schema.js.map +1 -1
- package/dist/embedding/embed-on-write.d.ts +98 -0
- package/dist/embedding/embed-on-write.js +104 -0
- package/dist/embedding/embed-on-write.js.map +1 -0
- package/dist/embedding/ollama.d.ts +18 -0
- package/dist/embedding/ollama.js +67 -0
- package/dist/embedding/ollama.js.map +1 -0
- package/dist/embedding/openai.d.ts +17 -0
- package/dist/embedding/openai.js +77 -0
- package/dist/embedding/openai.js.map +1 -0
- package/dist/embedding/provider.d.ts +34 -0
- package/dist/embedding/provider.js +92 -0
- package/dist/embedding/provider.js.map +1 -0
- package/dist/embedding/search.d.ts +61 -0
- package/dist/embedding/search.js +165 -0
- package/dist/embedding/search.js.map +1 -0
- package/dist/embedding/similarity.d.ts +21 -0
- package/dist/embedding/similarity.js +48 -0
- package/dist/embedding/similarity.js.map +1 -0
- package/dist/embedding/transformers-local.d.ts +24 -0
- package/dist/embedding/transformers-local.js +65 -0
- package/dist/embedding/transformers-local.js.map +1 -0
- package/dist/embedding/types.d.ts +27 -0
- package/dist/embedding/types.js +6 -0
- package/dist/embedding/types.js.map +1 -0
- package/dist/hook.d.ts +54 -1
- package/dist/hook.js +560 -7
- package/dist/hook.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/policy.d.ts +7 -1
- package/dist/policy.js +30 -0
- package/dist/policy.js.map +1 -1
- package/dist/project-detect.d.ts +8 -0
- package/dist/project-detect.js +36 -0
- package/dist/project-detect.js.map +1 -0
- package/dist/prompts/bootstrap-prompts.js +48 -0
- package/dist/prompts/bootstrap-prompts.js.map +1 -1
- package/dist/resources/config-guide-content.d.ts +7 -0
- package/dist/resources/config-guide-content.js +180 -0
- package/dist/resources/config-guide-content.js.map +1 -0
- package/dist/resources/context-resources.js +18 -1
- package/dist/resources/context-resources.js.map +1 -1
- package/dist/server-instructions.js +60 -3
- package/dist/server-instructions.js.map +1 -1
- package/dist/server.js +26 -18
- package/dist/server.js.map +1 -1
- package/dist/setup.d.ts +14 -0
- package/dist/setup.js +132 -10
- package/dist/setup.js.map +1 -1
- package/dist/store/federation.d.ts +126 -2
- package/dist/store/federation.js +719 -2
- package/dist/store/federation.js.map +1 -1
- package/dist/store/merge.d.ts +11 -0
- package/dist/store/merge.js +38 -8
- package/dist/store/merge.js.map +1 -1
- package/dist/store/session-state.d.ts +12 -0
- package/dist/store/session-state.js +27 -1
- package/dist/store/session-state.js.map +1 -1
- package/dist/store/sync-manager.d.ts +7 -5
- package/dist/store/sync-manager.js +134 -58
- package/dist/store/sync-manager.js.map +1 -1
- package/dist/tools/claim-tools.d.ts +7 -1
- package/dist/tools/claim-tools.js +328 -178
- package/dist/tools/claim-tools.js.map +1 -1
- package/dist/tools/decision-tools.d.ts +3 -2
- package/dist/tools/decision-tools.js +48 -131
- package/dist/tools/decision-tools.js.map +1 -1
- package/dist/tools/episode-tools.d.ts +2 -3
- package/dist/tools/episode-tools.js +59 -130
- package/dist/tools/episode-tools.js.map +1 -1
- package/dist/tools/export-tools.d.ts +7 -1
- package/dist/tools/export-tools.js +90 -68
- package/dist/tools/export-tools.js.map +1 -1
- package/dist/tools/get-tools.d.ts +11 -0
- package/dist/tools/get-tools.js +282 -0
- package/dist/tools/get-tools.js.map +1 -0
- package/dist/tools/groom-tools.d.ts +13 -0
- package/dist/tools/groom-tools.js +197 -0
- package/dist/tools/groom-tools.js.map +1 -0
- package/dist/tools/helpers.d.ts +10 -0
- package/dist/tools/helpers.js +10 -0
- package/dist/tools/helpers.js.map +1 -0
- package/dist/tools/insight-tools.d.ts +11 -0
- package/dist/tools/insight-tools.js +167 -0
- package/dist/tools/insight-tools.js.map +1 -0
- package/dist/tools/knowledge-tools.d.ts +7 -0
- package/dist/tools/knowledge-tools.js +151 -0
- package/dist/tools/knowledge-tools.js.map +1 -0
- package/dist/tools/maintenance-tools.d.ts +11 -0
- package/dist/tools/maintenance-tools.js +1074 -0
- package/dist/tools/maintenance-tools.js.map +1 -0
- package/dist/tools/memo-tools.d.ts +15 -0
- package/dist/tools/memo-tools.js +370 -0
- package/dist/tools/memo-tools.js.map +1 -0
- package/dist/tools/model-tools.d.ts +11 -0
- package/dist/tools/model-tools.js +175 -0
- package/dist/tools/model-tools.js.map +1 -0
- package/dist/tools/profile-tools.d.ts +13 -1
- package/dist/tools/profile-tools.js +80 -113
- package/dist/tools/profile-tools.js.map +1 -1
- package/dist/tools/promote-tools.js +91 -4
- package/dist/tools/promote-tools.js.map +1 -1
- package/dist/tools/query-tools.d.ts +13 -1
- package/dist/tools/query-tools.js +358 -121
- package/dist/tools/query-tools.js.map +1 -1
- package/dist/tools/store-tools.d.ts +5 -2
- package/dist/tools/store-tools.js +94 -74
- package/dist/tools/store-tools.js.map +1 -1
- package/dist/tools/theory-tools.js +24 -0
- package/dist/tools/theory-tools.js.map +1 -1
- package/dist/types.d.ts +641 -229
- package/dist/types.js +186 -134
- package/dist/types.js.map +1 -1
- package/misc/work-notes/main 20260224.txt +98 -0
- package/misc/work-notes/plans/20260220084456_UNKNOWN_Issue-#24-Episode/343/202/277/343/202/244/343/203/210/343/203/253/343/201/256-Qiita/346/226/271/345/274/217/343/202/254/343/202/244.md +52 -0
- package/misc/work-notes/plans/20260220095602_UNKNOWN_Issue-#10-/346/233/270/343/201/215/350/276/274/343/201/277/346/231/202/343/201/256/343/202/263/343/203/263/343/203/206/343/203/263/343/203/204Lint/346/251/237.md +120 -0
- package/misc/work-notes/plans/20260220100439_UNKNOWN_Issue-#10-/346/233/270/343/201/215/350/276/274/343/201/277/346/231/202/343/201/256/343/202/263/343/203/263/343/203/206/343/203/263/343/203/204Lint/346/251/237.md +156 -0
- package/misc/work-notes/plans/20260220110445_UNKNOWN_Issue-#11-/343/203/227/343/203/255/343/203/225/343/202/241/343/202/244/343/203/253/350/246/201/347/264/204/343/201/256/343/202/271/343/202/263/343/202/242/343/203/252/303/243.md +154 -0
- package/misc/work-notes/plans/20260220114035_UNKNOWN_Issue-#15-/343/202/263/343/203/263/343/203/206/343/202/255/343/202/271/343/203/210/344/272/210/347/256/227API/357/274/210token-budg.md +178 -0
- package/misc/work-notes/plans/20260220133902_UNKNOWN_Issue-#16-/343/202/257/343/203/251/343/202/244/343/202/242/343/203/263/343/203/210/345/210/245/343/203/235/343/203/252/343/202/267/343/203/274/345/261/244/357/274/210r.md +284 -0
- package/misc/work-notes/plans/20260220151356_UNKNOWN_Issue-#28-V9/343/203/236/343/202/244/343/202/260/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/347/265/261/345/220/210/357/274/210#23-+.md +178 -0
- package/misc/work-notes/plans/20260227042805_UNKNOWN_config.json-/343/202/254/343/202/244/343/203/211/346/225/264/345/202/231-+-MCP-resourceprompt-/303/250.md +90 -0
- package/misc/work-notes/plans/20260302113135_UNKNOWN_MCP/343/203/204/343/203/274/343/203/253/343/203/210/343/203/274/343/202/257/343/203/263/343/203/200/343/202/244/343/202/250/343/203/203/343/203/210-(~17,370-/303/242.md +152 -0
- package/misc/work-notes/plans/20260302151940_UNKNOWN_README.md-/347/217/276/350/241/214API/345/217/215/346/230/240/343/202/242/343/203/203/343/203/227/343/203/207/343/203/274/343/203/210.md +87 -0
- package/misc/work-notes/plans/20260302173113_UNKNOWN_watashi-manage-claim-/343/202/250/343/203/251/343/203/274/346/224/271/345/226/204-+-search-summ.md +70 -0
- package/misc/work-notes/plans/20260303091511_UNKNOWN_Issue-#60-entity-type-/347/234/201/347/225/245/345/257/276/345/277/234-/342/200/224-ID/345/206/205/351/203/250/350/247/243.md +205 -0
- package/misc/work-notes/plans/20260304152700_UNKNOWN_Issue-#61-setup-/343/202/263/343/203/236/343/203/263/343/203/211/343/201/247-MCP-/343/202/265/343/203/274/343/203/220/343/203/274/303/247/302/231.md +67 -0
- package/misc/work-notes/plans/20260304170153_UNKNOWN_Issue-#54-user-memoplanissue-/343/201/256-content-/342/206/222-l1-co.md +76 -0
- package/package.json +12 -2
- package/scripts/backfill-search-summary.mjs +219 -0
- package/scripts/migrate-theories.mjs +231 -0
- package/{watashi-db-cowork-plugin.zip → watashi-db-plugin.zip} +0 -0
- package/dist/config/instance.d.ts +0 -10
- package/dist/config/instance.js +0 -39
- package/dist/config/instance.js.map +0 -1
- package/dist/config/types.d.ts +0 -116
- package/dist/config/types.js +0 -37
- package/dist/config/types.js.map +0 -1
package/dist/database/schema.js
CHANGED
|
@@ -11,7 +11,24 @@
|
|
|
11
11
|
// 2026-02-17 修正: V7追加(昇格ツール: promoted status + audit_log拡張)
|
|
12
12
|
// 2026-02-20 修正: V8追加(プロファイルスコアリング: hit_count/last_hit_at カラム追加)
|
|
13
13
|
// 2026-02-20 修正: V9追加(V9マイグレーション統合: Episode FTS拡張 + search_summary + Theory新設 + タイムスタンプ統一)
|
|
14
|
-
|
|
14
|
+
// 2026-02-22 修正: V10追加(FTS5 trigram tokenizer 対応 — 日本語検索改善 Issue #20)
|
|
15
|
+
// 2026-02-22 修正: V11追加(Embedding BLOBカラム追加 — ベクトル検索 Issue #29)
|
|
16
|
+
// 2026-02-23 修正: V12追加(エンティティ分類体系の再設計 Issue #40)
|
|
17
|
+
// 元の実装: SCHEMA_VERSION = 11
|
|
18
|
+
// 2026-02-25 修正: V13追加(横断検索テーブル Issue #30)
|
|
19
|
+
// 2026-02-25 修正: V14追加(evidence_refs カラム追加 Issue #44)
|
|
20
|
+
// 元の実装: SCHEMA_VERSION = 13
|
|
21
|
+
// 2026-02-25 修正: V15追加(Memoテーブル新設 Issue #45)
|
|
22
|
+
// 2026-02-26 修正: V16追加(memos → user_memos リネーム, audit_log CHECK拡張 — Issue #45 Phase 1)
|
|
23
|
+
// 2026-02-28 修正: V17追加(user_input カラム追加 — Issue #56)
|
|
24
|
+
// 2026-02-28 修正: V18追加(user_plans + user_issues テーブル新設 — Issue #57)
|
|
25
|
+
// 2026-03-01 修正: V19追加(Insight l1/l2 拡張 — Issue #53)
|
|
26
|
+
// 2026-03-01 修正: V20追加(全エンティティ l1/l2 カラム拡張 — Issue #59)
|
|
27
|
+
// 2026-03-03 修正: V21追加(audit_log に client_id カラム追加 + backfill)
|
|
28
|
+
// 2026-03-03 修正: V22追加(entity_id インデックス追加 — Issue #60)
|
|
29
|
+
// 2026-03-03 修正: V23追加(Claim に l1_content 追加 + embedding → l1_embedding リネーム — Issue #64)
|
|
30
|
+
// 2026-03-04 修正: V24追加(user_memo/plan/issue の content → l1_content + user_memos に user_input 追加 — Issue #54)
|
|
31
|
+
const SCHEMA_VERSION = 24;
|
|
15
32
|
/**
|
|
16
33
|
* データベーススキーマの初期化とマイグレーション
|
|
17
34
|
*/
|
|
@@ -52,6 +69,51 @@ export function initializeSchema(db) {
|
|
|
52
69
|
if (version < 9) {
|
|
53
70
|
applyV9(db);
|
|
54
71
|
}
|
|
72
|
+
if (version < 10) {
|
|
73
|
+
applyV10(db);
|
|
74
|
+
}
|
|
75
|
+
if (version < 11) {
|
|
76
|
+
applyV11(db);
|
|
77
|
+
}
|
|
78
|
+
if (version < 12) {
|
|
79
|
+
applyV12(db);
|
|
80
|
+
}
|
|
81
|
+
if (version < 13) {
|
|
82
|
+
applyV13(db);
|
|
83
|
+
}
|
|
84
|
+
if (version < 14) {
|
|
85
|
+
applyV14(db);
|
|
86
|
+
}
|
|
87
|
+
if (version < 15) {
|
|
88
|
+
applyV15(db);
|
|
89
|
+
}
|
|
90
|
+
if (version < 16) {
|
|
91
|
+
applyV16(db);
|
|
92
|
+
}
|
|
93
|
+
if (version < 17) {
|
|
94
|
+
applyV17(db);
|
|
95
|
+
}
|
|
96
|
+
if (version < 18) {
|
|
97
|
+
applyV18(db);
|
|
98
|
+
}
|
|
99
|
+
if (version < 19) {
|
|
100
|
+
applyV19(db);
|
|
101
|
+
}
|
|
102
|
+
if (version < 20) {
|
|
103
|
+
applyV20(db);
|
|
104
|
+
}
|
|
105
|
+
if (version < 21) {
|
|
106
|
+
applyV21(db);
|
|
107
|
+
}
|
|
108
|
+
if (version < 22) {
|
|
109
|
+
applyV22(db);
|
|
110
|
+
}
|
|
111
|
+
if (version < 23) {
|
|
112
|
+
applyV23(db);
|
|
113
|
+
}
|
|
114
|
+
if (version < 24) {
|
|
115
|
+
applyV24(db);
|
|
116
|
+
}
|
|
55
117
|
}
|
|
56
118
|
/**
|
|
57
119
|
* V1スキーマ: 初期テーブル構成
|
|
@@ -1184,6 +1246,4142 @@ function applyV9(db) {
|
|
|
1184
1246
|
}
|
|
1185
1247
|
}
|
|
1186
1248
|
}
|
|
1249
|
+
/**
|
|
1250
|
+
* V10マイグレーション: FTS5 trigram tokenizer への移行(Issue #20)
|
|
1251
|
+
* unicode61 → trigram に変更することで日本語CJK文字の部分文字列検索が可能になる。
|
|
1252
|
+
* SQLite 3.34.0+ で trigram tokenizer が利用可能(better-sqlite3 同梱の 3.49.2 で対応済み)。
|
|
1253
|
+
*
|
|
1254
|
+
* 手順:
|
|
1255
|
+
* 1. 12個のFTSトリガーをDROP
|
|
1256
|
+
* 2. 4つのFTSテーブルをDROP
|
|
1257
|
+
* 3. tokenize='trigram' で4テーブルを再作成(カラム構成はV9と同一)
|
|
1258
|
+
* 4. 12個のトリガーを再作成(V9と同構造)
|
|
1259
|
+
* 5. 4テーブルの rebuild コマンド実行
|
|
1260
|
+
* 6. schema_version (10) を記録
|
|
1261
|
+
*/
|
|
1262
|
+
function applyV10(db) {
|
|
1263
|
+
const transaction = db.transaction(() => {
|
|
1264
|
+
// --- 1. 既存FTSトリガーをDROP ---
|
|
1265
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_fts_insert`);
|
|
1266
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_fts_update`);
|
|
1267
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_fts_delete`);
|
|
1268
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_insert`);
|
|
1269
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_update`);
|
|
1270
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_delete`);
|
|
1271
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_insert`);
|
|
1272
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_update`);
|
|
1273
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_delete`);
|
|
1274
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_insert`);
|
|
1275
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_update`);
|
|
1276
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_delete`);
|
|
1277
|
+
// --- 2. 既存FTSテーブルをDROP ---
|
|
1278
|
+
db.exec(`DROP TABLE IF EXISTS claims_fts`);
|
|
1279
|
+
db.exec(`DROP TABLE IF EXISTS episodes_fts`);
|
|
1280
|
+
db.exec(`DROP TABLE IF EXISTS decisions_fts`);
|
|
1281
|
+
db.exec(`DROP TABLE IF EXISTS theories_fts`);
|
|
1282
|
+
// --- 3. trigram tokenizer でFTSテーブルを再作成 ---
|
|
1283
|
+
db.exec(`
|
|
1284
|
+
CREATE VIRTUAL TABLE claims_fts USING fts5(
|
|
1285
|
+
subject, predicate, object, evidence, search_summary,
|
|
1286
|
+
content='claims',
|
|
1287
|
+
content_rowid='rowid',
|
|
1288
|
+
tokenize='trigram'
|
|
1289
|
+
)
|
|
1290
|
+
`);
|
|
1291
|
+
db.exec(`
|
|
1292
|
+
CREATE VIRTUAL TABLE episodes_fts USING fts5(
|
|
1293
|
+
title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary,
|
|
1294
|
+
content='episodes',
|
|
1295
|
+
content_rowid='rowid',
|
|
1296
|
+
tokenize='trigram'
|
|
1297
|
+
)
|
|
1298
|
+
`);
|
|
1299
|
+
db.exec(`
|
|
1300
|
+
CREATE VIRTUAL TABLE decisions_fts USING fts5(
|
|
1301
|
+
title, description, reasoning, search_summary,
|
|
1302
|
+
content='decisions',
|
|
1303
|
+
content_rowid='rowid',
|
|
1304
|
+
tokenize='trigram'
|
|
1305
|
+
)
|
|
1306
|
+
`);
|
|
1307
|
+
db.exec(`
|
|
1308
|
+
CREATE VIRTUAL TABLE theories_fts USING fts5(
|
|
1309
|
+
title, description, core_thesis, principles, tags, search_summary,
|
|
1310
|
+
content='theories',
|
|
1311
|
+
content_rowid='rowid',
|
|
1312
|
+
tokenize='trigram'
|
|
1313
|
+
)
|
|
1314
|
+
`);
|
|
1315
|
+
// --- 4. FTSトリガー再作成(V9と同構造) ---
|
|
1316
|
+
// claims FTSトリガー
|
|
1317
|
+
db.exec(`
|
|
1318
|
+
CREATE TRIGGER claims_fts_insert AFTER INSERT ON claims BEGIN
|
|
1319
|
+
INSERT INTO claims_fts(rowid, subject, predicate, object, evidence, search_summary)
|
|
1320
|
+
VALUES (new.rowid, new.subject, new.predicate, new.object, new.evidence, new.search_summary);
|
|
1321
|
+
END
|
|
1322
|
+
`);
|
|
1323
|
+
db.exec(`
|
|
1324
|
+
CREATE TRIGGER claims_fts_update AFTER UPDATE ON claims BEGIN
|
|
1325
|
+
INSERT INTO claims_fts(claims_fts, rowid, subject, predicate, object, evidence, search_summary)
|
|
1326
|
+
VALUES ('delete', old.rowid, old.subject, old.predicate, old.object, old.evidence, old.search_summary);
|
|
1327
|
+
INSERT INTO claims_fts(rowid, subject, predicate, object, evidence, search_summary)
|
|
1328
|
+
VALUES (new.rowid, new.subject, new.predicate, new.object, new.evidence, new.search_summary);
|
|
1329
|
+
END
|
|
1330
|
+
`);
|
|
1331
|
+
db.exec(`
|
|
1332
|
+
CREATE TRIGGER claims_fts_delete AFTER DELETE ON claims BEGIN
|
|
1333
|
+
INSERT INTO claims_fts(claims_fts, rowid, subject, predicate, object, evidence, search_summary)
|
|
1334
|
+
VALUES ('delete', old.rowid, old.subject, old.predicate, old.object, old.evidence, old.search_summary);
|
|
1335
|
+
END
|
|
1336
|
+
`);
|
|
1337
|
+
// episodes FTSトリガー(JSON配列クリーニング付き)
|
|
1338
|
+
db.exec(`
|
|
1339
|
+
CREATE TRIGGER episodes_fts_insert AFTER INSERT ON episodes BEGIN
|
|
1340
|
+
INSERT INTO episodes_fts(rowid, title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary)
|
|
1341
|
+
VALUES (new.rowid, new.title, new.context, new.trigger,
|
|
1342
|
+
replace(replace(replace(new.principles, '["',''), '"]',''), '","',' '),
|
|
1343
|
+
replace(replace(replace(new.problems, '["',''), '"]',''), '","',' '),
|
|
1344
|
+
replace(replace(replace(new.desires, '["',''), '"]',''), '","',' '),
|
|
1345
|
+
replace(replace(replace(new.decisions, '["',''), '"]',''), '","',' '),
|
|
1346
|
+
replace(replace(replace(new.outcomes, '["',''), '"]',''), '","',' '),
|
|
1347
|
+
new.search_summary);
|
|
1348
|
+
END
|
|
1349
|
+
`);
|
|
1350
|
+
db.exec(`
|
|
1351
|
+
CREATE TRIGGER episodes_fts_update AFTER UPDATE ON episodes BEGIN
|
|
1352
|
+
INSERT INTO episodes_fts(episodes_fts, rowid, title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary)
|
|
1353
|
+
VALUES ('delete', old.rowid, old.title, old.context, old.trigger,
|
|
1354
|
+
replace(replace(replace(old.principles, '["',''), '"]',''), '","',' '),
|
|
1355
|
+
replace(replace(replace(old.problems, '["',''), '"]',''), '","',' '),
|
|
1356
|
+
replace(replace(replace(old.desires, '["',''), '"]',''), '","',' '),
|
|
1357
|
+
replace(replace(replace(old.decisions, '["',''), '"]',''), '","',' '),
|
|
1358
|
+
replace(replace(replace(old.outcomes, '["',''), '"]',''), '","',' '),
|
|
1359
|
+
old.search_summary);
|
|
1360
|
+
INSERT INTO episodes_fts(rowid, title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary)
|
|
1361
|
+
VALUES (new.rowid, new.title, new.context, new.trigger,
|
|
1362
|
+
replace(replace(replace(new.principles, '["',''), '"]',''), '","',' '),
|
|
1363
|
+
replace(replace(replace(new.problems, '["',''), '"]',''), '","',' '),
|
|
1364
|
+
replace(replace(replace(new.desires, '["',''), '"]',''), '","',' '),
|
|
1365
|
+
replace(replace(replace(new.decisions, '["',''), '"]',''), '","',' '),
|
|
1366
|
+
replace(replace(replace(new.outcomes, '["',''), '"]',''), '","',' '),
|
|
1367
|
+
new.search_summary);
|
|
1368
|
+
END
|
|
1369
|
+
`);
|
|
1370
|
+
db.exec(`
|
|
1371
|
+
CREATE TRIGGER episodes_fts_delete AFTER DELETE ON episodes BEGIN
|
|
1372
|
+
INSERT INTO episodes_fts(episodes_fts, rowid, title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary)
|
|
1373
|
+
VALUES ('delete', old.rowid, old.title, old.context, old.trigger,
|
|
1374
|
+
replace(replace(replace(old.principles, '["',''), '"]',''), '","',' '),
|
|
1375
|
+
replace(replace(replace(old.problems, '["',''), '"]',''), '","',' '),
|
|
1376
|
+
replace(replace(replace(old.desires, '["',''), '"]',''), '","',' '),
|
|
1377
|
+
replace(replace(replace(old.decisions, '["',''), '"]',''), '","',' '),
|
|
1378
|
+
replace(replace(replace(old.outcomes, '["',''), '"]',''), '","',' '),
|
|
1379
|
+
old.search_summary);
|
|
1380
|
+
END
|
|
1381
|
+
`);
|
|
1382
|
+
// decisions FTSトリガー
|
|
1383
|
+
db.exec(`
|
|
1384
|
+
CREATE TRIGGER decisions_fts_insert AFTER INSERT ON decisions BEGIN
|
|
1385
|
+
INSERT INTO decisions_fts(rowid, title, description, reasoning, search_summary)
|
|
1386
|
+
VALUES (new.rowid, new.title, new.description, new.reasoning, new.search_summary);
|
|
1387
|
+
END
|
|
1388
|
+
`);
|
|
1389
|
+
db.exec(`
|
|
1390
|
+
CREATE TRIGGER decisions_fts_update AFTER UPDATE ON decisions BEGIN
|
|
1391
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, reasoning, search_summary)
|
|
1392
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.reasoning, old.search_summary);
|
|
1393
|
+
INSERT INTO decisions_fts(rowid, title, description, reasoning, search_summary)
|
|
1394
|
+
VALUES (new.rowid, new.title, new.description, new.reasoning, new.search_summary);
|
|
1395
|
+
END
|
|
1396
|
+
`);
|
|
1397
|
+
db.exec(`
|
|
1398
|
+
CREATE TRIGGER decisions_fts_delete AFTER DELETE ON decisions BEGIN
|
|
1399
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, reasoning, search_summary)
|
|
1400
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.reasoning, old.search_summary);
|
|
1401
|
+
END
|
|
1402
|
+
`);
|
|
1403
|
+
// theories FTSトリガー(JSON配列クリーニング付き)
|
|
1404
|
+
db.exec(`
|
|
1405
|
+
CREATE TRIGGER theories_fts_insert AFTER INSERT ON theories BEGIN
|
|
1406
|
+
INSERT INTO theories_fts(rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1407
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1408
|
+
replace(replace(replace(new.principles, '["',''), '"]',''), '","',' '),
|
|
1409
|
+
replace(replace(replace(new.tags, '["',''), '"]',''), '","',' '),
|
|
1410
|
+
new.search_summary);
|
|
1411
|
+
END
|
|
1412
|
+
`);
|
|
1413
|
+
db.exec(`
|
|
1414
|
+
CREATE TRIGGER theories_fts_update AFTER UPDATE ON theories BEGIN
|
|
1415
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1416
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1417
|
+
replace(replace(replace(old.principles, '["',''), '"]',''), '","',' '),
|
|
1418
|
+
replace(replace(replace(old.tags, '["',''), '"]',''), '","',' '),
|
|
1419
|
+
old.search_summary);
|
|
1420
|
+
INSERT INTO theories_fts(rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1421
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1422
|
+
replace(replace(replace(new.principles, '["',''), '"]',''), '","',' '),
|
|
1423
|
+
replace(replace(replace(new.tags, '["',''), '"]',''), '","',' '),
|
|
1424
|
+
new.search_summary);
|
|
1425
|
+
END
|
|
1426
|
+
`);
|
|
1427
|
+
db.exec(`
|
|
1428
|
+
CREATE TRIGGER theories_fts_delete AFTER DELETE ON theories BEGIN
|
|
1429
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1430
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1431
|
+
replace(replace(replace(old.principles, '["',''), '"]',''), '","',' '),
|
|
1432
|
+
replace(replace(replace(old.tags, '["',''), '"]',''), '","',' '),
|
|
1433
|
+
old.search_summary);
|
|
1434
|
+
END
|
|
1435
|
+
`);
|
|
1436
|
+
// --- 5. 全FTS rebuild ---
|
|
1437
|
+
db.exec(`INSERT INTO claims_fts(claims_fts) VALUES('rebuild')`);
|
|
1438
|
+
db.exec(`INSERT INTO episodes_fts(episodes_fts) VALUES('rebuild')`);
|
|
1439
|
+
db.exec(`INSERT INTO decisions_fts(decisions_fts) VALUES('rebuild')`);
|
|
1440
|
+
db.exec(`INSERT INTO theories_fts(theories_fts) VALUES('rebuild')`);
|
|
1441
|
+
// --- 6. スキーマバージョン記録 ---
|
|
1442
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (10)`);
|
|
1443
|
+
});
|
|
1444
|
+
transaction();
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* V11スキーマ: Embedding BLOBカラム追加(Issue #29: ベクトル検索)
|
|
1448
|
+
* claims, decisions, episodes, theories の4テーブルに embedding BLOB カラムを追加。
|
|
1449
|
+
* NULL許容(Embedding未設定時やAPI失敗時は NULL のまま動作)。
|
|
1450
|
+
*/
|
|
1451
|
+
function applyV11(db) {
|
|
1452
|
+
const transaction = db.transaction(() => {
|
|
1453
|
+
// 各テーブルに embedding カラム追加(存在しない場合のみ)
|
|
1454
|
+
if (!hasColumn(db, "claims", "embedding")) {
|
|
1455
|
+
db.exec(`ALTER TABLE claims ADD COLUMN embedding BLOB`);
|
|
1456
|
+
}
|
|
1457
|
+
if (!hasColumn(db, "decisions", "embedding")) {
|
|
1458
|
+
db.exec(`ALTER TABLE decisions ADD COLUMN embedding BLOB`);
|
|
1459
|
+
}
|
|
1460
|
+
if (!hasColumn(db, "episodes", "embedding")) {
|
|
1461
|
+
db.exec(`ALTER TABLE episodes ADD COLUMN embedding BLOB`);
|
|
1462
|
+
}
|
|
1463
|
+
if (!hasColumn(db, "theories", "embedding")) {
|
|
1464
|
+
db.exec(`ALTER TABLE theories ADD COLUMN embedding BLOB`);
|
|
1465
|
+
}
|
|
1466
|
+
// スキーマバージョン記録
|
|
1467
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (11)`);
|
|
1468
|
+
});
|
|
1469
|
+
transaction();
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* V12スキーマ: エンティティ分類体系の再設計(Issue #40)
|
|
1473
|
+
* - insights テーブル新規作成(Theoryから non_goals, open_questions を除外した軽量版)
|
|
1474
|
+
* - models テーブル新規作成(Theoryと同構造)
|
|
1475
|
+
* - episodes に groomed_at カラム追加
|
|
1476
|
+
* - insights_fts / models_fts(trigram tokenizer)
|
|
1477
|
+
* - FTS同期トリガー(INSERT/UPDATE/DELETE × 2テーブル)
|
|
1478
|
+
* - audit_log の entity_type CHECK に 'insight','model' 追加
|
|
1479
|
+
*/
|
|
1480
|
+
function applyV12(db) {
|
|
1481
|
+
const fkState = db.pragma("foreign_keys");
|
|
1482
|
+
db.pragma("foreign_keys = OFF");
|
|
1483
|
+
try {
|
|
1484
|
+
const transaction = db.transaction(() => {
|
|
1485
|
+
// --- 1. insights テーブル(Theoryから non_goals, open_questions を除外) ---
|
|
1486
|
+
db.exec(`
|
|
1487
|
+
CREATE TABLE IF NOT EXISTS insights (
|
|
1488
|
+
id TEXT PRIMARY KEY,
|
|
1489
|
+
title TEXT NOT NULL,
|
|
1490
|
+
description TEXT,
|
|
1491
|
+
core_thesis TEXT,
|
|
1492
|
+
principles TEXT NOT NULL DEFAULT '[]',
|
|
1493
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
1494
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
1495
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
1496
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
1497
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
1498
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
1499
|
+
search_summary TEXT,
|
|
1500
|
+
embedding BLOB,
|
|
1501
|
+
promoted_from_store TEXT,
|
|
1502
|
+
promoted_from_id TEXT,
|
|
1503
|
+
source_tool TEXT,
|
|
1504
|
+
session_id TEXT,
|
|
1505
|
+
client_name TEXT,
|
|
1506
|
+
client_version TEXT,
|
|
1507
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1508
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1509
|
+
)
|
|
1510
|
+
`);
|
|
1511
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_status ON insights(status)`);
|
|
1512
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_scope ON insights(scope)`);
|
|
1513
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_created ON insights(created_at)`);
|
|
1514
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_updated ON insights(updated_at)`);
|
|
1515
|
+
db.exec(`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`);
|
|
1516
|
+
// --- 2. models テーブル(Theoryと同構造) ---
|
|
1517
|
+
db.exec(`
|
|
1518
|
+
CREATE TABLE IF NOT EXISTS models (
|
|
1519
|
+
id TEXT PRIMARY KEY,
|
|
1520
|
+
title TEXT NOT NULL,
|
|
1521
|
+
description TEXT,
|
|
1522
|
+
core_thesis TEXT,
|
|
1523
|
+
principles TEXT NOT NULL DEFAULT '[]',
|
|
1524
|
+
non_goals TEXT NOT NULL DEFAULT '[]',
|
|
1525
|
+
open_questions TEXT NOT NULL DEFAULT '[]',
|
|
1526
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
1527
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
1528
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
1529
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
1530
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
1531
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
1532
|
+
search_summary TEXT,
|
|
1533
|
+
embedding BLOB,
|
|
1534
|
+
promoted_from_store TEXT,
|
|
1535
|
+
promoted_from_id TEXT,
|
|
1536
|
+
source_tool TEXT,
|
|
1537
|
+
session_id TEXT,
|
|
1538
|
+
client_name TEXT,
|
|
1539
|
+
client_version TEXT,
|
|
1540
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1541
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1542
|
+
)
|
|
1543
|
+
`);
|
|
1544
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_models_status ON models(status)`);
|
|
1545
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_models_scope ON models(scope)`);
|
|
1546
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_models_created ON models(created_at)`);
|
|
1547
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_models_updated ON models(updated_at)`);
|
|
1548
|
+
db.exec(`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`);
|
|
1549
|
+
// --- 3. episodes に groomed_at カラム追加 ---
|
|
1550
|
+
if (!hasColumn(db, "episodes", "groomed_at")) {
|
|
1551
|
+
db.exec(`ALTER TABLE episodes ADD COLUMN groomed_at TEXT`);
|
|
1552
|
+
}
|
|
1553
|
+
// --- 4. insights_fts / models_fts(trigram tokenizer) ---
|
|
1554
|
+
db.exec(`DROP TABLE IF EXISTS insights_fts`);
|
|
1555
|
+
db.exec(`
|
|
1556
|
+
CREATE VIRTUAL TABLE insights_fts USING fts5(
|
|
1557
|
+
title, description, core_thesis, principles, tags, search_summary,
|
|
1558
|
+
content='insights',
|
|
1559
|
+
content_rowid='rowid',
|
|
1560
|
+
tokenize='trigram'
|
|
1561
|
+
)
|
|
1562
|
+
`);
|
|
1563
|
+
db.exec(`DROP TABLE IF EXISTS models_fts`);
|
|
1564
|
+
db.exec(`
|
|
1565
|
+
CREATE VIRTUAL TABLE models_fts USING fts5(
|
|
1566
|
+
title, description, core_thesis, principles, tags, search_summary,
|
|
1567
|
+
content='models',
|
|
1568
|
+
content_rowid='rowid',
|
|
1569
|
+
tokenize='trigram'
|
|
1570
|
+
)
|
|
1571
|
+
`);
|
|
1572
|
+
// --- 5. insights FTS同期トリガー ---
|
|
1573
|
+
db.exec(`
|
|
1574
|
+
CREATE TRIGGER IF NOT EXISTS insights_fts_insert AFTER INSERT ON insights BEGIN
|
|
1575
|
+
INSERT INTO insights_fts(rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1576
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1577
|
+
replace(replace(replace(new.principles, '["',''), '"]',''), '","',' '),
|
|
1578
|
+
replace(replace(replace(new.tags, '["',''), '"]',''), '","',' '),
|
|
1579
|
+
new.search_summary);
|
|
1580
|
+
END
|
|
1581
|
+
`);
|
|
1582
|
+
db.exec(`
|
|
1583
|
+
CREATE TRIGGER IF NOT EXISTS insights_fts_update AFTER UPDATE ON insights BEGIN
|
|
1584
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1585
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1586
|
+
replace(replace(replace(old.principles, '["',''), '"]',''), '","',' '),
|
|
1587
|
+
replace(replace(replace(old.tags, '["',''), '"]',''), '","',' '),
|
|
1588
|
+
old.search_summary);
|
|
1589
|
+
INSERT INTO insights_fts(rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1590
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1591
|
+
replace(replace(replace(new.principles, '["',''), '"]',''), '","',' '),
|
|
1592
|
+
replace(replace(replace(new.tags, '["',''), '"]',''), '","',' '),
|
|
1593
|
+
new.search_summary);
|
|
1594
|
+
END
|
|
1595
|
+
`);
|
|
1596
|
+
db.exec(`
|
|
1597
|
+
CREATE TRIGGER IF NOT EXISTS insights_fts_delete AFTER DELETE ON insights BEGIN
|
|
1598
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1599
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1600
|
+
replace(replace(replace(old.principles, '["',''), '"]',''), '","',' '),
|
|
1601
|
+
replace(replace(replace(old.tags, '["',''), '"]',''), '","',' '),
|
|
1602
|
+
old.search_summary);
|
|
1603
|
+
END
|
|
1604
|
+
`);
|
|
1605
|
+
// --- 6. models FTS同期トリガー ---
|
|
1606
|
+
db.exec(`
|
|
1607
|
+
CREATE TRIGGER IF NOT EXISTS models_fts_insert AFTER INSERT ON models BEGIN
|
|
1608
|
+
INSERT INTO models_fts(rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1609
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1610
|
+
replace(replace(replace(new.principles, '["',''), '"]',''), '","',' '),
|
|
1611
|
+
replace(replace(replace(new.tags, '["',''), '"]',''), '","',' '),
|
|
1612
|
+
new.search_summary);
|
|
1613
|
+
END
|
|
1614
|
+
`);
|
|
1615
|
+
db.exec(`
|
|
1616
|
+
CREATE TRIGGER IF NOT EXISTS models_fts_update AFTER UPDATE ON models BEGIN
|
|
1617
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1618
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1619
|
+
replace(replace(replace(old.principles, '["',''), '"]',''), '","',' '),
|
|
1620
|
+
replace(replace(replace(old.tags, '["',''), '"]',''), '","',' '),
|
|
1621
|
+
old.search_summary);
|
|
1622
|
+
INSERT INTO models_fts(rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1623
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1624
|
+
replace(replace(replace(new.principles, '["',''), '"]',''), '","',' '),
|
|
1625
|
+
replace(replace(replace(new.tags, '["',''), '"]',''), '","',' '),
|
|
1626
|
+
new.search_summary);
|
|
1627
|
+
END
|
|
1628
|
+
`);
|
|
1629
|
+
db.exec(`
|
|
1630
|
+
CREATE TRIGGER IF NOT EXISTS models_fts_delete AFTER DELETE ON models BEGIN
|
|
1631
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, core_thesis, principles, tags, search_summary)
|
|
1632
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1633
|
+
replace(replace(replace(old.principles, '["',''), '"]',''), '","',' '),
|
|
1634
|
+
replace(replace(replace(old.tags, '["',''), '"]',''), '","',' '),
|
|
1635
|
+
old.search_summary);
|
|
1636
|
+
END
|
|
1637
|
+
`);
|
|
1638
|
+
// --- 7. audit_log テーブル再作成(entity_type CHECK に 'insight','model' 追加) ---
|
|
1639
|
+
db.exec(`
|
|
1640
|
+
CREATE TABLE audit_log_new (
|
|
1641
|
+
id TEXT PRIMARY KEY,
|
|
1642
|
+
operation TEXT NOT NULL CHECK(operation IN (
|
|
1643
|
+
'create','update','retract','supersede','reverse','obsolete','archive','promote'
|
|
1644
|
+
)),
|
|
1645
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode','theory','insight','model')),
|
|
1646
|
+
entity_id TEXT NOT NULL,
|
|
1647
|
+
summary TEXT NOT NULL,
|
|
1648
|
+
details TEXT,
|
|
1649
|
+
client_name TEXT,
|
|
1650
|
+
client_version TEXT,
|
|
1651
|
+
session_id TEXT,
|
|
1652
|
+
source_tool TEXT,
|
|
1653
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1654
|
+
)
|
|
1655
|
+
`);
|
|
1656
|
+
db.exec(`INSERT INTO audit_log_new SELECT * FROM audit_log`);
|
|
1657
|
+
db.exec(`DROP TABLE audit_log`);
|
|
1658
|
+
db.exec(`ALTER TABLE audit_log_new RENAME TO audit_log`);
|
|
1659
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_entity ON audit_log(entity_type, entity_id)`);
|
|
1660
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_operation ON audit_log(operation)`);
|
|
1661
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_created ON audit_log(created_at)`);
|
|
1662
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_session ON audit_log(session_id)`);
|
|
1663
|
+
// --- 8. FTS rebuild ---
|
|
1664
|
+
db.exec(`INSERT INTO insights_fts(insights_fts) VALUES('rebuild')`);
|
|
1665
|
+
db.exec(`INSERT INTO models_fts(models_fts) VALUES('rebuild')`);
|
|
1666
|
+
// --- 9. 外部キー整合性チェック ---
|
|
1667
|
+
const fkViolations = db.pragma("foreign_key_check");
|
|
1668
|
+
if (fkViolations.length > 0) {
|
|
1669
|
+
throw new Error(`V12マイグレーション後に外部キー違反が検出されました: ${JSON.stringify(fkViolations)}`);
|
|
1670
|
+
}
|
|
1671
|
+
// --- 10. スキーマバージョン記録 ---
|
|
1672
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (12)`);
|
|
1673
|
+
});
|
|
1674
|
+
transaction();
|
|
1675
|
+
}
|
|
1676
|
+
finally {
|
|
1677
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
1678
|
+
db.pragma("foreign_keys = ON");
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* V13スキーマ: 横断検索テーブル (Issue #30)
|
|
1684
|
+
* unified_search_items + unified_search_fts (trigram)
|
|
1685
|
+
* 全6エンティティを横断してFTS5で一括検索するための2次インデックス。
|
|
1686
|
+
* 元テーブルのINSERT/UPDATE/DELETEトリガーで自動同期。
|
|
1687
|
+
*/
|
|
1688
|
+
function applyV13(db) {
|
|
1689
|
+
const fkState = db.pragma("foreign_keys");
|
|
1690
|
+
db.pragma("foreign_keys = OFF");
|
|
1691
|
+
try {
|
|
1692
|
+
const transaction = db.transaction(() => {
|
|
1693
|
+
// --- 1. unified_search_items テーブル ---
|
|
1694
|
+
db.exec(`
|
|
1695
|
+
CREATE TABLE IF NOT EXISTS unified_search_items (
|
|
1696
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','episode','decision','theory','insight','model')),
|
|
1697
|
+
entity_id TEXT NOT NULL,
|
|
1698
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
1699
|
+
category TEXT,
|
|
1700
|
+
title_summary TEXT NOT NULL,
|
|
1701
|
+
search_text TEXT NOT NULL,
|
|
1702
|
+
search_summary TEXT,
|
|
1703
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
1704
|
+
embedding BLOB,
|
|
1705
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1706
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1707
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
1708
|
+
)
|
|
1709
|
+
`);
|
|
1710
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_scope ON unified_search_items(scope)`);
|
|
1711
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_entity_type ON unified_search_items(entity_type)`);
|
|
1712
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_updated ON unified_search_items(updated_at)`);
|
|
1713
|
+
// --- 2. unified_search_fts(trigram tokenizer) ---
|
|
1714
|
+
db.exec(`DROP TABLE IF EXISTS unified_search_fts`);
|
|
1715
|
+
db.exec(`
|
|
1716
|
+
CREATE VIRTUAL TABLE unified_search_fts USING fts5(
|
|
1717
|
+
title_summary, search_text, search_summary,
|
|
1718
|
+
content='unified_search_items',
|
|
1719
|
+
content_rowid='rowid',
|
|
1720
|
+
tokenize='trigram'
|
|
1721
|
+
)
|
|
1722
|
+
`);
|
|
1723
|
+
// --- 3. FTS同期トリガー(unified_search_items → unified_search_fts) ---
|
|
1724
|
+
db.exec(`
|
|
1725
|
+
CREATE TRIGGER IF NOT EXISTS unified_search_fts_insert AFTER INSERT ON unified_search_items BEGIN
|
|
1726
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
1727
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
1728
|
+
END
|
|
1729
|
+
`);
|
|
1730
|
+
db.exec(`
|
|
1731
|
+
CREATE TRIGGER IF NOT EXISTS unified_search_fts_update AFTER UPDATE ON unified_search_items BEGIN
|
|
1732
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
1733
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
1734
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
1735
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
1736
|
+
END
|
|
1737
|
+
`);
|
|
1738
|
+
db.exec(`
|
|
1739
|
+
CREATE TRIGGER IF NOT EXISTS unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
1740
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
1741
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
1742
|
+
END
|
|
1743
|
+
`);
|
|
1744
|
+
// --- 4. Source table sync triggers ---
|
|
1745
|
+
// JSON配列をスペース区切りテキストに変換するSQLヘルパー
|
|
1746
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
1747
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
1748
|
+
const entities = [
|
|
1749
|
+
{
|
|
1750
|
+
type: "claim",
|
|
1751
|
+
table: "claims",
|
|
1752
|
+
category: (p) => `${p}category`,
|
|
1753
|
+
title: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object`,
|
|
1754
|
+
search: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object || ' ' || ${coal(`${p}evidence`)} || ' ' || ${coal(`${p}falsifier`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
1755
|
+
searchSummary: (p) => `${p}search_summary`,
|
|
1756
|
+
tags: () => `'[]'`,
|
|
1757
|
+
},
|
|
1758
|
+
{
|
|
1759
|
+
type: "episode",
|
|
1760
|
+
table: "episodes",
|
|
1761
|
+
category: () => `NULL`,
|
|
1762
|
+
title: (p) => `${p}title`,
|
|
1763
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}context`)} || ' ' || ${coal(`${p}trigger`)} || ' ' || ${jc(`${p}problems`)} || ' ' || ${jc(`${p}desires`)} || ' ' || ${jc(`${p}decisions`)} || ' ' || ${jc(`${p}outcomes`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
1764
|
+
searchSummary: (p) => `${p}search_summary`,
|
|
1765
|
+
tags: (p) => `${p}tags`,
|
|
1766
|
+
},
|
|
1767
|
+
{
|
|
1768
|
+
type: "decision",
|
|
1769
|
+
table: "decisions",
|
|
1770
|
+
category: () => `NULL`,
|
|
1771
|
+
title: (p) => `${p}title`,
|
|
1772
|
+
search: (p) => `${p}title || ' ' || ${p}description || ' ' || ${p}reasoning || ' ' || ${jc(`${p}alternatives`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
1773
|
+
searchSummary: (p) => `${p}search_summary`,
|
|
1774
|
+
tags: () => `'[]'`,
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
type: "theory",
|
|
1778
|
+
table: "theories",
|
|
1779
|
+
category: () => `NULL`,
|
|
1780
|
+
title: (p) => `${p}title`,
|
|
1781
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
1782
|
+
searchSummary: (p) => `${p}search_summary`,
|
|
1783
|
+
tags: (p) => `${p}tags`,
|
|
1784
|
+
},
|
|
1785
|
+
{
|
|
1786
|
+
type: "insight",
|
|
1787
|
+
table: "insights",
|
|
1788
|
+
category: () => `NULL`,
|
|
1789
|
+
title: (p) => `${p}title`,
|
|
1790
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
1791
|
+
searchSummary: (p) => `${p}search_summary`,
|
|
1792
|
+
tags: (p) => `${p}tags`,
|
|
1793
|
+
},
|
|
1794
|
+
{
|
|
1795
|
+
type: "model",
|
|
1796
|
+
table: "models",
|
|
1797
|
+
category: () => `NULL`,
|
|
1798
|
+
title: (p) => `${p}title`,
|
|
1799
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
1800
|
+
searchSummary: (p) => `${p}search_summary`,
|
|
1801
|
+
tags: (p) => `${p}tags`,
|
|
1802
|
+
},
|
|
1803
|
+
];
|
|
1804
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
1805
|
+
for (const e of entities) {
|
|
1806
|
+
// INSERT trigger: アクティブなレコードのみ横断検索に追加
|
|
1807
|
+
db.exec(`
|
|
1808
|
+
CREATE TRIGGER IF NOT EXISTS ${e.table}_unified_insert AFTER INSERT ON ${e.table}
|
|
1809
|
+
WHEN new.status = 'active' BEGIN
|
|
1810
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1811
|
+
VALUES ('${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, ${e.searchSummary("new.")}, ${e.tags("new.")}, new.created_at, new.updated_at);
|
|
1812
|
+
END
|
|
1813
|
+
`);
|
|
1814
|
+
// UPDATE trigger: 旧エントリを削除 + アクティブなら再挿入(単一トリガーで実行順序を保証)
|
|
1815
|
+
db.exec(`
|
|
1816
|
+
CREATE TRIGGER IF NOT EXISTS ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN
|
|
1817
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
1818
|
+
INSERT INTO unified_search_items(${columns})
|
|
1819
|
+
SELECT '${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, ${e.searchSummary("new.")}, ${e.tags("new.")}, new.created_at, new.updated_at
|
|
1820
|
+
WHERE new.status = 'active';
|
|
1821
|
+
END
|
|
1822
|
+
`);
|
|
1823
|
+
// DELETE trigger: 横断検索から削除
|
|
1824
|
+
db.exec(`
|
|
1825
|
+
CREATE TRIGGER IF NOT EXISTS ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN
|
|
1826
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
1827
|
+
END
|
|
1828
|
+
`);
|
|
1829
|
+
}
|
|
1830
|
+
// --- 5. 既存データからの初期構築 ---
|
|
1831
|
+
for (const e of entities) {
|
|
1832
|
+
db.exec(`
|
|
1833
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
1834
|
+
SELECT '${e.type}', id, scope, ${e.category("")}, ${e.title("")}, ${e.search("")}, ${e.searchSummary("")}, ${e.tags("")}, created_at, updated_at
|
|
1835
|
+
FROM ${e.table}
|
|
1836
|
+
WHERE status = 'active'
|
|
1837
|
+
`);
|
|
1838
|
+
}
|
|
1839
|
+
// --- 6. FTS rebuild ---
|
|
1840
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
1841
|
+
// --- 7. 外部キー整合性チェック ---
|
|
1842
|
+
const fkViolations = db.pragma("foreign_key_check");
|
|
1843
|
+
if (fkViolations.length > 0) {
|
|
1844
|
+
throw new Error(`V13マイグレーション後に外部キー違反が検出されました: ${JSON.stringify(fkViolations)}`);
|
|
1845
|
+
}
|
|
1846
|
+
// --- 8. スキーマバージョン記録 ---
|
|
1847
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (13)`);
|
|
1848
|
+
});
|
|
1849
|
+
transaction();
|
|
1850
|
+
}
|
|
1851
|
+
finally {
|
|
1852
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
1853
|
+
db.pragma("foreign_keys = ON");
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* V14マイグレーション: Theory/Insight/Model に evidence_refs カラム追加 (Issue #44)
|
|
1859
|
+
*/
|
|
1860
|
+
function applyV14(db) {
|
|
1861
|
+
const fkState = db.pragma("foreign_keys");
|
|
1862
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
1863
|
+
db.pragma("foreign_keys = OFF");
|
|
1864
|
+
}
|
|
1865
|
+
try {
|
|
1866
|
+
const transaction = db.transaction(() => {
|
|
1867
|
+
// --- 1. evidence_refs カラム追加 ---
|
|
1868
|
+
for (const table of ["theories", "insights", "models"]) {
|
|
1869
|
+
if (!hasColumn(db, table, "evidence_refs")) {
|
|
1870
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN evidence_refs TEXT NOT NULL DEFAULT '[]'`);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
// --- 2. FTSテーブル再作成(evidence_refs カラム追加) ---
|
|
1874
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
1875
|
+
// theories_fts
|
|
1876
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_insert`);
|
|
1877
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_update`);
|
|
1878
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_delete`);
|
|
1879
|
+
db.exec(`DROP TABLE IF EXISTS theories_fts`);
|
|
1880
|
+
db.exec(`
|
|
1881
|
+
CREATE VIRTUAL TABLE theories_fts USING fts5(
|
|
1882
|
+
title, description, core_thesis, principles, evidence_refs, tags, search_summary,
|
|
1883
|
+
content='theories',
|
|
1884
|
+
content_rowid='rowid',
|
|
1885
|
+
tokenize='trigram'
|
|
1886
|
+
)
|
|
1887
|
+
`);
|
|
1888
|
+
// insights_fts
|
|
1889
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_insert`);
|
|
1890
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_update`);
|
|
1891
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_delete`);
|
|
1892
|
+
db.exec(`DROP TABLE IF EXISTS insights_fts`);
|
|
1893
|
+
db.exec(`
|
|
1894
|
+
CREATE VIRTUAL TABLE insights_fts USING fts5(
|
|
1895
|
+
title, description, core_thesis, principles, evidence_refs, tags, search_summary,
|
|
1896
|
+
content='insights',
|
|
1897
|
+
content_rowid='rowid',
|
|
1898
|
+
tokenize='trigram'
|
|
1899
|
+
)
|
|
1900
|
+
`);
|
|
1901
|
+
// models_fts
|
|
1902
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_insert`);
|
|
1903
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_update`);
|
|
1904
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_delete`);
|
|
1905
|
+
db.exec(`DROP TABLE IF EXISTS models_fts`);
|
|
1906
|
+
db.exec(`
|
|
1907
|
+
CREATE VIRTUAL TABLE models_fts USING fts5(
|
|
1908
|
+
title, description, core_thesis, principles, evidence_refs, tags, search_summary,
|
|
1909
|
+
content='models',
|
|
1910
|
+
content_rowid='rowid',
|
|
1911
|
+
tokenize='trigram'
|
|
1912
|
+
)
|
|
1913
|
+
`);
|
|
1914
|
+
// --- 3. FTS同期トリガー再作成 ---
|
|
1915
|
+
// theories
|
|
1916
|
+
db.exec(`
|
|
1917
|
+
CREATE TRIGGER theories_fts_insert AFTER INSERT ON theories BEGIN
|
|
1918
|
+
INSERT INTO theories_fts(rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1919
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1920
|
+
${jc("new.principles")}, ${jc("new.evidence_refs")},
|
|
1921
|
+
${jc("new.tags")}, new.search_summary);
|
|
1922
|
+
END
|
|
1923
|
+
`);
|
|
1924
|
+
db.exec(`
|
|
1925
|
+
CREATE TRIGGER theories_fts_update AFTER UPDATE ON theories BEGIN
|
|
1926
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1927
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1928
|
+
${jc("old.principles")}, ${jc("old.evidence_refs")},
|
|
1929
|
+
${jc("old.tags")}, old.search_summary);
|
|
1930
|
+
INSERT INTO theories_fts(rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1931
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1932
|
+
${jc("new.principles")}, ${jc("new.evidence_refs")},
|
|
1933
|
+
${jc("new.tags")}, new.search_summary);
|
|
1934
|
+
END
|
|
1935
|
+
`);
|
|
1936
|
+
db.exec(`
|
|
1937
|
+
CREATE TRIGGER theories_fts_delete AFTER DELETE ON theories BEGIN
|
|
1938
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1939
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1940
|
+
${jc("old.principles")}, ${jc("old.evidence_refs")},
|
|
1941
|
+
${jc("old.tags")}, old.search_summary);
|
|
1942
|
+
END
|
|
1943
|
+
`);
|
|
1944
|
+
// insights
|
|
1945
|
+
db.exec(`
|
|
1946
|
+
CREATE TRIGGER insights_fts_insert AFTER INSERT ON insights BEGIN
|
|
1947
|
+
INSERT INTO insights_fts(rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1948
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1949
|
+
${jc("new.principles")}, ${jc("new.evidence_refs")},
|
|
1950
|
+
${jc("new.tags")}, new.search_summary);
|
|
1951
|
+
END
|
|
1952
|
+
`);
|
|
1953
|
+
db.exec(`
|
|
1954
|
+
CREATE TRIGGER insights_fts_update AFTER UPDATE ON insights BEGIN
|
|
1955
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1956
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1957
|
+
${jc("old.principles")}, ${jc("old.evidence_refs")},
|
|
1958
|
+
${jc("old.tags")}, old.search_summary);
|
|
1959
|
+
INSERT INTO insights_fts(rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1960
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1961
|
+
${jc("new.principles")}, ${jc("new.evidence_refs")},
|
|
1962
|
+
${jc("new.tags")}, new.search_summary);
|
|
1963
|
+
END
|
|
1964
|
+
`);
|
|
1965
|
+
db.exec(`
|
|
1966
|
+
CREATE TRIGGER insights_fts_delete AFTER DELETE ON insights BEGIN
|
|
1967
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1968
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1969
|
+
${jc("old.principles")}, ${jc("old.evidence_refs")},
|
|
1970
|
+
${jc("old.tags")}, old.search_summary);
|
|
1971
|
+
END
|
|
1972
|
+
`);
|
|
1973
|
+
// models
|
|
1974
|
+
db.exec(`
|
|
1975
|
+
CREATE TRIGGER models_fts_insert AFTER INSERT ON models BEGIN
|
|
1976
|
+
INSERT INTO models_fts(rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1977
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1978
|
+
${jc("new.principles")}, ${jc("new.evidence_refs")},
|
|
1979
|
+
${jc("new.tags")}, new.search_summary);
|
|
1980
|
+
END
|
|
1981
|
+
`);
|
|
1982
|
+
db.exec(`
|
|
1983
|
+
CREATE TRIGGER models_fts_update AFTER UPDATE ON models BEGIN
|
|
1984
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1985
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1986
|
+
${jc("old.principles")}, ${jc("old.evidence_refs")},
|
|
1987
|
+
${jc("old.tags")}, old.search_summary);
|
|
1988
|
+
INSERT INTO models_fts(rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1989
|
+
VALUES (new.rowid, new.title, new.description, new.core_thesis,
|
|
1990
|
+
${jc("new.principles")}, ${jc("new.evidence_refs")},
|
|
1991
|
+
${jc("new.tags")}, new.search_summary);
|
|
1992
|
+
END
|
|
1993
|
+
`);
|
|
1994
|
+
db.exec(`
|
|
1995
|
+
CREATE TRIGGER models_fts_delete AFTER DELETE ON models BEGIN
|
|
1996
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, core_thesis, principles, evidence_refs, tags, search_summary)
|
|
1997
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.core_thesis,
|
|
1998
|
+
${jc("old.principles")}, ${jc("old.evidence_refs")},
|
|
1999
|
+
${jc("old.tags")}, old.search_summary);
|
|
2000
|
+
END
|
|
2001
|
+
`);
|
|
2002
|
+
// --- 4. unified_search_items トリガー更新(theory/insight/model の search_text に evidence_refs 追加) ---
|
|
2003
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
2004
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
2005
|
+
const updatedEntities = [
|
|
2006
|
+
{
|
|
2007
|
+
type: "theory", table: "theories",
|
|
2008
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
type: "insight", table: "insights",
|
|
2012
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
type: "model", table: "models",
|
|
2016
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2017
|
+
},
|
|
2018
|
+
];
|
|
2019
|
+
for (const e of updatedEntities) {
|
|
2020
|
+
// Drop old triggers
|
|
2021
|
+
db.exec(`DROP TRIGGER IF EXISTS ${e.table}_unified_insert`);
|
|
2022
|
+
db.exec(`DROP TRIGGER IF EXISTS ${e.table}_unified_update`);
|
|
2023
|
+
db.exec(`DROP TRIGGER IF EXISTS ${e.table}_unified_delete`);
|
|
2024
|
+
// Recreate with evidence_refs in search text
|
|
2025
|
+
db.exec(`
|
|
2026
|
+
CREATE TRIGGER ${e.table}_unified_insert AFTER INSERT ON ${e.table}
|
|
2027
|
+
WHEN new.status = 'active' BEGIN
|
|
2028
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
2029
|
+
VALUES ('${e.type}', new.id, new.scope, NULL, new.title, ${e.search("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
2030
|
+
END
|
|
2031
|
+
`);
|
|
2032
|
+
db.exec(`
|
|
2033
|
+
CREATE TRIGGER ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN
|
|
2034
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
2035
|
+
INSERT INTO unified_search_items(${columns})
|
|
2036
|
+
SELECT '${e.type}', new.id, new.scope, NULL, new.title, ${e.search("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
2037
|
+
WHERE new.status = 'active';
|
|
2038
|
+
END
|
|
2039
|
+
`);
|
|
2040
|
+
db.exec(`
|
|
2041
|
+
CREATE TRIGGER ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN
|
|
2042
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
2043
|
+
END
|
|
2044
|
+
`);
|
|
2045
|
+
}
|
|
2046
|
+
// --- 5. FTS + unified_search_items リビルド ---
|
|
2047
|
+
db.exec(`INSERT INTO theories_fts(theories_fts) VALUES('rebuild')`);
|
|
2048
|
+
db.exec(`INSERT INTO insights_fts(insights_fts) VALUES('rebuild')`);
|
|
2049
|
+
db.exec(`INSERT INTO models_fts(models_fts) VALUES('rebuild')`);
|
|
2050
|
+
// unified_search_items の theory/insight/model を再構築
|
|
2051
|
+
for (const e of updatedEntities) {
|
|
2052
|
+
db.exec(`DELETE FROM unified_search_items WHERE entity_type = '${e.type}'`);
|
|
2053
|
+
db.exec(`
|
|
2054
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
2055
|
+
SELECT '${e.type}', id, scope, NULL, title, ${e.search("")}, search_summary, tags, created_at, updated_at
|
|
2056
|
+
FROM ${e.table}
|
|
2057
|
+
WHERE status = 'active'
|
|
2058
|
+
`);
|
|
2059
|
+
}
|
|
2060
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
2061
|
+
// --- 6. スキーマバージョン記録 ---
|
|
2062
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (14)`);
|
|
2063
|
+
});
|
|
2064
|
+
transaction();
|
|
2065
|
+
}
|
|
2066
|
+
finally {
|
|
2067
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
2068
|
+
db.pragma("foreign_keys = ON");
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* V15マイグレーション: Memo テーブル新設 (Issue #45)
|
|
2074
|
+
* 「わたし」の本質ではない参照用知識(秘書の手帳メタファー)を記録するエンティティ。
|
|
2075
|
+
* usage_policy による可視性制御あり。
|
|
2076
|
+
*/
|
|
2077
|
+
function applyV15(db) {
|
|
2078
|
+
const fkState = db.pragma("foreign_keys");
|
|
2079
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
2080
|
+
db.pragma("foreign_keys = OFF");
|
|
2081
|
+
}
|
|
2082
|
+
try {
|
|
2083
|
+
const transaction = db.transaction(() => {
|
|
2084
|
+
// --- 1. memos テーブル ---
|
|
2085
|
+
db.exec(`
|
|
2086
|
+
CREATE TABLE IF NOT EXISTS memos (
|
|
2087
|
+
id TEXT PRIMARY KEY,
|
|
2088
|
+
title TEXT NOT NULL,
|
|
2089
|
+
content TEXT NOT NULL,
|
|
2090
|
+
usage_policy TEXT NOT NULL DEFAULT 'on_request'
|
|
2091
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
2092
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
2093
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
2094
|
+
search_summary TEXT,
|
|
2095
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
2096
|
+
CHECK(status IN ('active','archived')),
|
|
2097
|
+
embedding BLOB,
|
|
2098
|
+
client_name TEXT,
|
|
2099
|
+
client_version TEXT,
|
|
2100
|
+
source_tool TEXT,
|
|
2101
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2102
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2103
|
+
)
|
|
2104
|
+
`);
|
|
2105
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_memos_status ON memos(status)`);
|
|
2106
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_memos_scope ON memos(scope)`);
|
|
2107
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_memos_usage_policy ON memos(usage_policy)`);
|
|
2108
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_memos_updated ON memos(updated_at)`);
|
|
2109
|
+
// --- 2. memos_fts (trigram tokenizer) ---
|
|
2110
|
+
db.exec(`
|
|
2111
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memos_fts USING fts5(
|
|
2112
|
+
title, content, tags, search_summary,
|
|
2113
|
+
content='memos',
|
|
2114
|
+
content_rowid='rowid',
|
|
2115
|
+
tokenize='trigram'
|
|
2116
|
+
)
|
|
2117
|
+
`);
|
|
2118
|
+
// --- 3. FTS同期トリガー ---
|
|
2119
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
2120
|
+
db.exec(`
|
|
2121
|
+
CREATE TRIGGER IF NOT EXISTS memos_fts_insert AFTER INSERT ON memos BEGIN
|
|
2122
|
+
INSERT INTO memos_fts(rowid, title, content, tags, search_summary)
|
|
2123
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
2124
|
+
END
|
|
2125
|
+
`);
|
|
2126
|
+
db.exec(`
|
|
2127
|
+
CREATE TRIGGER IF NOT EXISTS memos_fts_update AFTER UPDATE ON memos BEGIN
|
|
2128
|
+
INSERT INTO memos_fts(memos_fts, rowid, title, content, tags, search_summary)
|
|
2129
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
2130
|
+
INSERT INTO memos_fts(rowid, title, content, tags, search_summary)
|
|
2131
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
2132
|
+
END
|
|
2133
|
+
`);
|
|
2134
|
+
db.exec(`
|
|
2135
|
+
CREATE TRIGGER IF NOT EXISTS memos_fts_delete AFTER DELETE ON memos BEGIN
|
|
2136
|
+
INSERT INTO memos_fts(memos_fts, rowid, title, content, tags, search_summary)
|
|
2137
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
2138
|
+
END
|
|
2139
|
+
`);
|
|
2140
|
+
// --- 4. unified_search_items: CHECK制約を拡張して 'memo' を追加 ---
|
|
2141
|
+
// 既存のFTS・source triggerを全削除→テーブル再作成→全trigger再作成
|
|
2142
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_insert`);
|
|
2143
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_update`);
|
|
2144
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_delete`);
|
|
2145
|
+
for (const table of ["claims", "episodes", "decisions", "theories", "insights", "models"]) {
|
|
2146
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_insert`);
|
|
2147
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_update`);
|
|
2148
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_delete`);
|
|
2149
|
+
}
|
|
2150
|
+
db.exec(`
|
|
2151
|
+
CREATE TABLE unified_search_items_new (
|
|
2152
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','episode','decision','theory','insight','model','memo')),
|
|
2153
|
+
entity_id TEXT NOT NULL,
|
|
2154
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
2155
|
+
category TEXT,
|
|
2156
|
+
title_summary TEXT NOT NULL,
|
|
2157
|
+
search_text TEXT NOT NULL,
|
|
2158
|
+
search_summary TEXT,
|
|
2159
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
2160
|
+
embedding BLOB,
|
|
2161
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2162
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2163
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
2164
|
+
)
|
|
2165
|
+
`);
|
|
2166
|
+
db.exec(`INSERT INTO unified_search_items_new SELECT * FROM unified_search_items`);
|
|
2167
|
+
db.exec(`DROP TABLE unified_search_items`);
|
|
2168
|
+
db.exec(`ALTER TABLE unified_search_items_new RENAME TO unified_search_items`);
|
|
2169
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_scope ON unified_search_items(scope)`);
|
|
2170
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_entity_type ON unified_search_items(entity_type)`);
|
|
2171
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_updated ON unified_search_items(updated_at)`);
|
|
2172
|
+
// unified_search_fts 再作成
|
|
2173
|
+
db.exec(`DROP TABLE IF EXISTS unified_search_fts`);
|
|
2174
|
+
db.exec(`
|
|
2175
|
+
CREATE VIRTUAL TABLE unified_search_fts USING fts5(
|
|
2176
|
+
title_summary, search_text, search_summary,
|
|
2177
|
+
content='unified_search_items',
|
|
2178
|
+
content_rowid='rowid',
|
|
2179
|
+
tokenize='trigram'
|
|
2180
|
+
)
|
|
2181
|
+
`);
|
|
2182
|
+
// FTS同期トリガー再作成
|
|
2183
|
+
db.exec(`
|
|
2184
|
+
CREATE TRIGGER unified_search_fts_insert AFTER INSERT ON unified_search_items BEGIN
|
|
2185
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
2186
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
2187
|
+
END
|
|
2188
|
+
`);
|
|
2189
|
+
db.exec(`
|
|
2190
|
+
CREATE TRIGGER unified_search_fts_update AFTER UPDATE ON unified_search_items BEGIN
|
|
2191
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
2192
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
2193
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
2194
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
2195
|
+
END
|
|
2196
|
+
`);
|
|
2197
|
+
db.exec(`
|
|
2198
|
+
CREATE TRIGGER unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
2199
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
2200
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
2201
|
+
END
|
|
2202
|
+
`);
|
|
2203
|
+
// --- 5. Source table sync triggers 再作成(V13+V14のパターンを統合) ---
|
|
2204
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
2205
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
2206
|
+
const entities = [
|
|
2207
|
+
{
|
|
2208
|
+
type: "claim", table: "claims",
|
|
2209
|
+
category: (p) => `${p}category`,
|
|
2210
|
+
title: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object`,
|
|
2211
|
+
search: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object || ' ' || ${coal(`${p}evidence`)} || ' ' || ${coal(`${p}falsifier`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2212
|
+
tags: () => `'[]'`,
|
|
2213
|
+
},
|
|
2214
|
+
{
|
|
2215
|
+
type: "episode", table: "episodes",
|
|
2216
|
+
category: () => `NULL`,
|
|
2217
|
+
title: (p) => `${p}title`,
|
|
2218
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}context`)} || ' ' || ${coal(`${p}trigger`)} || ' ' || ${jc(`${p}problems`)} || ' ' || ${jc(`${p}desires`)} || ' ' || ${jc(`${p}decisions`)} || ' ' || ${jc(`${p}outcomes`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2219
|
+
tags: (p) => `${p}tags`,
|
|
2220
|
+
},
|
|
2221
|
+
{
|
|
2222
|
+
type: "decision", table: "decisions",
|
|
2223
|
+
category: () => `NULL`,
|
|
2224
|
+
title: (p) => `${p}title`,
|
|
2225
|
+
search: (p) => `${p}title || ' ' || ${p}description || ' ' || ${p}reasoning || ' ' || ${jc(`${p}alternatives`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2226
|
+
tags: () => `'[]'`,
|
|
2227
|
+
},
|
|
2228
|
+
{
|
|
2229
|
+
type: "theory", table: "theories",
|
|
2230
|
+
category: () => `NULL`,
|
|
2231
|
+
title: (p) => `${p}title`,
|
|
2232
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2233
|
+
tags: (p) => `${p}tags`,
|
|
2234
|
+
},
|
|
2235
|
+
{
|
|
2236
|
+
type: "insight", table: "insights",
|
|
2237
|
+
category: () => `NULL`,
|
|
2238
|
+
title: (p) => `${p}title`,
|
|
2239
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2240
|
+
tags: (p) => `${p}tags`,
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
type: "model", table: "models",
|
|
2244
|
+
category: () => `NULL`,
|
|
2245
|
+
title: (p) => `${p}title`,
|
|
2246
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2247
|
+
tags: (p) => `${p}tags`,
|
|
2248
|
+
},
|
|
2249
|
+
{
|
|
2250
|
+
type: "memo", table: "memos",
|
|
2251
|
+
category: () => `NULL`,
|
|
2252
|
+
title: (p) => `${p}title`,
|
|
2253
|
+
search: (p) => `${p}title || ' ' || ${p}content || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2254
|
+
tags: (p) => `${p}tags`,
|
|
2255
|
+
statusCondition: "AND new.usage_policy != 'human_directed'",
|
|
2256
|
+
},
|
|
2257
|
+
];
|
|
2258
|
+
for (const e of entities) {
|
|
2259
|
+
const insertWhen = e.statusCondition
|
|
2260
|
+
? `WHEN new.status = 'active' ${e.statusCondition}`
|
|
2261
|
+
: `WHEN new.status = 'active'`;
|
|
2262
|
+
const updateWhere = e.statusCondition
|
|
2263
|
+
? `WHERE new.status = 'active' ${e.statusCondition}`
|
|
2264
|
+
: `WHERE new.status = 'active'`;
|
|
2265
|
+
db.exec(`
|
|
2266
|
+
CREATE TRIGGER ${e.table}_unified_insert AFTER INSERT ON ${e.table}
|
|
2267
|
+
${insertWhen} BEGIN
|
|
2268
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
2269
|
+
VALUES ('${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, new.search_summary, ${e.tags("new.")}, new.created_at, new.updated_at);
|
|
2270
|
+
END
|
|
2271
|
+
`);
|
|
2272
|
+
db.exec(`
|
|
2273
|
+
CREATE TRIGGER ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN
|
|
2274
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
2275
|
+
INSERT INTO unified_search_items(${columns})
|
|
2276
|
+
SELECT '${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, new.search_summary, ${e.tags("new.")}, new.created_at, new.updated_at
|
|
2277
|
+
${updateWhere};
|
|
2278
|
+
END
|
|
2279
|
+
`);
|
|
2280
|
+
db.exec(`
|
|
2281
|
+
CREATE TRIGGER ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN
|
|
2282
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
2283
|
+
END
|
|
2284
|
+
`);
|
|
2285
|
+
}
|
|
2286
|
+
// --- 6. FTS rebuild ---
|
|
2287
|
+
db.exec(`INSERT INTO memos_fts(memos_fts) VALUES('rebuild')`);
|
|
2288
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
2289
|
+
// --- 7. スキーマバージョン記録 ---
|
|
2290
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (15)`);
|
|
2291
|
+
});
|
|
2292
|
+
transaction();
|
|
2293
|
+
}
|
|
2294
|
+
finally {
|
|
2295
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
2296
|
+
db.pragma("foreign_keys = ON");
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
/**
|
|
2301
|
+
* V16マイグレーション: memos → user_memos リネーム (Issue #45)
|
|
2302
|
+
* user_系テーブル統一のため、memosテーブルをuser_memosにリネーム。
|
|
2303
|
+
* FTS・トリガー・unified_search_items・audit_logsのentity_typeも更新。
|
|
2304
|
+
*/
|
|
2305
|
+
function applyV16(db) {
|
|
2306
|
+
const fkState = db.pragma("foreign_keys");
|
|
2307
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
2308
|
+
db.pragma("foreign_keys = OFF");
|
|
2309
|
+
}
|
|
2310
|
+
try {
|
|
2311
|
+
const transaction = db.transaction(() => {
|
|
2312
|
+
// --- 1. テーブルリネーム ---
|
|
2313
|
+
db.exec(`ALTER TABLE memos RENAME TO user_memos`);
|
|
2314
|
+
// --- 2. インデックス再作成(SQLiteはインデックスリネーム不可) ---
|
|
2315
|
+
db.exec(`DROP INDEX IF EXISTS idx_memos_status`);
|
|
2316
|
+
db.exec(`DROP INDEX IF EXISTS idx_memos_scope`);
|
|
2317
|
+
db.exec(`DROP INDEX IF EXISTS idx_memos_usage_policy`);
|
|
2318
|
+
db.exec(`DROP INDEX IF EXISTS idx_memos_updated`);
|
|
2319
|
+
db.exec(`CREATE INDEX idx_user_memos_status ON user_memos(status)`);
|
|
2320
|
+
db.exec(`CREATE INDEX idx_user_memos_scope ON user_memos(scope)`);
|
|
2321
|
+
db.exec(`CREATE INDEX idx_user_memos_usage_policy ON user_memos(usage_policy)`);
|
|
2322
|
+
db.exec(`CREATE INDEX idx_user_memos_updated ON user_memos(updated_at)`);
|
|
2323
|
+
// --- 3. 旧FTSトリガー・テーブル削除 ---
|
|
2324
|
+
db.exec(`DROP TRIGGER IF EXISTS memos_fts_insert`);
|
|
2325
|
+
db.exec(`DROP TRIGGER IF EXISTS memos_fts_update`);
|
|
2326
|
+
db.exec(`DROP TRIGGER IF EXISTS memos_fts_delete`);
|
|
2327
|
+
db.exec(`DROP TABLE IF EXISTS memos_fts`);
|
|
2328
|
+
// --- 4. 新FTSテーブル作成 ---
|
|
2329
|
+
db.exec(`
|
|
2330
|
+
CREATE VIRTUAL TABLE user_memos_fts USING fts5(
|
|
2331
|
+
title, content, tags, search_summary,
|
|
2332
|
+
content='user_memos',
|
|
2333
|
+
content_rowid='rowid',
|
|
2334
|
+
tokenize='trigram'
|
|
2335
|
+
)
|
|
2336
|
+
`);
|
|
2337
|
+
// --- 5. 新FTSトリガー作成 ---
|
|
2338
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
2339
|
+
db.exec(`
|
|
2340
|
+
CREATE TRIGGER user_memos_fts_insert AFTER INSERT ON user_memos BEGIN
|
|
2341
|
+
INSERT INTO user_memos_fts(rowid, title, content, tags, search_summary)
|
|
2342
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
2343
|
+
END
|
|
2344
|
+
`);
|
|
2345
|
+
db.exec(`
|
|
2346
|
+
CREATE TRIGGER user_memos_fts_update AFTER UPDATE ON user_memos BEGIN
|
|
2347
|
+
INSERT INTO user_memos_fts(user_memos_fts, rowid, title, content, tags, search_summary)
|
|
2348
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
2349
|
+
INSERT INTO user_memos_fts(rowid, title, content, tags, search_summary)
|
|
2350
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
2351
|
+
END
|
|
2352
|
+
`);
|
|
2353
|
+
db.exec(`
|
|
2354
|
+
CREATE TRIGGER user_memos_fts_delete AFTER DELETE ON user_memos BEGIN
|
|
2355
|
+
INSERT INTO user_memos_fts(user_memos_fts, rowid, title, content, tags, search_summary)
|
|
2356
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
2357
|
+
END
|
|
2358
|
+
`);
|
|
2359
|
+
// --- 6. FTSリビルド ---
|
|
2360
|
+
db.exec(`INSERT INTO user_memos_fts(user_memos_fts) VALUES('rebuild')`);
|
|
2361
|
+
// --- 7. unified_search_items: entity_type 'memo' → 'user_memo' ---
|
|
2362
|
+
// unified_search_itemsのCHECK制約を更新するためテーブル再作成が必要
|
|
2363
|
+
// 2026-02-26 修正: 旧CHECK制約を持つテーブルにUPDATEすると制約違反になるため、
|
|
2364
|
+
// 新テーブルに INSERT ... SELECT で変換コピーする方式に変更
|
|
2365
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_insert`);
|
|
2366
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_update`);
|
|
2367
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_delete`);
|
|
2368
|
+
for (const table of ["claims", "episodes", "decisions", "theories", "insights", "models", "memos"]) {
|
|
2369
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_insert`);
|
|
2370
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_update`);
|
|
2371
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_delete`);
|
|
2372
|
+
}
|
|
2373
|
+
db.exec(`
|
|
2374
|
+
CREATE TABLE unified_search_items_new (
|
|
2375
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','episode','decision','theory','insight','model','user_memo')),
|
|
2376
|
+
entity_id TEXT NOT NULL,
|
|
2377
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
2378
|
+
category TEXT,
|
|
2379
|
+
title_summary TEXT NOT NULL,
|
|
2380
|
+
search_text TEXT NOT NULL,
|
|
2381
|
+
search_summary TEXT,
|
|
2382
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
2383
|
+
embedding BLOB,
|
|
2384
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2385
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2386
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
2387
|
+
)
|
|
2388
|
+
`);
|
|
2389
|
+
// entity_type 'memo' → 'user_memo' に変換しながらコピー
|
|
2390
|
+
db.exec(`
|
|
2391
|
+
INSERT INTO unified_search_items_new
|
|
2392
|
+
SELECT
|
|
2393
|
+
CASE WHEN entity_type = 'memo' THEN 'user_memo' ELSE entity_type END,
|
|
2394
|
+
entity_id, scope, category, title_summary, search_text, search_summary, tags, embedding, created_at, updated_at
|
|
2395
|
+
FROM unified_search_items
|
|
2396
|
+
`);
|
|
2397
|
+
db.exec(`DROP TABLE unified_search_items`);
|
|
2398
|
+
db.exec(`ALTER TABLE unified_search_items_new RENAME TO unified_search_items`);
|
|
2399
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_scope ON unified_search_items(scope)`);
|
|
2400
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_entity_type ON unified_search_items(entity_type)`);
|
|
2401
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_updated ON unified_search_items(updated_at)`);
|
|
2402
|
+
// unified_search_fts 再作成
|
|
2403
|
+
db.exec(`DROP TABLE IF EXISTS unified_search_fts`);
|
|
2404
|
+
db.exec(`
|
|
2405
|
+
CREATE VIRTUAL TABLE unified_search_fts USING fts5(
|
|
2406
|
+
title_summary, search_text, search_summary,
|
|
2407
|
+
content='unified_search_items',
|
|
2408
|
+
content_rowid='rowid',
|
|
2409
|
+
tokenize='trigram'
|
|
2410
|
+
)
|
|
2411
|
+
`);
|
|
2412
|
+
// FTS同期トリガー再作成
|
|
2413
|
+
db.exec(`
|
|
2414
|
+
CREATE TRIGGER unified_search_fts_insert AFTER INSERT ON unified_search_items BEGIN
|
|
2415
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
2416
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
2417
|
+
END
|
|
2418
|
+
`);
|
|
2419
|
+
db.exec(`
|
|
2420
|
+
CREATE TRIGGER unified_search_fts_update AFTER UPDATE ON unified_search_items BEGIN
|
|
2421
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
2422
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
2423
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
2424
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
2425
|
+
END
|
|
2426
|
+
`);
|
|
2427
|
+
db.exec(`
|
|
2428
|
+
CREATE TRIGGER unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
2429
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
2430
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
2431
|
+
END
|
|
2432
|
+
`);
|
|
2433
|
+
// --- 8. Source table sync triggers 再作成 ---
|
|
2434
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
2435
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
2436
|
+
const entities = [
|
|
2437
|
+
{
|
|
2438
|
+
type: "claim", table: "claims",
|
|
2439
|
+
category: (p) => `${p}category`,
|
|
2440
|
+
title: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object`,
|
|
2441
|
+
search: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object || ' ' || ${coal(`${p}evidence`)} || ' ' || ${coal(`${p}falsifier`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2442
|
+
tags: () => `'[]'`,
|
|
2443
|
+
},
|
|
2444
|
+
{
|
|
2445
|
+
type: "episode", table: "episodes",
|
|
2446
|
+
category: () => `NULL`,
|
|
2447
|
+
title: (p) => `${p}title`,
|
|
2448
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}context`)} || ' ' || ${coal(`${p}trigger`)} || ' ' || ${jc(`${p}problems`)} || ' ' || ${jc(`${p}desires`)} || ' ' || ${jc(`${p}decisions`)} || ' ' || ${jc(`${p}outcomes`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2449
|
+
tags: (p) => `${p}tags`,
|
|
2450
|
+
},
|
|
2451
|
+
{
|
|
2452
|
+
type: "decision", table: "decisions",
|
|
2453
|
+
category: () => `NULL`,
|
|
2454
|
+
title: (p) => `${p}title`,
|
|
2455
|
+
search: (p) => `${p}title || ' ' || ${p}description || ' ' || ${p}reasoning || ' ' || ${jc(`${p}alternatives`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2456
|
+
tags: () => `'[]'`,
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
type: "theory", table: "theories",
|
|
2460
|
+
category: () => `NULL`,
|
|
2461
|
+
title: (p) => `${p}title`,
|
|
2462
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2463
|
+
tags: (p) => `${p}tags`,
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
type: "insight", table: "insights",
|
|
2467
|
+
category: () => `NULL`,
|
|
2468
|
+
title: (p) => `${p}title`,
|
|
2469
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2470
|
+
tags: (p) => `${p}tags`,
|
|
2471
|
+
},
|
|
2472
|
+
{
|
|
2473
|
+
type: "model", table: "models",
|
|
2474
|
+
category: () => `NULL`,
|
|
2475
|
+
title: (p) => `${p}title`,
|
|
2476
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2477
|
+
tags: (p) => `${p}tags`,
|
|
2478
|
+
},
|
|
2479
|
+
{
|
|
2480
|
+
// 2026-02-25 リネーム: memo → user_memo (Issue #45 Phase 1)
|
|
2481
|
+
type: "user_memo", table: "user_memos",
|
|
2482
|
+
category: () => `NULL`,
|
|
2483
|
+
title: (p) => `${p}title`,
|
|
2484
|
+
search: (p) => `${p}title || ' ' || ${p}content || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2485
|
+
tags: (p) => `${p}tags`,
|
|
2486
|
+
statusCondition: "AND new.usage_policy != 'human_directed'",
|
|
2487
|
+
},
|
|
2488
|
+
];
|
|
2489
|
+
for (const e of entities) {
|
|
2490
|
+
const insertWhen = e.statusCondition
|
|
2491
|
+
? `WHEN new.status = 'active' ${e.statusCondition}`
|
|
2492
|
+
: `WHEN new.status = 'active'`;
|
|
2493
|
+
const updateWhere = e.statusCondition
|
|
2494
|
+
? `WHERE new.status = 'active' ${e.statusCondition}`
|
|
2495
|
+
: `WHERE new.status = 'active'`;
|
|
2496
|
+
db.exec(`
|
|
2497
|
+
CREATE TRIGGER ${e.table}_unified_insert AFTER INSERT ON ${e.table}
|
|
2498
|
+
${insertWhen} BEGIN
|
|
2499
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
2500
|
+
VALUES ('${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, new.search_summary, ${e.tags("new.")}, new.created_at, new.updated_at);
|
|
2501
|
+
END
|
|
2502
|
+
`);
|
|
2503
|
+
db.exec(`
|
|
2504
|
+
CREATE TRIGGER ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN
|
|
2505
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
2506
|
+
INSERT INTO unified_search_items(${columns})
|
|
2507
|
+
SELECT '${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, new.search_summary, ${e.tags("new.")}, new.created_at, new.updated_at
|
|
2508
|
+
${updateWhere};
|
|
2509
|
+
END
|
|
2510
|
+
`);
|
|
2511
|
+
db.exec(`
|
|
2512
|
+
CREATE TRIGGER ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN
|
|
2513
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
2514
|
+
END
|
|
2515
|
+
`);
|
|
2516
|
+
}
|
|
2517
|
+
// --- 9. unified_search_fts リビルド ---
|
|
2518
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
2519
|
+
// --- 10. audit_log: CHECK制約に 'user_memo' を追加 ---
|
|
2520
|
+
// V15でmemoをaudit_log CHECK制約に追加し忘れていたバグも合わせて修正
|
|
2521
|
+
// SQLiteはCHECK制約変更不可のため、テーブル再作成で対応
|
|
2522
|
+
db.exec(`
|
|
2523
|
+
CREATE TABLE audit_log_new (
|
|
2524
|
+
id TEXT PRIMARY KEY,
|
|
2525
|
+
operation TEXT NOT NULL CHECK(operation IN (
|
|
2526
|
+
'create','update','retract','supersede','reverse','obsolete','archive','promote'
|
|
2527
|
+
)),
|
|
2528
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode','theory','insight','model','user_memo')),
|
|
2529
|
+
entity_id TEXT NOT NULL,
|
|
2530
|
+
summary TEXT NOT NULL,
|
|
2531
|
+
details TEXT,
|
|
2532
|
+
client_name TEXT,
|
|
2533
|
+
client_version TEXT,
|
|
2534
|
+
session_id TEXT,
|
|
2535
|
+
source_tool TEXT,
|
|
2536
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2537
|
+
)
|
|
2538
|
+
`);
|
|
2539
|
+
// entity_type 'memo' → 'user_memo' に変換しながらコピー
|
|
2540
|
+
db.exec(`
|
|
2541
|
+
INSERT INTO audit_log_new
|
|
2542
|
+
SELECT id, operation,
|
|
2543
|
+
CASE WHEN entity_type = 'memo' THEN 'user_memo' ELSE entity_type END,
|
|
2544
|
+
entity_id, summary, details, client_name, client_version, session_id, source_tool, created_at
|
|
2545
|
+
FROM audit_log
|
|
2546
|
+
`);
|
|
2547
|
+
db.exec(`DROP TABLE audit_log`);
|
|
2548
|
+
db.exec(`ALTER TABLE audit_log_new RENAME TO audit_log`);
|
|
2549
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_entity ON audit_log(entity_type, entity_id)`);
|
|
2550
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_operation ON audit_log(operation)`);
|
|
2551
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_created ON audit_log(created_at)`);
|
|
2552
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_session ON audit_log(session_id)`);
|
|
2553
|
+
// --- 11. スキーマバージョン記録 ---
|
|
2554
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (16)`);
|
|
2555
|
+
});
|
|
2556
|
+
transaction();
|
|
2557
|
+
}
|
|
2558
|
+
finally {
|
|
2559
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
2560
|
+
db.pragma("foreign_keys = ON");
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
/**
|
|
2565
|
+
* V17スキーマ: user_input カラム追加(Issue #56)
|
|
2566
|
+
* episodes, decisions テーブルにユーザー原文保存用カラムを追加。
|
|
2567
|
+
* FTSインデックスを再構築し user_input を検索対象に含める。
|
|
2568
|
+
* unified_search_items の search_text にも user_input を含める。
|
|
2569
|
+
*/
|
|
2570
|
+
function applyV17(db) {
|
|
2571
|
+
const fkState = db.pragma("foreign_keys");
|
|
2572
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
2573
|
+
db.pragma("foreign_keys = OFF");
|
|
2574
|
+
}
|
|
2575
|
+
try {
|
|
2576
|
+
const transaction = db.transaction(() => {
|
|
2577
|
+
// --- 1. user_input カラム追加 ---
|
|
2578
|
+
if (!hasColumn(db, "episodes", "user_input")) {
|
|
2579
|
+
db.exec(`ALTER TABLE episodes ADD COLUMN user_input TEXT`);
|
|
2580
|
+
}
|
|
2581
|
+
if (!hasColumn(db, "decisions", "user_input")) {
|
|
2582
|
+
db.exec(`ALTER TABLE decisions ADD COLUMN user_input TEXT`);
|
|
2583
|
+
}
|
|
2584
|
+
// --- 2. episodes_fts 再構築(user_input 追加) ---
|
|
2585
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_insert`);
|
|
2586
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_update`);
|
|
2587
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_delete`);
|
|
2588
|
+
db.exec(`DROP TABLE IF EXISTS episodes_fts`);
|
|
2589
|
+
db.exec(`
|
|
2590
|
+
CREATE VIRTUAL TABLE episodes_fts USING fts5(
|
|
2591
|
+
title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary, user_input,
|
|
2592
|
+
content='episodes',
|
|
2593
|
+
content_rowid='rowid',
|
|
2594
|
+
tokenize='trigram'
|
|
2595
|
+
)
|
|
2596
|
+
`);
|
|
2597
|
+
// --- 3. decisions_fts 再構築(user_input 追加) ---
|
|
2598
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_insert`);
|
|
2599
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_update`);
|
|
2600
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_delete`);
|
|
2601
|
+
db.exec(`DROP TABLE IF EXISTS decisions_fts`);
|
|
2602
|
+
db.exec(`
|
|
2603
|
+
CREATE VIRTUAL TABLE decisions_fts USING fts5(
|
|
2604
|
+
title, description, reasoning, search_summary, user_input,
|
|
2605
|
+
content='decisions',
|
|
2606
|
+
content_rowid='rowid',
|
|
2607
|
+
tokenize='trigram'
|
|
2608
|
+
)
|
|
2609
|
+
`);
|
|
2610
|
+
// --- 4. FTS同期トリガー再作成 ---
|
|
2611
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
2612
|
+
// episodes FTSトリガー
|
|
2613
|
+
db.exec(`
|
|
2614
|
+
CREATE TRIGGER episodes_fts_insert AFTER INSERT ON episodes BEGIN
|
|
2615
|
+
INSERT INTO episodes_fts(rowid, title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary, user_input)
|
|
2616
|
+
VALUES (new.rowid, new.title, new.context, new.trigger,
|
|
2617
|
+
${jc("new.principles")}, ${jc("new.problems")}, ${jc("new.desires")},
|
|
2618
|
+
${jc("new.decisions")}, ${jc("new.outcomes")},
|
|
2619
|
+
new.search_summary, new.user_input);
|
|
2620
|
+
END
|
|
2621
|
+
`);
|
|
2622
|
+
db.exec(`
|
|
2623
|
+
CREATE TRIGGER episodes_fts_update AFTER UPDATE ON episodes BEGIN
|
|
2624
|
+
INSERT INTO episodes_fts(episodes_fts, rowid, title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary, user_input)
|
|
2625
|
+
VALUES ('delete', old.rowid, old.title, old.context, old.trigger,
|
|
2626
|
+
${jc("old.principles")}, ${jc("old.problems")}, ${jc("old.desires")},
|
|
2627
|
+
${jc("old.decisions")}, ${jc("old.outcomes")},
|
|
2628
|
+
old.search_summary, old.user_input);
|
|
2629
|
+
INSERT INTO episodes_fts(rowid, title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary, user_input)
|
|
2630
|
+
VALUES (new.rowid, new.title, new.context, new.trigger,
|
|
2631
|
+
${jc("new.principles")}, ${jc("new.problems")}, ${jc("new.desires")},
|
|
2632
|
+
${jc("new.decisions")}, ${jc("new.outcomes")},
|
|
2633
|
+
new.search_summary, new.user_input);
|
|
2634
|
+
END
|
|
2635
|
+
`);
|
|
2636
|
+
db.exec(`
|
|
2637
|
+
CREATE TRIGGER episodes_fts_delete AFTER DELETE ON episodes BEGIN
|
|
2638
|
+
INSERT INTO episodes_fts(episodes_fts, rowid, title, context, trigger, principles, problems, desires, decisions, outcomes, search_summary, user_input)
|
|
2639
|
+
VALUES ('delete', old.rowid, old.title, old.context, old.trigger,
|
|
2640
|
+
${jc("old.principles")}, ${jc("old.problems")}, ${jc("old.desires")},
|
|
2641
|
+
${jc("old.decisions")}, ${jc("old.outcomes")},
|
|
2642
|
+
old.search_summary, old.user_input);
|
|
2643
|
+
END
|
|
2644
|
+
`);
|
|
2645
|
+
// decisions FTSトリガー
|
|
2646
|
+
db.exec(`
|
|
2647
|
+
CREATE TRIGGER decisions_fts_insert AFTER INSERT ON decisions BEGIN
|
|
2648
|
+
INSERT INTO decisions_fts(rowid, title, description, reasoning, search_summary, user_input)
|
|
2649
|
+
VALUES (new.rowid, new.title, new.description, new.reasoning, new.search_summary, new.user_input);
|
|
2650
|
+
END
|
|
2651
|
+
`);
|
|
2652
|
+
db.exec(`
|
|
2653
|
+
CREATE TRIGGER decisions_fts_update AFTER UPDATE ON decisions BEGIN
|
|
2654
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, reasoning, search_summary, user_input)
|
|
2655
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.reasoning, old.search_summary, old.user_input);
|
|
2656
|
+
INSERT INTO decisions_fts(rowid, title, description, reasoning, search_summary, user_input)
|
|
2657
|
+
VALUES (new.rowid, new.title, new.description, new.reasoning, new.search_summary, new.user_input);
|
|
2658
|
+
END
|
|
2659
|
+
`);
|
|
2660
|
+
db.exec(`
|
|
2661
|
+
CREATE TRIGGER decisions_fts_delete AFTER DELETE ON decisions BEGIN
|
|
2662
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, reasoning, search_summary, user_input)
|
|
2663
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.reasoning, old.search_summary, old.user_input);
|
|
2664
|
+
END
|
|
2665
|
+
`);
|
|
2666
|
+
// --- 5. unified_search_items トリガー更新(episode/decision の search_text に user_input 追加) ---
|
|
2667
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
2668
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
2669
|
+
// episode unified トリガー再作成
|
|
2670
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_unified_insert`);
|
|
2671
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_unified_update`);
|
|
2672
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_unified_delete`);
|
|
2673
|
+
const epSearch = (p) => `${p}title || ' ' || ${coal(`${p}context`)} || ' ' || ${coal(`${p}trigger`)} || ' ' || ${jc(`${p}problems`)} || ' ' || ${jc(`${p}desires`)} || ' ' || ${jc(`${p}decisions`)} || ' ' || ${jc(`${p}outcomes`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)} || ' ' || ${coal(`${p}user_input`)}`;
|
|
2674
|
+
db.exec(`
|
|
2675
|
+
CREATE TRIGGER episodes_unified_insert AFTER INSERT ON episodes
|
|
2676
|
+
WHEN new.status = 'active' BEGIN
|
|
2677
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
2678
|
+
VALUES ('episode', new.id, new.scope, NULL, new.title, ${epSearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
2679
|
+
END
|
|
2680
|
+
`);
|
|
2681
|
+
db.exec(`
|
|
2682
|
+
CREATE TRIGGER episodes_unified_update AFTER UPDATE ON episodes BEGIN
|
|
2683
|
+
DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id;
|
|
2684
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
2685
|
+
SELECT 'episode', new.id, new.scope, NULL, new.title, ${epSearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
2686
|
+
WHERE new.status = 'active';
|
|
2687
|
+
END
|
|
2688
|
+
`);
|
|
2689
|
+
db.exec(`
|
|
2690
|
+
CREATE TRIGGER episodes_unified_delete AFTER DELETE ON episodes BEGIN
|
|
2691
|
+
DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id;
|
|
2692
|
+
END
|
|
2693
|
+
`);
|
|
2694
|
+
// decision unified トリガー再作成
|
|
2695
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_unified_insert`);
|
|
2696
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_unified_update`);
|
|
2697
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_unified_delete`);
|
|
2698
|
+
const decSearch = (p) => `${p}title || ' ' || ${p}description || ' ' || ${p}reasoning || ' ' || ${jc(`${p}alternatives`)} || ' ' || ${coal(`${p}search_summary`)} || ' ' || ${coal(`${p}user_input`)}`;
|
|
2699
|
+
db.exec(`
|
|
2700
|
+
CREATE TRIGGER decisions_unified_insert AFTER INSERT ON decisions
|
|
2701
|
+
WHEN new.status = 'active' BEGIN
|
|
2702
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
2703
|
+
VALUES ('decision', new.id, new.scope, NULL, new.title, ${decSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at);
|
|
2704
|
+
END
|
|
2705
|
+
`);
|
|
2706
|
+
db.exec(`
|
|
2707
|
+
CREATE TRIGGER decisions_unified_update AFTER UPDATE ON decisions BEGIN
|
|
2708
|
+
DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id;
|
|
2709
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
2710
|
+
SELECT 'decision', new.id, new.scope, NULL, new.title, ${decSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at
|
|
2711
|
+
WHERE new.status = 'active';
|
|
2712
|
+
END
|
|
2713
|
+
`);
|
|
2714
|
+
db.exec(`
|
|
2715
|
+
CREATE TRIGGER decisions_unified_delete AFTER DELETE ON decisions BEGIN
|
|
2716
|
+
DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id;
|
|
2717
|
+
END
|
|
2718
|
+
`);
|
|
2719
|
+
// --- 6. FTSリビルド ---
|
|
2720
|
+
db.exec(`INSERT INTO episodes_fts(episodes_fts) VALUES('rebuild')`);
|
|
2721
|
+
db.exec(`INSERT INTO decisions_fts(decisions_fts) VALUES('rebuild')`);
|
|
2722
|
+
// --- 7. スキーマバージョン記録 ---
|
|
2723
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (17)`);
|
|
2724
|
+
});
|
|
2725
|
+
transaction();
|
|
2726
|
+
}
|
|
2727
|
+
finally {
|
|
2728
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
2729
|
+
db.pragma("foreign_keys = ON");
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
/**
|
|
2734
|
+
* V18スキーマ: user_plans + user_issues テーブル新設(Issue #57)
|
|
2735
|
+
* セッション間で作業計画を永続化する仕組み + Issue #45 Phase 2-3。
|
|
2736
|
+
* - user_plans: Markdownチェックリスト形式の作業計画(active/completed/archived)
|
|
2737
|
+
* - user_issues: 課題・問題の蓄積(open/closed/archived、コメントスレッド付き)
|
|
2738
|
+
* - FTS5 + unified_search_items 対応
|
|
2739
|
+
* - audit_log CHECK制約拡張
|
|
2740
|
+
*/
|
|
2741
|
+
function applyV18(db) {
|
|
2742
|
+
const fkState = db.pragma("foreign_keys");
|
|
2743
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
2744
|
+
db.pragma("foreign_keys = OFF");
|
|
2745
|
+
}
|
|
2746
|
+
try {
|
|
2747
|
+
const transaction = db.transaction(() => {
|
|
2748
|
+
// --- 1. user_plans テーブル ---
|
|
2749
|
+
db.exec(`
|
|
2750
|
+
CREATE TABLE IF NOT EXISTS user_plans (
|
|
2751
|
+
id TEXT PRIMARY KEY,
|
|
2752
|
+
title TEXT NOT NULL,
|
|
2753
|
+
content TEXT NOT NULL,
|
|
2754
|
+
usage_policy TEXT NOT NULL DEFAULT 'auto'
|
|
2755
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
2756
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
2757
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
2758
|
+
search_summary TEXT,
|
|
2759
|
+
user_input TEXT,
|
|
2760
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
2761
|
+
CHECK(status IN ('active','completed','archived')),
|
|
2762
|
+
embedding BLOB,
|
|
2763
|
+
client_name TEXT,
|
|
2764
|
+
client_version TEXT,
|
|
2765
|
+
source_tool TEXT,
|
|
2766
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2767
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2768
|
+
)
|
|
2769
|
+
`);
|
|
2770
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_plans_status ON user_plans(status)`);
|
|
2771
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_plans_scope ON user_plans(scope)`);
|
|
2772
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_plans_usage_policy ON user_plans(usage_policy)`);
|
|
2773
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_plans_updated ON user_plans(updated_at)`);
|
|
2774
|
+
// --- 2. user_issues テーブル ---
|
|
2775
|
+
db.exec(`
|
|
2776
|
+
CREATE TABLE IF NOT EXISTS user_issues (
|
|
2777
|
+
id TEXT PRIMARY KEY,
|
|
2778
|
+
title TEXT NOT NULL,
|
|
2779
|
+
content TEXT NOT NULL,
|
|
2780
|
+
entries TEXT NOT NULL DEFAULT '[]',
|
|
2781
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
2782
|
+
CHECK(priority IN ('low','medium','high','critical')),
|
|
2783
|
+
usage_policy TEXT NOT NULL DEFAULT 'on_request'
|
|
2784
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
2785
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
2786
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
2787
|
+
search_summary TEXT,
|
|
2788
|
+
user_input TEXT,
|
|
2789
|
+
status TEXT NOT NULL DEFAULT 'open'
|
|
2790
|
+
CHECK(status IN ('open','closed','archived')),
|
|
2791
|
+
embedding BLOB,
|
|
2792
|
+
client_name TEXT,
|
|
2793
|
+
client_version TEXT,
|
|
2794
|
+
source_tool TEXT,
|
|
2795
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2796
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2797
|
+
)
|
|
2798
|
+
`);
|
|
2799
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_issues_status ON user_issues(status)`);
|
|
2800
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_issues_scope ON user_issues(scope)`);
|
|
2801
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_issues_priority ON user_issues(priority)`);
|
|
2802
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_issues_updated ON user_issues(updated_at)`);
|
|
2803
|
+
// --- 3. FTS5テーブル(trigram tokenizer) ---
|
|
2804
|
+
db.exec(`
|
|
2805
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS user_plans_fts USING fts5(
|
|
2806
|
+
title, content, tags, search_summary,
|
|
2807
|
+
content='user_plans',
|
|
2808
|
+
content_rowid='rowid',
|
|
2809
|
+
tokenize='trigram'
|
|
2810
|
+
)
|
|
2811
|
+
`);
|
|
2812
|
+
db.exec(`
|
|
2813
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS user_issues_fts USING fts5(
|
|
2814
|
+
title, content, entries, tags, search_summary,
|
|
2815
|
+
content='user_issues',
|
|
2816
|
+
content_rowid='rowid',
|
|
2817
|
+
tokenize='trigram'
|
|
2818
|
+
)
|
|
2819
|
+
`);
|
|
2820
|
+
// --- 4. FTS同期トリガー ---
|
|
2821
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
2822
|
+
// user_plans FTSトリガー
|
|
2823
|
+
db.exec(`
|
|
2824
|
+
CREATE TRIGGER user_plans_fts_insert AFTER INSERT ON user_plans BEGIN
|
|
2825
|
+
INSERT INTO user_plans_fts(rowid, title, content, tags, search_summary)
|
|
2826
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
2827
|
+
END
|
|
2828
|
+
`);
|
|
2829
|
+
db.exec(`
|
|
2830
|
+
CREATE TRIGGER user_plans_fts_update AFTER UPDATE ON user_plans BEGIN
|
|
2831
|
+
INSERT INTO user_plans_fts(user_plans_fts, rowid, title, content, tags, search_summary)
|
|
2832
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
2833
|
+
INSERT INTO user_plans_fts(rowid, title, content, tags, search_summary)
|
|
2834
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
2835
|
+
END
|
|
2836
|
+
`);
|
|
2837
|
+
db.exec(`
|
|
2838
|
+
CREATE TRIGGER user_plans_fts_delete AFTER DELETE ON user_plans BEGIN
|
|
2839
|
+
INSERT INTO user_plans_fts(user_plans_fts, rowid, title, content, tags, search_summary)
|
|
2840
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
2841
|
+
END
|
|
2842
|
+
`);
|
|
2843
|
+
// user_issues FTSトリガー
|
|
2844
|
+
db.exec(`
|
|
2845
|
+
CREATE TRIGGER user_issues_fts_insert AFTER INSERT ON user_issues BEGIN
|
|
2846
|
+
INSERT INTO user_issues_fts(rowid, title, content, entries, tags, search_summary)
|
|
2847
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.entries")}, ${jc("new.tags")}, new.search_summary);
|
|
2848
|
+
END
|
|
2849
|
+
`);
|
|
2850
|
+
db.exec(`
|
|
2851
|
+
CREATE TRIGGER user_issues_fts_update AFTER UPDATE ON user_issues BEGIN
|
|
2852
|
+
INSERT INTO user_issues_fts(user_issues_fts, rowid, title, content, entries, tags, search_summary)
|
|
2853
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.entries")}, ${jc("old.tags")}, old.search_summary);
|
|
2854
|
+
INSERT INTO user_issues_fts(rowid, title, content, entries, tags, search_summary)
|
|
2855
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.entries")}, ${jc("new.tags")}, new.search_summary);
|
|
2856
|
+
END
|
|
2857
|
+
`);
|
|
2858
|
+
db.exec(`
|
|
2859
|
+
CREATE TRIGGER user_issues_fts_delete AFTER DELETE ON user_issues BEGIN
|
|
2860
|
+
INSERT INTO user_issues_fts(user_issues_fts, rowid, title, content, entries, tags, search_summary)
|
|
2861
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.entries")}, ${jc("old.tags")}, old.search_summary);
|
|
2862
|
+
END
|
|
2863
|
+
`);
|
|
2864
|
+
// --- 5. unified_search_items: CHECK制約を拡張して user_plan, user_issue を追加 ---
|
|
2865
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_insert`);
|
|
2866
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_update`);
|
|
2867
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_delete`);
|
|
2868
|
+
for (const table of ["claims", "episodes", "decisions", "theories", "insights", "models", "user_memos"]) {
|
|
2869
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_insert`);
|
|
2870
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_update`);
|
|
2871
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_delete`);
|
|
2872
|
+
}
|
|
2873
|
+
db.exec(`
|
|
2874
|
+
CREATE TABLE unified_search_items_new (
|
|
2875
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','episode','decision','theory','insight','model','user_memo','user_plan','user_issue')),
|
|
2876
|
+
entity_id TEXT NOT NULL,
|
|
2877
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
2878
|
+
category TEXT,
|
|
2879
|
+
title_summary TEXT NOT NULL,
|
|
2880
|
+
search_text TEXT NOT NULL,
|
|
2881
|
+
search_summary TEXT,
|
|
2882
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
2883
|
+
embedding BLOB,
|
|
2884
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2885
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2886
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
2887
|
+
)
|
|
2888
|
+
`);
|
|
2889
|
+
db.exec(`INSERT INTO unified_search_items_new SELECT * FROM unified_search_items`);
|
|
2890
|
+
db.exec(`DROP TABLE unified_search_items`);
|
|
2891
|
+
db.exec(`ALTER TABLE unified_search_items_new RENAME TO unified_search_items`);
|
|
2892
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_scope ON unified_search_items(scope)`);
|
|
2893
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_entity_type ON unified_search_items(entity_type)`);
|
|
2894
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_unified_search_updated ON unified_search_items(updated_at)`);
|
|
2895
|
+
// unified_search_fts 再作成
|
|
2896
|
+
db.exec(`DROP TABLE IF EXISTS unified_search_fts`);
|
|
2897
|
+
db.exec(`
|
|
2898
|
+
CREATE VIRTUAL TABLE unified_search_fts USING fts5(
|
|
2899
|
+
title_summary, search_text, search_summary,
|
|
2900
|
+
content='unified_search_items',
|
|
2901
|
+
content_rowid='rowid',
|
|
2902
|
+
tokenize='trigram'
|
|
2903
|
+
)
|
|
2904
|
+
`);
|
|
2905
|
+
// FTS同期トリガー再作成
|
|
2906
|
+
db.exec(`
|
|
2907
|
+
CREATE TRIGGER unified_search_fts_insert AFTER INSERT ON unified_search_items BEGIN
|
|
2908
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
2909
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
2910
|
+
END
|
|
2911
|
+
`);
|
|
2912
|
+
db.exec(`
|
|
2913
|
+
CREATE TRIGGER unified_search_fts_update AFTER UPDATE ON unified_search_items BEGIN
|
|
2914
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
2915
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
2916
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
2917
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
2918
|
+
END
|
|
2919
|
+
`);
|
|
2920
|
+
db.exec(`
|
|
2921
|
+
CREATE TRIGGER unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
2922
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
2923
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
2924
|
+
END
|
|
2925
|
+
`);
|
|
2926
|
+
// --- 6. Source table sync triggers 再作成(全エンティティ) ---
|
|
2927
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
2928
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
2929
|
+
const entities = [
|
|
2930
|
+
{
|
|
2931
|
+
type: "claim", table: "claims",
|
|
2932
|
+
category: (p) => `${p}category`,
|
|
2933
|
+
title: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object`,
|
|
2934
|
+
search: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object || ' ' || ${coal(`${p}evidence`)} || ' ' || ${coal(`${p}falsifier`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2935
|
+
tags: () => `'[]'`,
|
|
2936
|
+
},
|
|
2937
|
+
{
|
|
2938
|
+
type: "episode", table: "episodes",
|
|
2939
|
+
category: () => `NULL`,
|
|
2940
|
+
title: (p) => `${p}title`,
|
|
2941
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}context`)} || ' ' || ${coal(`${p}trigger`)} || ' ' || ${jc(`${p}problems`)} || ' ' || ${jc(`${p}desires`)} || ' ' || ${jc(`${p}decisions`)} || ' ' || ${jc(`${p}outcomes`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)} || ' ' || ${coal(`${p}user_input`)}`,
|
|
2942
|
+
tags: (p) => `${p}tags`,
|
|
2943
|
+
},
|
|
2944
|
+
{
|
|
2945
|
+
type: "decision", table: "decisions",
|
|
2946
|
+
category: () => `NULL`,
|
|
2947
|
+
title: (p) => `${p}title`,
|
|
2948
|
+
search: (p) => `${p}title || ' ' || ${p}description || ' ' || ${p}reasoning || ' ' || ${jc(`${p}alternatives`)} || ' ' || ${coal(`${p}search_summary`)} || ' ' || ${coal(`${p}user_input`)}`,
|
|
2949
|
+
tags: () => `'[]'`,
|
|
2950
|
+
},
|
|
2951
|
+
{
|
|
2952
|
+
type: "theory", table: "theories",
|
|
2953
|
+
category: () => `NULL`,
|
|
2954
|
+
title: (p) => `${p}title`,
|
|
2955
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2956
|
+
tags: (p) => `${p}tags`,
|
|
2957
|
+
},
|
|
2958
|
+
{
|
|
2959
|
+
type: "insight", table: "insights",
|
|
2960
|
+
category: () => `NULL`,
|
|
2961
|
+
title: (p) => `${p}title`,
|
|
2962
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2963
|
+
tags: (p) => `${p}tags`,
|
|
2964
|
+
},
|
|
2965
|
+
{
|
|
2966
|
+
type: "model", table: "models",
|
|
2967
|
+
category: () => `NULL`,
|
|
2968
|
+
title: (p) => `${p}title`,
|
|
2969
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}description`)} || ' ' || ${coal(`${p}core_thesis`)} || ' ' || ${jc(`${p}principles`)} || ' ' || ${jc(`${p}evidence_refs`)} || ' ' || ${jc(`${p}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2970
|
+
tags: (p) => `${p}tags`,
|
|
2971
|
+
},
|
|
2972
|
+
{
|
|
2973
|
+
type: "user_memo", table: "user_memos",
|
|
2974
|
+
category: () => `NULL`,
|
|
2975
|
+
title: (p) => `${p}title`,
|
|
2976
|
+
search: (p) => `${p}title || ' ' || ${p}content || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2977
|
+
tags: (p) => `${p}tags`,
|
|
2978
|
+
statusCondition: "AND new.usage_policy != 'human_directed'",
|
|
2979
|
+
},
|
|
2980
|
+
{
|
|
2981
|
+
type: "user_plan", table: "user_plans",
|
|
2982
|
+
category: () => `NULL`,
|
|
2983
|
+
title: (p) => `${p}title`,
|
|
2984
|
+
search: (p) => `${p}title || ' ' || ${p}content || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2985
|
+
tags: (p) => `${p}tags`,
|
|
2986
|
+
statusCondition: "AND new.usage_policy != 'human_directed'",
|
|
2987
|
+
},
|
|
2988
|
+
{
|
|
2989
|
+
type: "user_issue", table: "user_issues",
|
|
2990
|
+
category: () => `NULL`,
|
|
2991
|
+
title: (p) => `${p}title`,
|
|
2992
|
+
search: (p) => `${p}title || ' ' || ${p}content || ' ' || ${jc(`${p}entries`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
2993
|
+
tags: (p) => `${p}tags`,
|
|
2994
|
+
statusField: "open",
|
|
2995
|
+
},
|
|
2996
|
+
];
|
|
2997
|
+
for (const e of entities) {
|
|
2998
|
+
const activeStatus = e.statusField ?? "active";
|
|
2999
|
+
const insertWhen = e.statusCondition
|
|
3000
|
+
? `WHEN new.status = '${activeStatus}' ${e.statusCondition}`
|
|
3001
|
+
: `WHEN new.status = '${activeStatus}'`;
|
|
3002
|
+
const updateWhere = e.statusCondition
|
|
3003
|
+
? `WHERE new.status = '${activeStatus}' ${e.statusCondition}`
|
|
3004
|
+
: `WHERE new.status = '${activeStatus}'`;
|
|
3005
|
+
db.exec(`
|
|
3006
|
+
CREATE TRIGGER ${e.table}_unified_insert AFTER INSERT ON ${e.table}
|
|
3007
|
+
${insertWhen} BEGIN
|
|
3008
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3009
|
+
VALUES ('${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, new.search_summary, ${e.tags("new.")}, new.created_at, new.updated_at);
|
|
3010
|
+
END
|
|
3011
|
+
`);
|
|
3012
|
+
db.exec(`
|
|
3013
|
+
CREATE TRIGGER ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN
|
|
3014
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
3015
|
+
INSERT INTO unified_search_items(${columns})
|
|
3016
|
+
SELECT '${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, new.search_summary, ${e.tags("new.")}, new.created_at, new.updated_at
|
|
3017
|
+
${updateWhere};
|
|
3018
|
+
END
|
|
3019
|
+
`);
|
|
3020
|
+
db.exec(`
|
|
3021
|
+
CREATE TRIGGER ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN
|
|
3022
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
3023
|
+
END
|
|
3024
|
+
`);
|
|
3025
|
+
}
|
|
3026
|
+
// --- 7. audit_log: CHECK制約に user_plan, user_issue を追加 ---
|
|
3027
|
+
db.exec(`
|
|
3028
|
+
CREATE TABLE audit_log_new (
|
|
3029
|
+
id TEXT PRIMARY KEY,
|
|
3030
|
+
operation TEXT NOT NULL CHECK(operation IN (
|
|
3031
|
+
'create','update','retract','supersede','reverse','obsolete','archive','promote'
|
|
3032
|
+
)),
|
|
3033
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','decision','episode','theory','insight','model','user_memo','user_plan','user_issue')),
|
|
3034
|
+
entity_id TEXT NOT NULL,
|
|
3035
|
+
summary TEXT NOT NULL,
|
|
3036
|
+
details TEXT,
|
|
3037
|
+
client_name TEXT,
|
|
3038
|
+
client_version TEXT,
|
|
3039
|
+
session_id TEXT,
|
|
3040
|
+
source_tool TEXT,
|
|
3041
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
3042
|
+
)
|
|
3043
|
+
`);
|
|
3044
|
+
db.exec(`
|
|
3045
|
+
INSERT INTO audit_log_new
|
|
3046
|
+
SELECT id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool, created_at
|
|
3047
|
+
FROM audit_log
|
|
3048
|
+
`);
|
|
3049
|
+
db.exec(`DROP TABLE audit_log`);
|
|
3050
|
+
db.exec(`ALTER TABLE audit_log_new RENAME TO audit_log`);
|
|
3051
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_entity ON audit_log(entity_type, entity_id)`);
|
|
3052
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_operation ON audit_log(operation)`);
|
|
3053
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_created ON audit_log(created_at)`);
|
|
3054
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_log_session ON audit_log(session_id)`);
|
|
3055
|
+
// --- 8. FTSリビルド ---
|
|
3056
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
3057
|
+
// --- 9. スキーマバージョン記録 ---
|
|
3058
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (18)`);
|
|
3059
|
+
});
|
|
3060
|
+
transaction();
|
|
3061
|
+
}
|
|
3062
|
+
finally {
|
|
3063
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
3064
|
+
db.pragma("foreign_keys = ON");
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
/**
|
|
3069
|
+
* V19マイグレーション: Insight l1/l2 拡張 (Issue #53)
|
|
3070
|
+
* - insights テーブル再作成(l1/l2 カラム追加、core_thesis → l2_core_thesis、principles → l2_principles リネーム)
|
|
3071
|
+
* - insights_fts 再作成(l1_content を検索対象に追加)
|
|
3072
|
+
* - unified_search_items の insights トリガー更新(l1_content を search_text に追加)
|
|
3073
|
+
*/
|
|
3074
|
+
function applyV19(db) {
|
|
3075
|
+
const fkState = db.pragma("foreign_keys");
|
|
3076
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
3077
|
+
db.pragma("foreign_keys = OFF");
|
|
3078
|
+
}
|
|
3079
|
+
try {
|
|
3080
|
+
const transaction = db.transaction(() => {
|
|
3081
|
+
// --- 1. insights テーブル再作成(INSERT...SELECT方式) ---
|
|
3082
|
+
db.exec(`ALTER TABLE insights RENAME TO insights_old`);
|
|
3083
|
+
db.exec(`
|
|
3084
|
+
CREATE TABLE insights (
|
|
3085
|
+
-- 1. 識別
|
|
3086
|
+
id TEXT PRIMARY KEY,
|
|
3087
|
+
title TEXT NOT NULL,
|
|
3088
|
+
description TEXT,
|
|
3089
|
+
|
|
3090
|
+
-- 2. L1: 自然言語コンテンツ
|
|
3091
|
+
l1_content TEXT,
|
|
3092
|
+
|
|
3093
|
+
-- 3. L2: 構造化コンテンツ
|
|
3094
|
+
l2_core_thesis TEXT,
|
|
3095
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
3096
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
3097
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
3098
|
+
l2_applicable_context TEXT,
|
|
3099
|
+
|
|
3100
|
+
-- 4. メタデータ
|
|
3101
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
3102
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
3103
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
3104
|
+
search_summary TEXT,
|
|
3105
|
+
embedding BLOB,
|
|
3106
|
+
|
|
3107
|
+
-- 5. 参照
|
|
3108
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
3109
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
3110
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
3111
|
+
|
|
3112
|
+
-- 6. 分類(昇格元)
|
|
3113
|
+
promoted_from_store TEXT,
|
|
3114
|
+
promoted_from_id TEXT,
|
|
3115
|
+
|
|
3116
|
+
-- 7. ライフサイクル
|
|
3117
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
3118
|
+
CHECK(status IN ('active','archived','promoted')),
|
|
3119
|
+
|
|
3120
|
+
-- 8. プロヴナンス
|
|
3121
|
+
source_tool TEXT,
|
|
3122
|
+
session_id TEXT,
|
|
3123
|
+
client_name TEXT,
|
|
3124
|
+
client_version TEXT,
|
|
3125
|
+
|
|
3126
|
+
-- 9. タイムスタンプ
|
|
3127
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
3128
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
3129
|
+
)
|
|
3130
|
+
`);
|
|
3131
|
+
// データ移行: core_thesis → l2_core_thesis, principles → l2_principles, 新カラムはデフォルト値
|
|
3132
|
+
db.exec(`
|
|
3133
|
+
INSERT INTO insights (
|
|
3134
|
+
id, title, description,
|
|
3135
|
+
l1_content,
|
|
3136
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
3137
|
+
tags, scope, confidence, search_summary, embedding,
|
|
3138
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
3139
|
+
promoted_from_store, promoted_from_id,
|
|
3140
|
+
status,
|
|
3141
|
+
source_tool, session_id, client_name, client_version,
|
|
3142
|
+
created_at, updated_at
|
|
3143
|
+
)
|
|
3144
|
+
SELECT
|
|
3145
|
+
id, title, description,
|
|
3146
|
+
NULL,
|
|
3147
|
+
core_thesis, principles, '[]', '[]', NULL,
|
|
3148
|
+
tags, scope, confidence, search_summary, embedding,
|
|
3149
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
3150
|
+
promoted_from_store, promoted_from_id,
|
|
3151
|
+
status,
|
|
3152
|
+
source_tool, session_id, client_name, client_version,
|
|
3153
|
+
created_at, updated_at
|
|
3154
|
+
FROM insights_old
|
|
3155
|
+
`);
|
|
3156
|
+
db.exec(`DROP TABLE insights_old`);
|
|
3157
|
+
// --- 2. インデックス再作成 ---
|
|
3158
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_scope ON insights(scope)`);
|
|
3159
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_status ON insights(status)`);
|
|
3160
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_confidence ON insights(confidence)`);
|
|
3161
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_created ON insights(created_at)`);
|
|
3162
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_insights_updated ON insights(updated_at)`);
|
|
3163
|
+
db.exec(`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`);
|
|
3164
|
+
// --- 3. insights_fts 再構築(l1_content を検索対象に追加) ---
|
|
3165
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
3166
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_insert`);
|
|
3167
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_update`);
|
|
3168
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_delete`);
|
|
3169
|
+
db.exec(`DROP TABLE IF EXISTS insights_fts`);
|
|
3170
|
+
db.exec(`
|
|
3171
|
+
CREATE VIRTUAL TABLE insights_fts USING fts5(
|
|
3172
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
3173
|
+
content='insights',
|
|
3174
|
+
content_rowid='rowid',
|
|
3175
|
+
tokenize='trigram'
|
|
3176
|
+
)
|
|
3177
|
+
`);
|
|
3178
|
+
db.exec(`
|
|
3179
|
+
CREATE TRIGGER insights_fts_insert AFTER INSERT ON insights BEGIN
|
|
3180
|
+
INSERT INTO insights_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3181
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
3182
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")},
|
|
3183
|
+
${jc("new.tags")}, new.search_summary);
|
|
3184
|
+
END
|
|
3185
|
+
`);
|
|
3186
|
+
db.exec(`
|
|
3187
|
+
CREATE TRIGGER insights_fts_update AFTER UPDATE ON insights BEGIN
|
|
3188
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3189
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
3190
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")},
|
|
3191
|
+
${jc("old.tags")}, old.search_summary);
|
|
3192
|
+
INSERT INTO insights_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3193
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
3194
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")},
|
|
3195
|
+
${jc("new.tags")}, new.search_summary);
|
|
3196
|
+
END
|
|
3197
|
+
`);
|
|
3198
|
+
db.exec(`
|
|
3199
|
+
CREATE TRIGGER insights_fts_delete AFTER DELETE ON insights BEGIN
|
|
3200
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3201
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
3202
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")},
|
|
3203
|
+
${jc("old.tags")}, old.search_summary);
|
|
3204
|
+
END
|
|
3205
|
+
`);
|
|
3206
|
+
// --- 4. unified_search_items の insights トリガー更新(l1_content を search_text に追加) ---
|
|
3207
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
3208
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
3209
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_unified_insert`);
|
|
3210
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_unified_update`);
|
|
3211
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_unified_delete`);
|
|
3212
|
+
const insightSearch = (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`)}`;
|
|
3213
|
+
db.exec(`
|
|
3214
|
+
CREATE TRIGGER insights_unified_insert AFTER INSERT ON insights
|
|
3215
|
+
WHEN new.status = 'active' BEGIN
|
|
3216
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3217
|
+
VALUES ('insight', new.id, new.scope, NULL, new.title, ${insightSearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
3218
|
+
END
|
|
3219
|
+
`);
|
|
3220
|
+
db.exec(`
|
|
3221
|
+
CREATE TRIGGER insights_unified_update AFTER UPDATE ON insights BEGIN
|
|
3222
|
+
DELETE FROM unified_search_items WHERE entity_type = 'insight' AND entity_id = old.id;
|
|
3223
|
+
INSERT INTO unified_search_items(${columns})
|
|
3224
|
+
SELECT 'insight', new.id, new.scope, NULL, new.title, ${insightSearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
3225
|
+
WHERE new.status = 'active';
|
|
3226
|
+
END
|
|
3227
|
+
`);
|
|
3228
|
+
db.exec(`
|
|
3229
|
+
CREATE TRIGGER insights_unified_delete AFTER DELETE ON insights BEGIN
|
|
3230
|
+
DELETE FROM unified_search_items WHERE entity_type = 'insight' AND entity_id = old.id;
|
|
3231
|
+
END
|
|
3232
|
+
`);
|
|
3233
|
+
// --- 5. FTS + unified_search_items リビルド ---
|
|
3234
|
+
db.exec(`INSERT INTO insights_fts(insights_fts) VALUES('rebuild')`);
|
|
3235
|
+
// insight の unified_search_items を再構築
|
|
3236
|
+
db.exec(`DELETE FROM unified_search_items WHERE entity_type = 'insight'`);
|
|
3237
|
+
db.exec(`
|
|
3238
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3239
|
+
SELECT 'insight', id, scope, NULL, title, ${insightSearch("")}, search_summary, tags, created_at, updated_at
|
|
3240
|
+
FROM insights
|
|
3241
|
+
WHERE status = 'active'
|
|
3242
|
+
`);
|
|
3243
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
3244
|
+
// --- 6. 外部キー整合性チェック ---
|
|
3245
|
+
const fkViolations = db.pragma("foreign_key_check");
|
|
3246
|
+
if (fkViolations.length > 0) {
|
|
3247
|
+
throw new Error(`V19マイグレーション後に外部キー違反が検出されました: ${JSON.stringify(fkViolations)}`);
|
|
3248
|
+
}
|
|
3249
|
+
// --- 7. スキーマバージョン記録 ---
|
|
3250
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (19)`);
|
|
3251
|
+
});
|
|
3252
|
+
transaction();
|
|
3253
|
+
}
|
|
3254
|
+
finally {
|
|
3255
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
3256
|
+
db.pragma("foreign_keys = ON");
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
/**
|
|
3261
|
+
* V20スキーマ: 全エンティティ l1/l2 カラム拡張(Issue #59)
|
|
3262
|
+
* - Theories/Models: core_thesis → l2_core_thesis, principles → l2_principles + 新l2カラム + l1_content + embedding(Theories)
|
|
3263
|
+
* - Episodes: context → l2_context, trigger → l2_trigger, 他5フィールド → l2_* + l1_content
|
|
3264
|
+
* - Decisions: reasoning → l2_reasoning, alternatives → l2_alternatives + l1_content
|
|
3265
|
+
* - 各テーブルの FTS5 / unified_search_items トリガー再構築
|
|
3266
|
+
*/
|
|
3267
|
+
function applyV20(db) {
|
|
3268
|
+
const fkState = db.pragma("foreign_keys");
|
|
3269
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
3270
|
+
db.pragma("foreign_keys = OFF");
|
|
3271
|
+
}
|
|
3272
|
+
try {
|
|
3273
|
+
const transaction = db.transaction(() => {
|
|
3274
|
+
// JSON配列クリーニングヘルパー
|
|
3275
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
3276
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
3277
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
3278
|
+
// ========================================================
|
|
3279
|
+
// 1-A: Theories テーブル再構築
|
|
3280
|
+
// ========================================================
|
|
3281
|
+
db.exec(`ALTER TABLE theories RENAME TO theories_old`);
|
|
3282
|
+
db.exec(`
|
|
3283
|
+
CREATE TABLE theories (
|
|
3284
|
+
-- 1. 識別
|
|
3285
|
+
id TEXT PRIMARY KEY,
|
|
3286
|
+
title TEXT NOT NULL,
|
|
3287
|
+
description TEXT,
|
|
3288
|
+
|
|
3289
|
+
-- 2. L1: 自然言語コンテンツ
|
|
3290
|
+
l1_content TEXT,
|
|
3291
|
+
|
|
3292
|
+
-- 3. L2: 構造化コンテンツ
|
|
3293
|
+
l2_core_thesis TEXT,
|
|
3294
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
3295
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
3296
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
3297
|
+
l2_applicable_context TEXT,
|
|
3298
|
+
|
|
3299
|
+
-- 4. Theory固有
|
|
3300
|
+
non_goals TEXT NOT NULL DEFAULT '[]',
|
|
3301
|
+
open_questions TEXT NOT NULL DEFAULT '[]',
|
|
3302
|
+
|
|
3303
|
+
-- 5. メタデータ
|
|
3304
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
3305
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
3306
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
3307
|
+
search_summary TEXT,
|
|
3308
|
+
embedding BLOB,
|
|
3309
|
+
|
|
3310
|
+
-- 6. 参照
|
|
3311
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
3312
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
3313
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
3314
|
+
|
|
3315
|
+
-- 7. 分類(昇格元)
|
|
3316
|
+
promoted_from_store TEXT,
|
|
3317
|
+
promoted_from_id TEXT,
|
|
3318
|
+
|
|
3319
|
+
-- 8. ライフサイクル
|
|
3320
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
3321
|
+
CHECK(status IN ('active','archived','promoted')),
|
|
3322
|
+
|
|
3323
|
+
-- 9. プロヴナンス
|
|
3324
|
+
source_tool TEXT,
|
|
3325
|
+
session_id TEXT,
|
|
3326
|
+
client_name TEXT,
|
|
3327
|
+
client_version TEXT,
|
|
3328
|
+
|
|
3329
|
+
-- 10. タイムスタンプ
|
|
3330
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
3331
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
3332
|
+
)
|
|
3333
|
+
`);
|
|
3334
|
+
// データ移行: core_thesis → l2_core_thesis, principles → l2_principles
|
|
3335
|
+
db.exec(`
|
|
3336
|
+
INSERT INTO theories (
|
|
3337
|
+
id, title, description,
|
|
3338
|
+
l1_content,
|
|
3339
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
3340
|
+
non_goals, open_questions,
|
|
3341
|
+
tags, scope, confidence, search_summary, embedding,
|
|
3342
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
3343
|
+
promoted_from_store, promoted_from_id,
|
|
3344
|
+
status,
|
|
3345
|
+
source_tool, session_id, client_name, client_version,
|
|
3346
|
+
created_at, updated_at
|
|
3347
|
+
)
|
|
3348
|
+
SELECT
|
|
3349
|
+
id, title, description,
|
|
3350
|
+
NULL,
|
|
3351
|
+
core_thesis, principles, '[]', '[]', NULL,
|
|
3352
|
+
non_goals, open_questions,
|
|
3353
|
+
tags, scope, confidence, search_summary, NULL,
|
|
3354
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
3355
|
+
promoted_from_store, promoted_from_id,
|
|
3356
|
+
status,
|
|
3357
|
+
source_tool, session_id, client_name, client_version,
|
|
3358
|
+
created_at, updated_at
|
|
3359
|
+
FROM theories_old
|
|
3360
|
+
`);
|
|
3361
|
+
db.exec(`DROP TABLE theories_old`);
|
|
3362
|
+
// theories インデックス
|
|
3363
|
+
db.exec(`CREATE INDEX idx_theories_scope ON theories(scope)`);
|
|
3364
|
+
db.exec(`CREATE INDEX idx_theories_status ON theories(status)`);
|
|
3365
|
+
db.exec(`CREATE INDEX idx_theories_confidence ON theories(confidence)`);
|
|
3366
|
+
db.exec(`CREATE INDEX idx_theories_created ON theories(created_at)`);
|
|
3367
|
+
db.exec(`CREATE INDEX idx_theories_updated ON theories(updated_at)`);
|
|
3368
|
+
db.exec(`CREATE UNIQUE INDEX idx_theories_promoted_from ON theories(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
3369
|
+
// ========================================================
|
|
3370
|
+
// 1-B: Models テーブル再構築(Theoriesと同構造、embeddingは既存値コピー)
|
|
3371
|
+
// ========================================================
|
|
3372
|
+
db.exec(`ALTER TABLE models RENAME TO models_old`);
|
|
3373
|
+
db.exec(`
|
|
3374
|
+
CREATE TABLE models (
|
|
3375
|
+
-- 1. 識別
|
|
3376
|
+
id TEXT PRIMARY KEY,
|
|
3377
|
+
title TEXT NOT NULL,
|
|
3378
|
+
description TEXT,
|
|
3379
|
+
|
|
3380
|
+
-- 2. L1: 自然言語コンテンツ
|
|
3381
|
+
l1_content TEXT,
|
|
3382
|
+
|
|
3383
|
+
-- 3. L2: 構造化コンテンツ
|
|
3384
|
+
l2_core_thesis TEXT,
|
|
3385
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
3386
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
3387
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
3388
|
+
l2_applicable_context TEXT,
|
|
3389
|
+
|
|
3390
|
+
-- 4. Model固有
|
|
3391
|
+
non_goals TEXT NOT NULL DEFAULT '[]',
|
|
3392
|
+
open_questions TEXT NOT NULL DEFAULT '[]',
|
|
3393
|
+
|
|
3394
|
+
-- 5. メタデータ
|
|
3395
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
3396
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
3397
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
3398
|
+
search_summary TEXT,
|
|
3399
|
+
embedding BLOB,
|
|
3400
|
+
|
|
3401
|
+
-- 6. 参照
|
|
3402
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
3403
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
3404
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
3405
|
+
|
|
3406
|
+
-- 7. 分類(昇格元)
|
|
3407
|
+
promoted_from_store TEXT,
|
|
3408
|
+
promoted_from_id TEXT,
|
|
3409
|
+
|
|
3410
|
+
-- 8. ライフサイクル
|
|
3411
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
3412
|
+
CHECK(status IN ('active','archived','promoted')),
|
|
3413
|
+
|
|
3414
|
+
-- 9. プロヴナンス
|
|
3415
|
+
source_tool TEXT,
|
|
3416
|
+
session_id TEXT,
|
|
3417
|
+
client_name TEXT,
|
|
3418
|
+
client_version TEXT,
|
|
3419
|
+
|
|
3420
|
+
-- 10. タイムスタンプ
|
|
3421
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
3422
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
3423
|
+
)
|
|
3424
|
+
`);
|
|
3425
|
+
// データ移行: embeddingは既存値をコピー
|
|
3426
|
+
db.exec(`
|
|
3427
|
+
INSERT INTO models (
|
|
3428
|
+
id, title, description,
|
|
3429
|
+
l1_content,
|
|
3430
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
3431
|
+
non_goals, open_questions,
|
|
3432
|
+
tags, scope, confidence, search_summary, embedding,
|
|
3433
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
3434
|
+
promoted_from_store, promoted_from_id,
|
|
3435
|
+
status,
|
|
3436
|
+
source_tool, session_id, client_name, client_version,
|
|
3437
|
+
created_at, updated_at
|
|
3438
|
+
)
|
|
3439
|
+
SELECT
|
|
3440
|
+
id, title, description,
|
|
3441
|
+
NULL,
|
|
3442
|
+
core_thesis, principles, '[]', '[]', NULL,
|
|
3443
|
+
non_goals, open_questions,
|
|
3444
|
+
tags, scope, confidence, search_summary, embedding,
|
|
3445
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
3446
|
+
promoted_from_store, promoted_from_id,
|
|
3447
|
+
status,
|
|
3448
|
+
source_tool, session_id, client_name, client_version,
|
|
3449
|
+
created_at, updated_at
|
|
3450
|
+
FROM models_old
|
|
3451
|
+
`);
|
|
3452
|
+
db.exec(`DROP TABLE models_old`);
|
|
3453
|
+
// models インデックス
|
|
3454
|
+
db.exec(`CREATE INDEX idx_models_scope ON models(scope)`);
|
|
3455
|
+
db.exec(`CREATE INDEX idx_models_status ON models(status)`);
|
|
3456
|
+
db.exec(`CREATE INDEX idx_models_confidence ON models(confidence)`);
|
|
3457
|
+
db.exec(`CREATE INDEX idx_models_created ON models(created_at)`);
|
|
3458
|
+
db.exec(`CREATE INDEX idx_models_updated ON models(updated_at)`);
|
|
3459
|
+
db.exec(`CREATE UNIQUE INDEX idx_models_promoted_from ON models(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
3460
|
+
// ========================================================
|
|
3461
|
+
// 1-C: Episodes テーブル再構築
|
|
3462
|
+
// ========================================================
|
|
3463
|
+
db.exec(`ALTER TABLE episodes RENAME TO episodes_old`);
|
|
3464
|
+
db.exec(`
|
|
3465
|
+
CREATE TABLE episodes (
|
|
3466
|
+
-- 1. 識別
|
|
3467
|
+
id TEXT PRIMARY KEY,
|
|
3468
|
+
title TEXT NOT NULL,
|
|
3469
|
+
|
|
3470
|
+
-- 2. L1: 自然言語コンテンツ
|
|
3471
|
+
l1_content TEXT,
|
|
3472
|
+
|
|
3473
|
+
-- 3. L2: 構造化コンテンツ
|
|
3474
|
+
l2_context TEXT,
|
|
3475
|
+
l2_trigger TEXT,
|
|
3476
|
+
l2_problems TEXT NOT NULL DEFAULT '[]',
|
|
3477
|
+
l2_desires TEXT NOT NULL DEFAULT '[]',
|
|
3478
|
+
l2_decisions TEXT NOT NULL DEFAULT '[]',
|
|
3479
|
+
l2_outcomes TEXT NOT NULL DEFAULT '[]',
|
|
3480
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
3481
|
+
|
|
3482
|
+
-- 4. メタデータ
|
|
3483
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
3484
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
3485
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
3486
|
+
search_summary TEXT,
|
|
3487
|
+
embedding BLOB,
|
|
3488
|
+
|
|
3489
|
+
-- 5. 分類(昇格元)
|
|
3490
|
+
promoted_from_store TEXT,
|
|
3491
|
+
promoted_from_id TEXT,
|
|
3492
|
+
|
|
3493
|
+
-- 6. ライフサイクル
|
|
3494
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
3495
|
+
CHECK(status IN ('active','archived','promoted')),
|
|
3496
|
+
groomed_at TEXT,
|
|
3497
|
+
|
|
3498
|
+
-- 7. プロヴナンス
|
|
3499
|
+
source_tool TEXT,
|
|
3500
|
+
session_id TEXT,
|
|
3501
|
+
client_name TEXT,
|
|
3502
|
+
client_version TEXT,
|
|
3503
|
+
user_input TEXT,
|
|
3504
|
+
|
|
3505
|
+
-- 8. タイムスタンプ
|
|
3506
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
3507
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
3508
|
+
)
|
|
3509
|
+
`);
|
|
3510
|
+
// データ移行: 7フィールドリネーム
|
|
3511
|
+
db.exec(`
|
|
3512
|
+
INSERT INTO episodes (
|
|
3513
|
+
id, title,
|
|
3514
|
+
l1_content,
|
|
3515
|
+
l2_context, l2_trigger, l2_problems, l2_desires, l2_decisions, l2_outcomes, l2_principles,
|
|
3516
|
+
evidence_refs, tags, scope, search_summary, embedding,
|
|
3517
|
+
promoted_from_store, promoted_from_id,
|
|
3518
|
+
status, groomed_at,
|
|
3519
|
+
source_tool, session_id, client_name, client_version, user_input,
|
|
3520
|
+
created_at, updated_at
|
|
3521
|
+
)
|
|
3522
|
+
SELECT
|
|
3523
|
+
id, title,
|
|
3524
|
+
NULL,
|
|
3525
|
+
context, trigger, problems, desires, decisions, outcomes, principles,
|
|
3526
|
+
evidence_refs, tags, scope, search_summary, embedding,
|
|
3527
|
+
promoted_from_store, promoted_from_id,
|
|
3528
|
+
status, groomed_at,
|
|
3529
|
+
source_tool, session_id, client_name, client_version, user_input,
|
|
3530
|
+
created_at, updated_at
|
|
3531
|
+
FROM episodes_old
|
|
3532
|
+
`);
|
|
3533
|
+
db.exec(`DROP TABLE episodes_old`);
|
|
3534
|
+
// episodes インデックス
|
|
3535
|
+
db.exec(`CREATE INDEX idx_episodes_scope ON episodes(scope)`);
|
|
3536
|
+
db.exec(`CREATE INDEX idx_episodes_status ON episodes(status)`);
|
|
3537
|
+
db.exec(`CREATE INDEX idx_episodes_groomed ON episodes(groomed_at)`);
|
|
3538
|
+
db.exec(`CREATE INDEX idx_episodes_created ON episodes(created_at)`);
|
|
3539
|
+
db.exec(`CREATE INDEX idx_episodes_updated ON episodes(updated_at)`);
|
|
3540
|
+
db.exec(`CREATE UNIQUE INDEX idx_episodes_promoted_from ON episodes(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
3541
|
+
// ========================================================
|
|
3542
|
+
// 1-D: Decisions テーブル再構築
|
|
3543
|
+
// ========================================================
|
|
3544
|
+
db.exec(`ALTER TABLE decisions RENAME TO decisions_old`);
|
|
3545
|
+
db.exec(`
|
|
3546
|
+
CREATE TABLE decisions (
|
|
3547
|
+
-- 1. 識別
|
|
3548
|
+
id TEXT PRIMARY KEY,
|
|
3549
|
+
title TEXT NOT NULL,
|
|
3550
|
+
description TEXT NOT NULL,
|
|
3551
|
+
|
|
3552
|
+
-- 2. L1: 自然言語コンテンツ
|
|
3553
|
+
l1_content TEXT,
|
|
3554
|
+
|
|
3555
|
+
-- 3. L2: 構造化コンテンツ
|
|
3556
|
+
l2_reasoning TEXT NOT NULL,
|
|
3557
|
+
l2_alternatives TEXT NOT NULL DEFAULT '[]',
|
|
3558
|
+
|
|
3559
|
+
-- 4. メタデータ
|
|
3560
|
+
related_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
3561
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
3562
|
+
search_summary TEXT,
|
|
3563
|
+
embedding BLOB,
|
|
3564
|
+
|
|
3565
|
+
-- 5. 分類(昇格元)
|
|
3566
|
+
promoted_from_store TEXT,
|
|
3567
|
+
promoted_from_id TEXT,
|
|
3568
|
+
|
|
3569
|
+
-- 6. ライフサイクル
|
|
3570
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
3571
|
+
CHECK(status IN ('active','reversed','obsolete','promoted')),
|
|
3572
|
+
|
|
3573
|
+
-- 7. プロヴナンス
|
|
3574
|
+
source_tool TEXT,
|
|
3575
|
+
client_name TEXT,
|
|
3576
|
+
client_version TEXT,
|
|
3577
|
+
user_input TEXT,
|
|
3578
|
+
|
|
3579
|
+
-- 8. タイムスタンプ
|
|
3580
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
3581
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
3582
|
+
)
|
|
3583
|
+
`);
|
|
3584
|
+
// データ移行: reasoning → l2_reasoning, alternatives → l2_alternatives
|
|
3585
|
+
db.exec(`
|
|
3586
|
+
INSERT INTO decisions (
|
|
3587
|
+
id, title, description,
|
|
3588
|
+
l1_content,
|
|
3589
|
+
l2_reasoning, l2_alternatives,
|
|
3590
|
+
related_claim_ids, scope, search_summary, embedding,
|
|
3591
|
+
promoted_from_store, promoted_from_id,
|
|
3592
|
+
status,
|
|
3593
|
+
source_tool, client_name, client_version, user_input,
|
|
3594
|
+
created_at, updated_at
|
|
3595
|
+
)
|
|
3596
|
+
SELECT
|
|
3597
|
+
id, title, description,
|
|
3598
|
+
NULL,
|
|
3599
|
+
reasoning, alternatives,
|
|
3600
|
+
related_claim_ids, scope, search_summary, embedding,
|
|
3601
|
+
promoted_from_store, promoted_from_id,
|
|
3602
|
+
status,
|
|
3603
|
+
source_tool, client_name, client_version, user_input,
|
|
3604
|
+
created_at, updated_at
|
|
3605
|
+
FROM decisions_old
|
|
3606
|
+
`);
|
|
3607
|
+
db.exec(`DROP TABLE decisions_old`);
|
|
3608
|
+
// decisions インデックス
|
|
3609
|
+
db.exec(`CREATE INDEX idx_decisions_scope ON decisions(scope)`);
|
|
3610
|
+
db.exec(`CREATE INDEX idx_decisions_status ON decisions(status)`);
|
|
3611
|
+
db.exec(`CREATE INDEX idx_decisions_created ON decisions(created_at)`);
|
|
3612
|
+
db.exec(`CREATE INDEX idx_decisions_updated ON decisions(updated_at)`);
|
|
3613
|
+
db.exec(`CREATE UNIQUE INDEX idx_decisions_promoted_from ON decisions(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
3614
|
+
// ========================================================
|
|
3615
|
+
// 1-E: FTS 再構築(4テーブル)
|
|
3616
|
+
// ========================================================
|
|
3617
|
+
// --- theories_fts ---
|
|
3618
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_insert`);
|
|
3619
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_update`);
|
|
3620
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_delete`);
|
|
3621
|
+
db.exec(`DROP TABLE IF EXISTS theories_fts`);
|
|
3622
|
+
db.exec(`
|
|
3623
|
+
CREATE VIRTUAL TABLE theories_fts USING fts5(
|
|
3624
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
3625
|
+
content='theories',
|
|
3626
|
+
content_rowid='rowid',
|
|
3627
|
+
tokenize='trigram'
|
|
3628
|
+
)
|
|
3629
|
+
`);
|
|
3630
|
+
db.exec(`
|
|
3631
|
+
CREATE TRIGGER theories_fts_insert AFTER INSERT ON theories BEGIN
|
|
3632
|
+
INSERT INTO theories_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3633
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
3634
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")},
|
|
3635
|
+
${jc("new.tags")}, new.search_summary);
|
|
3636
|
+
END
|
|
3637
|
+
`);
|
|
3638
|
+
db.exec(`
|
|
3639
|
+
CREATE TRIGGER theories_fts_update AFTER UPDATE ON theories BEGIN
|
|
3640
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3641
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
3642
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")},
|
|
3643
|
+
${jc("old.tags")}, old.search_summary);
|
|
3644
|
+
INSERT INTO theories_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3645
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
3646
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")},
|
|
3647
|
+
${jc("new.tags")}, new.search_summary);
|
|
3648
|
+
END
|
|
3649
|
+
`);
|
|
3650
|
+
db.exec(`
|
|
3651
|
+
CREATE TRIGGER theories_fts_delete AFTER DELETE ON theories BEGIN
|
|
3652
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3653
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
3654
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")},
|
|
3655
|
+
${jc("old.tags")}, old.search_summary);
|
|
3656
|
+
END
|
|
3657
|
+
`);
|
|
3658
|
+
// --- models_fts ---
|
|
3659
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_insert`);
|
|
3660
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_update`);
|
|
3661
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_delete`);
|
|
3662
|
+
db.exec(`DROP TABLE IF EXISTS models_fts`);
|
|
3663
|
+
db.exec(`
|
|
3664
|
+
CREATE VIRTUAL TABLE models_fts USING fts5(
|
|
3665
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
3666
|
+
content='models',
|
|
3667
|
+
content_rowid='rowid',
|
|
3668
|
+
tokenize='trigram'
|
|
3669
|
+
)
|
|
3670
|
+
`);
|
|
3671
|
+
db.exec(`
|
|
3672
|
+
CREATE TRIGGER models_fts_insert AFTER INSERT ON models BEGIN
|
|
3673
|
+
INSERT INTO models_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3674
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
3675
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")},
|
|
3676
|
+
${jc("new.tags")}, new.search_summary);
|
|
3677
|
+
END
|
|
3678
|
+
`);
|
|
3679
|
+
db.exec(`
|
|
3680
|
+
CREATE TRIGGER models_fts_update AFTER UPDATE ON models BEGIN
|
|
3681
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3682
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
3683
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")},
|
|
3684
|
+
${jc("old.tags")}, old.search_summary);
|
|
3685
|
+
INSERT INTO models_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3686
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
3687
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")},
|
|
3688
|
+
${jc("new.tags")}, new.search_summary);
|
|
3689
|
+
END
|
|
3690
|
+
`);
|
|
3691
|
+
db.exec(`
|
|
3692
|
+
CREATE TRIGGER models_fts_delete AFTER DELETE ON models BEGIN
|
|
3693
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
3694
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
3695
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")},
|
|
3696
|
+
${jc("old.tags")}, old.search_summary);
|
|
3697
|
+
END
|
|
3698
|
+
`);
|
|
3699
|
+
// --- episodes_fts ---
|
|
3700
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_insert`);
|
|
3701
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_update`);
|
|
3702
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_delete`);
|
|
3703
|
+
db.exec(`DROP TABLE IF EXISTS episodes_fts`);
|
|
3704
|
+
db.exec(`
|
|
3705
|
+
CREATE VIRTUAL TABLE episodes_fts USING fts5(
|
|
3706
|
+
title, l1_content, l2_context, l2_trigger, l2_principles, l2_problems, l2_desires, l2_decisions, l2_outcomes, search_summary, user_input,
|
|
3707
|
+
content='episodes',
|
|
3708
|
+
content_rowid='rowid',
|
|
3709
|
+
tokenize='trigram'
|
|
3710
|
+
)
|
|
3711
|
+
`);
|
|
3712
|
+
db.exec(`
|
|
3713
|
+
CREATE TRIGGER episodes_fts_insert AFTER INSERT ON episodes BEGIN
|
|
3714
|
+
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)
|
|
3715
|
+
VALUES (new.rowid, new.title, new.l1_content, new.l2_context, new.l2_trigger,
|
|
3716
|
+
${jc("new.l2_principles")}, ${jc("new.l2_problems")}, ${jc("new.l2_desires")},
|
|
3717
|
+
${jc("new.l2_decisions")}, ${jc("new.l2_outcomes")},
|
|
3718
|
+
new.search_summary, new.user_input);
|
|
3719
|
+
END
|
|
3720
|
+
`);
|
|
3721
|
+
db.exec(`
|
|
3722
|
+
CREATE TRIGGER episodes_fts_update AFTER UPDATE ON episodes BEGIN
|
|
3723
|
+
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)
|
|
3724
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, old.l2_context, old.l2_trigger,
|
|
3725
|
+
${jc("old.l2_principles")}, ${jc("old.l2_problems")}, ${jc("old.l2_desires")},
|
|
3726
|
+
${jc("old.l2_decisions")}, ${jc("old.l2_outcomes")},
|
|
3727
|
+
old.search_summary, old.user_input);
|
|
3728
|
+
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)
|
|
3729
|
+
VALUES (new.rowid, new.title, new.l1_content, new.l2_context, new.l2_trigger,
|
|
3730
|
+
${jc("new.l2_principles")}, ${jc("new.l2_problems")}, ${jc("new.l2_desires")},
|
|
3731
|
+
${jc("new.l2_decisions")}, ${jc("new.l2_outcomes")},
|
|
3732
|
+
new.search_summary, new.user_input);
|
|
3733
|
+
END
|
|
3734
|
+
`);
|
|
3735
|
+
db.exec(`
|
|
3736
|
+
CREATE TRIGGER episodes_fts_delete AFTER DELETE ON episodes BEGIN
|
|
3737
|
+
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)
|
|
3738
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, old.l2_context, old.l2_trigger,
|
|
3739
|
+
${jc("old.l2_principles")}, ${jc("old.l2_problems")}, ${jc("old.l2_desires")},
|
|
3740
|
+
${jc("old.l2_decisions")}, ${jc("old.l2_outcomes")},
|
|
3741
|
+
old.search_summary, old.user_input);
|
|
3742
|
+
END
|
|
3743
|
+
`);
|
|
3744
|
+
// --- decisions_fts ---
|
|
3745
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_insert`);
|
|
3746
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_update`);
|
|
3747
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_delete`);
|
|
3748
|
+
db.exec(`DROP TABLE IF EXISTS decisions_fts`);
|
|
3749
|
+
db.exec(`
|
|
3750
|
+
CREATE VIRTUAL TABLE decisions_fts USING fts5(
|
|
3751
|
+
title, description, l1_content, l2_reasoning, search_summary, user_input,
|
|
3752
|
+
content='decisions',
|
|
3753
|
+
content_rowid='rowid',
|
|
3754
|
+
tokenize='trigram'
|
|
3755
|
+
)
|
|
3756
|
+
`);
|
|
3757
|
+
db.exec(`
|
|
3758
|
+
CREATE TRIGGER decisions_fts_insert AFTER INSERT ON decisions BEGIN
|
|
3759
|
+
INSERT INTO decisions_fts(rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
3760
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_reasoning, new.search_summary, new.user_input);
|
|
3761
|
+
END
|
|
3762
|
+
`);
|
|
3763
|
+
db.exec(`
|
|
3764
|
+
CREATE TRIGGER decisions_fts_update AFTER UPDATE ON decisions BEGIN
|
|
3765
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
3766
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_reasoning, old.search_summary, old.user_input);
|
|
3767
|
+
INSERT INTO decisions_fts(rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
3768
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_reasoning, new.search_summary, new.user_input);
|
|
3769
|
+
END
|
|
3770
|
+
`);
|
|
3771
|
+
db.exec(`
|
|
3772
|
+
CREATE TRIGGER decisions_fts_delete AFTER DELETE ON decisions BEGIN
|
|
3773
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
3774
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_reasoning, old.search_summary, old.user_input);
|
|
3775
|
+
END
|
|
3776
|
+
`);
|
|
3777
|
+
// ========================================================
|
|
3778
|
+
// 1-F: unified_search_items トリガー再構築(4テーブル)
|
|
3779
|
+
// ========================================================
|
|
3780
|
+
// --- theories ---
|
|
3781
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_unified_insert`);
|
|
3782
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_unified_update`);
|
|
3783
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_unified_delete`);
|
|
3784
|
+
const theorySearch = (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}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
3785
|
+
db.exec(`
|
|
3786
|
+
CREATE TRIGGER theories_unified_insert AFTER INSERT ON theories
|
|
3787
|
+
WHEN new.status = 'active' BEGIN
|
|
3788
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3789
|
+
VALUES ('theory', new.id, new.scope, NULL, new.title, ${theorySearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
3790
|
+
END
|
|
3791
|
+
`);
|
|
3792
|
+
db.exec(`
|
|
3793
|
+
CREATE TRIGGER theories_unified_update AFTER UPDATE ON theories BEGIN
|
|
3794
|
+
DELETE FROM unified_search_items WHERE entity_type = 'theory' AND entity_id = old.id;
|
|
3795
|
+
INSERT INTO unified_search_items(${columns})
|
|
3796
|
+
SELECT 'theory', new.id, new.scope, NULL, new.title, ${theorySearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
3797
|
+
WHERE new.status = 'active';
|
|
3798
|
+
END
|
|
3799
|
+
`);
|
|
3800
|
+
db.exec(`
|
|
3801
|
+
CREATE TRIGGER theories_unified_delete AFTER DELETE ON theories BEGIN
|
|
3802
|
+
DELETE FROM unified_search_items WHERE entity_type = 'theory' AND entity_id = old.id;
|
|
3803
|
+
END
|
|
3804
|
+
`);
|
|
3805
|
+
// --- models ---
|
|
3806
|
+
db.exec(`DROP TRIGGER IF EXISTS models_unified_insert`);
|
|
3807
|
+
db.exec(`DROP TRIGGER IF EXISTS models_unified_update`);
|
|
3808
|
+
db.exec(`DROP TRIGGER IF EXISTS models_unified_delete`);
|
|
3809
|
+
const modelSearch = (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}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`;
|
|
3810
|
+
db.exec(`
|
|
3811
|
+
CREATE TRIGGER models_unified_insert AFTER INSERT ON models
|
|
3812
|
+
WHEN new.status = 'active' BEGIN
|
|
3813
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3814
|
+
VALUES ('model', new.id, new.scope, NULL, new.title, ${modelSearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
3815
|
+
END
|
|
3816
|
+
`);
|
|
3817
|
+
db.exec(`
|
|
3818
|
+
CREATE TRIGGER models_unified_update AFTER UPDATE ON models BEGIN
|
|
3819
|
+
DELETE FROM unified_search_items WHERE entity_type = 'model' AND entity_id = old.id;
|
|
3820
|
+
INSERT INTO unified_search_items(${columns})
|
|
3821
|
+
SELECT 'model', new.id, new.scope, NULL, new.title, ${modelSearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
3822
|
+
WHERE new.status = 'active';
|
|
3823
|
+
END
|
|
3824
|
+
`);
|
|
3825
|
+
db.exec(`
|
|
3826
|
+
CREATE TRIGGER models_unified_delete AFTER DELETE ON models BEGIN
|
|
3827
|
+
DELETE FROM unified_search_items WHERE entity_type = 'model' AND entity_id = old.id;
|
|
3828
|
+
END
|
|
3829
|
+
`);
|
|
3830
|
+
// --- episodes ---
|
|
3831
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_unified_insert`);
|
|
3832
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_unified_update`);
|
|
3833
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_unified_delete`);
|
|
3834
|
+
const epSearch = (p) => `${p}title || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}l2_context`)} || ' ' || ${coal(`${p}l2_trigger`)} || ' ' || ${jc(`${p}l2_problems`)} || ' ' || ${jc(`${p}l2_desires`)} || ' ' || ${jc(`${p}l2_decisions`)} || ' ' || ${jc(`${p}l2_outcomes`)} || ' ' || ${jc(`${p}l2_principles`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)} || ' ' || ${coal(`${p}user_input`)}`;
|
|
3835
|
+
db.exec(`
|
|
3836
|
+
CREATE TRIGGER episodes_unified_insert AFTER INSERT ON episodes
|
|
3837
|
+
WHEN new.status = 'active' BEGIN
|
|
3838
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3839
|
+
VALUES ('episode', new.id, new.scope, NULL, new.title, ${epSearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at);
|
|
3840
|
+
END
|
|
3841
|
+
`);
|
|
3842
|
+
db.exec(`
|
|
3843
|
+
CREATE TRIGGER episodes_unified_update AFTER UPDATE ON episodes BEGIN
|
|
3844
|
+
DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id;
|
|
3845
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3846
|
+
SELECT 'episode', new.id, new.scope, NULL, new.title, ${epSearch("new.")}, new.search_summary, new.tags, new.created_at, new.updated_at
|
|
3847
|
+
WHERE new.status = 'active';
|
|
3848
|
+
END
|
|
3849
|
+
`);
|
|
3850
|
+
db.exec(`
|
|
3851
|
+
CREATE TRIGGER episodes_unified_delete AFTER DELETE ON episodes BEGIN
|
|
3852
|
+
DELETE FROM unified_search_items WHERE entity_type = 'episode' AND entity_id = old.id;
|
|
3853
|
+
END
|
|
3854
|
+
`);
|
|
3855
|
+
// --- decisions ---
|
|
3856
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_unified_insert`);
|
|
3857
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_unified_update`);
|
|
3858
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_unified_delete`);
|
|
3859
|
+
const decSearch = (p) => `${p}title || ' ' || ${p}description || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${p}l2_reasoning || ' ' || ${jc(`${p}l2_alternatives`)} || ' ' || ${coal(`${p}search_summary`)} || ' ' || ${coal(`${p}user_input`)}`;
|
|
3860
|
+
db.exec(`
|
|
3861
|
+
CREATE TRIGGER decisions_unified_insert AFTER INSERT ON decisions
|
|
3862
|
+
WHEN new.status = 'active' BEGIN
|
|
3863
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3864
|
+
VALUES ('decision', new.id, new.scope, NULL, new.title, ${decSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at);
|
|
3865
|
+
END
|
|
3866
|
+
`);
|
|
3867
|
+
db.exec(`
|
|
3868
|
+
CREATE TRIGGER decisions_unified_update AFTER UPDATE ON decisions BEGIN
|
|
3869
|
+
DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id;
|
|
3870
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3871
|
+
SELECT 'decision', new.id, new.scope, NULL, new.title, ${decSearch("new.")}, new.search_summary, '[]', new.created_at, new.updated_at
|
|
3872
|
+
WHERE new.status = 'active';
|
|
3873
|
+
END
|
|
3874
|
+
`);
|
|
3875
|
+
db.exec(`
|
|
3876
|
+
CREATE TRIGGER decisions_unified_delete AFTER DELETE ON decisions BEGIN
|
|
3877
|
+
DELETE FROM unified_search_items WHERE entity_type = 'decision' AND entity_id = old.id;
|
|
3878
|
+
END
|
|
3879
|
+
`);
|
|
3880
|
+
// ========================================================
|
|
3881
|
+
// 1-G: FTS + unified_search_items リビルド
|
|
3882
|
+
// ========================================================
|
|
3883
|
+
db.exec(`INSERT INTO theories_fts(theories_fts) VALUES('rebuild')`);
|
|
3884
|
+
db.exec(`INSERT INTO models_fts(models_fts) VALUES('rebuild')`);
|
|
3885
|
+
db.exec(`INSERT INTO episodes_fts(episodes_fts) VALUES('rebuild')`);
|
|
3886
|
+
db.exec(`INSERT INTO decisions_fts(decisions_fts) VALUES('rebuild')`);
|
|
3887
|
+
// unified_search_items の4エンティティを再構築
|
|
3888
|
+
for (const e of [
|
|
3889
|
+
{ type: "theory", table: "theories", search: theorySearch },
|
|
3890
|
+
{ type: "model", table: "models", search: modelSearch },
|
|
3891
|
+
{ type: "episode", table: "episodes", search: epSearch },
|
|
3892
|
+
{ type: "decision", table: "decisions", search: decSearch },
|
|
3893
|
+
]) {
|
|
3894
|
+
db.exec(`DELETE FROM unified_search_items WHERE entity_type = '${e.type}'`);
|
|
3895
|
+
db.exec(`
|
|
3896
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
3897
|
+
SELECT '${e.type}', id, scope, NULL, title, ${e.search("")}, search_summary, ${e.type === "decision" ? "'[]'" : "tags"}, created_at, updated_at
|
|
3898
|
+
FROM ${e.table}
|
|
3899
|
+
WHERE status = 'active'
|
|
3900
|
+
`);
|
|
3901
|
+
}
|
|
3902
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
3903
|
+
// --- 外部キー整合性チェック ---
|
|
3904
|
+
const fkViolations = db.pragma("foreign_key_check");
|
|
3905
|
+
if (fkViolations.length > 0) {
|
|
3906
|
+
throw new Error(`V20マイグレーション後に外部キー違反が検出されました: ${JSON.stringify(fkViolations)}`);
|
|
3907
|
+
}
|
|
3908
|
+
// --- スキーマバージョン記録 ---
|
|
3909
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (20)`);
|
|
3910
|
+
});
|
|
3911
|
+
transaction();
|
|
3912
|
+
}
|
|
3913
|
+
finally {
|
|
3914
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
3915
|
+
db.pragma("foreign_keys = ON");
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
/**
|
|
3920
|
+
* V21: audit_log に client_id カラム追加 + 既存レコードの backfill
|
|
3921
|
+
* どのデバイスで作られたレコードか追跡するため。
|
|
3922
|
+
* sync で複数デバイスのデータが混在する前に backfill することで、
|
|
3923
|
+
* 既存データにもデバイス情報を付与する。
|
|
3924
|
+
*/
|
|
3925
|
+
function applyV21(db) {
|
|
3926
|
+
// audit_log に client_id カラムを追加
|
|
3927
|
+
if (!hasColumn(db, "audit_log", "client_id")) {
|
|
3928
|
+
db.exec(`ALTER TABLE audit_log ADD COLUMN client_id TEXT`);
|
|
3929
|
+
}
|
|
3930
|
+
// 既存レコードに現在のデバイスの client_id を backfill
|
|
3931
|
+
// (まだマージされていない = すべて自分のデバイスで作ったレコード)
|
|
3932
|
+
const clientIdRow = db.prepare("SELECT value FROM store_meta WHERE key = 'client_id'").get();
|
|
3933
|
+
if (clientIdRow) {
|
|
3934
|
+
db.prepare("UPDATE audit_log SET client_id = ? WHERE client_id IS NULL").run(clientIdRow.value);
|
|
3935
|
+
}
|
|
3936
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (21)`);
|
|
3937
|
+
}
|
|
3938
|
+
/**
|
|
3939
|
+
* V22スキーマ: unified_search_items に entity_id インデックス追加(Issue #60)
|
|
3940
|
+
* entity_id 単独での検索(entity_type 省略時の自動解決)を高速化する。
|
|
3941
|
+
*/
|
|
3942
|
+
function applyV22(db) {
|
|
3943
|
+
db.exec(`
|
|
3944
|
+
CREATE INDEX IF NOT EXISTS idx_unified_search_entity_id ON unified_search_items(entity_id);
|
|
3945
|
+
INSERT INTO schema_version (version) VALUES (22);
|
|
3946
|
+
`);
|
|
3947
|
+
}
|
|
3948
|
+
/**
|
|
3949
|
+
* V23マイグレーション: Claim に l1_content 追加 + embedding → l1_embedding リネーム (Issue #64)
|
|
3950
|
+
* - claims テーブル再構築: l1_content TEXT 追加 + embedding → l1_embedding
|
|
3951
|
+
* - 他8テーブル: embedding → l1_embedding リネーム(episodes, decisions, theories, insights, models, user_memos, user_plans, user_issues)
|
|
3952
|
+
* - unified_search_items: embedding → l1_embedding
|
|
3953
|
+
* - claims_fts: l1_content カラム追加
|
|
3954
|
+
* - 全 unified_search_items トリガー再構築(claims の search_text に l1_content 追加)
|
|
3955
|
+
*/
|
|
3956
|
+
function applyV23(db) {
|
|
3957
|
+
const fkState = db.pragma("foreign_keys");
|
|
3958
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
3959
|
+
db.pragma("foreign_keys = OFF");
|
|
3960
|
+
}
|
|
3961
|
+
// 2026-03-03 修正: ALTER TABLE RENAME 時に FK 参照が自動更新されるのを防止
|
|
3962
|
+
// SQLite 3.26+ では legacy_alter_table = OFF (デフォルト) だと
|
|
3963
|
+
// RENAME TABLE 時に他テーブルの FK 参照が新テーブル名に書き換わってしまう。
|
|
3964
|
+
// 例: claim_checks REFERENCES claims(id) → claims_old(id) に変わり、DROP 後にエラー。
|
|
3965
|
+
db.pragma("legacy_alter_table = ON");
|
|
3966
|
+
try {
|
|
3967
|
+
const transaction = db.transaction(() => {
|
|
3968
|
+
// JSON配列クリーニングヘルパー
|
|
3969
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
3970
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
3971
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
3972
|
+
// ========================================================
|
|
3973
|
+
// 1-A: claims テーブル再構築(l1_content 追加 + embedding → l1_embedding)
|
|
3974
|
+
// ========================================================
|
|
3975
|
+
db.exec(`ALTER TABLE claims RENAME TO claims_old`);
|
|
3976
|
+
db.exec(`
|
|
3977
|
+
CREATE TABLE claims (
|
|
3978
|
+
-- 1. 識別
|
|
3979
|
+
id TEXT PRIMARY KEY,
|
|
3980
|
+
subject TEXT NOT NULL,
|
|
3981
|
+
predicate TEXT NOT NULL,
|
|
3982
|
+
object TEXT NOT NULL,
|
|
3983
|
+
|
|
3984
|
+
-- 2. 分類
|
|
3985
|
+
category TEXT NOT NULL CHECK(category IN ('preference','identity','skill','value','workflow','knowledge','custom')),
|
|
3986
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
3987
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
3988
|
+
|
|
3989
|
+
-- 3. 知識・検証
|
|
3990
|
+
evidence TEXT,
|
|
3991
|
+
falsifier TEXT,
|
|
3992
|
+
|
|
3993
|
+
-- 4. L1: 自然言語コンテンツ(Issue #64 追加)
|
|
3994
|
+
l1_content TEXT,
|
|
3995
|
+
|
|
3996
|
+
-- 5. メタデータ
|
|
3997
|
+
search_summary TEXT,
|
|
3998
|
+
-- 2026-03-03 リネーム (Issue #64): embedding → l1_embedding
|
|
3999
|
+
l1_embedding BLOB,
|
|
4000
|
+
|
|
4001
|
+
-- 6. エンゲージメント
|
|
4002
|
+
hit_count INTEGER NOT NULL DEFAULT 0,
|
|
4003
|
+
last_hit_at TEXT,
|
|
4004
|
+
|
|
4005
|
+
-- 7. 分類(昇格元)
|
|
4006
|
+
promoted_from_store TEXT,
|
|
4007
|
+
promoted_from_id TEXT,
|
|
4008
|
+
|
|
4009
|
+
-- 8. ライフサイクル
|
|
4010
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','retracted','superseded','promoted')),
|
|
4011
|
+
|
|
4012
|
+
-- 9. プロヴナンス
|
|
4013
|
+
source_tool TEXT,
|
|
4014
|
+
source_session TEXT,
|
|
4015
|
+
client_name TEXT,
|
|
4016
|
+
client_version TEXT,
|
|
4017
|
+
|
|
4018
|
+
-- 10. タイムスタンプ
|
|
4019
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4020
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4021
|
+
)
|
|
4022
|
+
`);
|
|
4023
|
+
db.exec(`
|
|
4024
|
+
INSERT INTO claims (
|
|
4025
|
+
id, subject, predicate, object,
|
|
4026
|
+
category, scope, confidence,
|
|
4027
|
+
evidence, falsifier,
|
|
4028
|
+
l1_content,
|
|
4029
|
+
search_summary, l1_embedding,
|
|
4030
|
+
hit_count, last_hit_at,
|
|
4031
|
+
promoted_from_store, promoted_from_id,
|
|
4032
|
+
status,
|
|
4033
|
+
source_tool, source_session, client_name, client_version,
|
|
4034
|
+
created_at, updated_at
|
|
4035
|
+
)
|
|
4036
|
+
SELECT
|
|
4037
|
+
id, subject, predicate, object,
|
|
4038
|
+
category, scope, confidence,
|
|
4039
|
+
evidence, falsifier,
|
|
4040
|
+
NULL,
|
|
4041
|
+
search_summary, embedding,
|
|
4042
|
+
hit_count, last_hit_at,
|
|
4043
|
+
promoted_from_store, promoted_from_id,
|
|
4044
|
+
status,
|
|
4045
|
+
source_tool, source_session, client_name, client_version,
|
|
4046
|
+
created_at, updated_at
|
|
4047
|
+
FROM claims_old
|
|
4048
|
+
`);
|
|
4049
|
+
db.exec(`DROP TABLE claims_old`);
|
|
4050
|
+
// claims インデックス
|
|
4051
|
+
db.exec(`CREATE INDEX idx_claims_category ON claims(category)`);
|
|
4052
|
+
db.exec(`CREATE INDEX idx_claims_scope ON claims(scope)`);
|
|
4053
|
+
db.exec(`CREATE INDEX idx_claims_status ON claims(status)`);
|
|
4054
|
+
db.exec(`CREATE INDEX idx_claims_subject ON claims(subject)`);
|
|
4055
|
+
db.exec(`CREATE INDEX idx_claims_updated ON claims(updated_at)`);
|
|
4056
|
+
db.exec(`CREATE UNIQUE INDEX idx_claims_promoted_from ON claims(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
4057
|
+
// ========================================================
|
|
4058
|
+
// 1-B: episodes テーブル再構築(embedding → l1_embedding)
|
|
4059
|
+
// ========================================================
|
|
4060
|
+
db.exec(`ALTER TABLE episodes RENAME TO episodes_old`);
|
|
4061
|
+
db.exec(`
|
|
4062
|
+
CREATE TABLE episodes (
|
|
4063
|
+
id TEXT PRIMARY KEY,
|
|
4064
|
+
title TEXT NOT NULL,
|
|
4065
|
+
l1_content TEXT,
|
|
4066
|
+
l2_context TEXT,
|
|
4067
|
+
l2_trigger TEXT,
|
|
4068
|
+
l2_problems TEXT NOT NULL DEFAULT '[]',
|
|
4069
|
+
l2_desires TEXT NOT NULL DEFAULT '[]',
|
|
4070
|
+
l2_decisions TEXT NOT NULL DEFAULT '[]',
|
|
4071
|
+
l2_outcomes TEXT NOT NULL DEFAULT '[]',
|
|
4072
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
4073
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
4074
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4075
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4076
|
+
search_summary TEXT,
|
|
4077
|
+
l1_embedding BLOB,
|
|
4078
|
+
promoted_from_store TEXT,
|
|
4079
|
+
promoted_from_id TEXT,
|
|
4080
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
4081
|
+
groomed_at TEXT,
|
|
4082
|
+
source_tool TEXT,
|
|
4083
|
+
session_id TEXT,
|
|
4084
|
+
client_name TEXT,
|
|
4085
|
+
client_version TEXT,
|
|
4086
|
+
user_input TEXT,
|
|
4087
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
4088
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
4089
|
+
)
|
|
4090
|
+
`);
|
|
4091
|
+
db.exec(`
|
|
4092
|
+
INSERT INTO episodes (
|
|
4093
|
+
id, title, l1_content,
|
|
4094
|
+
l2_context, l2_trigger, l2_problems, l2_desires, l2_decisions, l2_outcomes, l2_principles,
|
|
4095
|
+
evidence_refs, tags, scope, search_summary, l1_embedding,
|
|
4096
|
+
promoted_from_store, promoted_from_id,
|
|
4097
|
+
status, groomed_at,
|
|
4098
|
+
source_tool, session_id, client_name, client_version, user_input,
|
|
4099
|
+
created_at, updated_at
|
|
4100
|
+
)
|
|
4101
|
+
SELECT
|
|
4102
|
+
id, title, l1_content,
|
|
4103
|
+
l2_context, l2_trigger, l2_problems, l2_desires, l2_decisions, l2_outcomes, l2_principles,
|
|
4104
|
+
evidence_refs, tags, scope, search_summary, embedding,
|
|
4105
|
+
promoted_from_store, promoted_from_id,
|
|
4106
|
+
status, groomed_at,
|
|
4107
|
+
source_tool, session_id, client_name, client_version, user_input,
|
|
4108
|
+
created_at, updated_at
|
|
4109
|
+
FROM episodes_old
|
|
4110
|
+
`);
|
|
4111
|
+
db.exec(`DROP TABLE episodes_old`);
|
|
4112
|
+
db.exec(`CREATE INDEX idx_episodes_scope ON episodes(scope)`);
|
|
4113
|
+
db.exec(`CREATE INDEX idx_episodes_status ON episodes(status)`);
|
|
4114
|
+
db.exec(`CREATE INDEX idx_episodes_created ON episodes(created_at)`);
|
|
4115
|
+
db.exec(`CREATE INDEX idx_episodes_updated ON episodes(updated_at)`);
|
|
4116
|
+
db.exec(`CREATE UNIQUE INDEX idx_episodes_promoted_from ON episodes(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
4117
|
+
// ========================================================
|
|
4118
|
+
// 1-C: decisions テーブル再構築(embedding → l1_embedding)
|
|
4119
|
+
// ========================================================
|
|
4120
|
+
db.exec(`ALTER TABLE decisions RENAME TO decisions_old`);
|
|
4121
|
+
db.exec(`
|
|
4122
|
+
CREATE TABLE decisions (
|
|
4123
|
+
id TEXT PRIMARY KEY,
|
|
4124
|
+
title TEXT NOT NULL,
|
|
4125
|
+
description TEXT NOT NULL,
|
|
4126
|
+
l1_content TEXT,
|
|
4127
|
+
l2_reasoning TEXT NOT NULL,
|
|
4128
|
+
l2_alternatives TEXT NOT NULL DEFAULT '[]',
|
|
4129
|
+
related_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
4130
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4131
|
+
search_summary TEXT,
|
|
4132
|
+
l1_embedding BLOB,
|
|
4133
|
+
promoted_from_store TEXT,
|
|
4134
|
+
promoted_from_id TEXT,
|
|
4135
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','reversed','obsolete','promoted')),
|
|
4136
|
+
source_tool TEXT,
|
|
4137
|
+
client_name TEXT,
|
|
4138
|
+
client_version TEXT,
|
|
4139
|
+
user_input TEXT,
|
|
4140
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
4141
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
4142
|
+
)
|
|
4143
|
+
`);
|
|
4144
|
+
db.exec(`
|
|
4145
|
+
INSERT INTO decisions (
|
|
4146
|
+
id, title, description, l1_content, l2_reasoning, l2_alternatives,
|
|
4147
|
+
related_claim_ids, scope, search_summary, l1_embedding,
|
|
4148
|
+
promoted_from_store, promoted_from_id,
|
|
4149
|
+
status, source_tool, client_name, client_version, user_input,
|
|
4150
|
+
created_at, updated_at
|
|
4151
|
+
)
|
|
4152
|
+
SELECT
|
|
4153
|
+
id, title, description, l1_content, l2_reasoning, l2_alternatives,
|
|
4154
|
+
related_claim_ids, scope, search_summary, embedding,
|
|
4155
|
+
promoted_from_store, promoted_from_id,
|
|
4156
|
+
status, source_tool, client_name, client_version, user_input,
|
|
4157
|
+
created_at, updated_at
|
|
4158
|
+
FROM decisions_old
|
|
4159
|
+
`);
|
|
4160
|
+
db.exec(`DROP TABLE decisions_old`);
|
|
4161
|
+
db.exec(`CREATE INDEX idx_decisions_scope ON decisions(scope)`);
|
|
4162
|
+
db.exec(`CREATE INDEX idx_decisions_status ON decisions(status)`);
|
|
4163
|
+
db.exec(`CREATE INDEX idx_decisions_created ON decisions(created_at)`);
|
|
4164
|
+
db.exec(`CREATE INDEX idx_decisions_updated ON decisions(updated_at)`);
|
|
4165
|
+
db.exec(`CREATE UNIQUE INDEX idx_decisions_promoted_from ON decisions(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
4166
|
+
// ========================================================
|
|
4167
|
+
// 1-D: theories テーブル再構築(embedding → l1_embedding)
|
|
4168
|
+
// ========================================================
|
|
4169
|
+
db.exec(`ALTER TABLE theories RENAME TO theories_old`);
|
|
4170
|
+
db.exec(`
|
|
4171
|
+
CREATE TABLE theories (
|
|
4172
|
+
id TEXT PRIMARY KEY,
|
|
4173
|
+
title TEXT NOT NULL,
|
|
4174
|
+
description TEXT,
|
|
4175
|
+
l1_content TEXT,
|
|
4176
|
+
l2_core_thesis TEXT,
|
|
4177
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
4178
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
4179
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
4180
|
+
l2_applicable_context TEXT,
|
|
4181
|
+
non_goals TEXT NOT NULL DEFAULT '[]',
|
|
4182
|
+
open_questions TEXT NOT NULL DEFAULT '[]',
|
|
4183
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4184
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4185
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
4186
|
+
search_summary TEXT,
|
|
4187
|
+
l1_embedding BLOB,
|
|
4188
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
4189
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
4190
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
4191
|
+
promoted_from_store TEXT,
|
|
4192
|
+
promoted_from_id TEXT,
|
|
4193
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
4194
|
+
source_tool TEXT,
|
|
4195
|
+
session_id TEXT,
|
|
4196
|
+
client_name TEXT,
|
|
4197
|
+
client_version TEXT,
|
|
4198
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
4199
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
4200
|
+
)
|
|
4201
|
+
`);
|
|
4202
|
+
db.exec(`
|
|
4203
|
+
INSERT INTO theories (
|
|
4204
|
+
id, title, description, l1_content,
|
|
4205
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
4206
|
+
non_goals, open_questions,
|
|
4207
|
+
tags, scope, confidence, search_summary, l1_embedding,
|
|
4208
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
4209
|
+
promoted_from_store, promoted_from_id,
|
|
4210
|
+
status, source_tool, session_id, client_name, client_version,
|
|
4211
|
+
created_at, updated_at
|
|
4212
|
+
)
|
|
4213
|
+
SELECT
|
|
4214
|
+
id, title, description, l1_content,
|
|
4215
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
4216
|
+
non_goals, open_questions,
|
|
4217
|
+
tags, scope, confidence, search_summary, embedding,
|
|
4218
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
4219
|
+
promoted_from_store, promoted_from_id,
|
|
4220
|
+
status, source_tool, session_id, client_name, client_version,
|
|
4221
|
+
created_at, updated_at
|
|
4222
|
+
FROM theories_old
|
|
4223
|
+
`);
|
|
4224
|
+
db.exec(`DROP TABLE theories_old`);
|
|
4225
|
+
db.exec(`CREATE INDEX idx_theories_scope ON theories(scope)`);
|
|
4226
|
+
db.exec(`CREATE INDEX idx_theories_status ON theories(status)`);
|
|
4227
|
+
db.exec(`CREATE INDEX idx_theories_created ON theories(created_at)`);
|
|
4228
|
+
db.exec(`CREATE INDEX idx_theories_updated ON theories(updated_at)`);
|
|
4229
|
+
db.exec(`CREATE UNIQUE INDEX idx_theories_promoted_from ON theories(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
4230
|
+
// ========================================================
|
|
4231
|
+
// 1-E: insights テーブル再構築(embedding → l1_embedding)
|
|
4232
|
+
// ========================================================
|
|
4233
|
+
db.exec(`ALTER TABLE insights RENAME TO insights_old`);
|
|
4234
|
+
db.exec(`
|
|
4235
|
+
CREATE TABLE insights (
|
|
4236
|
+
id TEXT PRIMARY KEY,
|
|
4237
|
+
title TEXT NOT NULL,
|
|
4238
|
+
description TEXT,
|
|
4239
|
+
l1_content TEXT,
|
|
4240
|
+
l2_core_thesis TEXT,
|
|
4241
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
4242
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
4243
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
4244
|
+
l2_applicable_context TEXT,
|
|
4245
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4246
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4247
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
4248
|
+
search_summary TEXT,
|
|
4249
|
+
l1_embedding BLOB,
|
|
4250
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
4251
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
4252
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
4253
|
+
promoted_from_store TEXT,
|
|
4254
|
+
promoted_from_id TEXT,
|
|
4255
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
4256
|
+
source_tool TEXT,
|
|
4257
|
+
session_id TEXT,
|
|
4258
|
+
client_name TEXT,
|
|
4259
|
+
client_version TEXT,
|
|
4260
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
4261
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
4262
|
+
)
|
|
4263
|
+
`);
|
|
4264
|
+
db.exec(`
|
|
4265
|
+
INSERT INTO insights (
|
|
4266
|
+
id, title, description, l1_content,
|
|
4267
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
4268
|
+
tags, scope, confidence, search_summary, l1_embedding,
|
|
4269
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
4270
|
+
promoted_from_store, promoted_from_id,
|
|
4271
|
+
status, source_tool, session_id, client_name, client_version,
|
|
4272
|
+
created_at, updated_at
|
|
4273
|
+
)
|
|
4274
|
+
SELECT
|
|
4275
|
+
id, title, description, l1_content,
|
|
4276
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
4277
|
+
tags, scope, confidence, search_summary, embedding,
|
|
4278
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
4279
|
+
promoted_from_store, promoted_from_id,
|
|
4280
|
+
status, source_tool, session_id, client_name, client_version,
|
|
4281
|
+
created_at, updated_at
|
|
4282
|
+
FROM insights_old
|
|
4283
|
+
`);
|
|
4284
|
+
db.exec(`DROP TABLE insights_old`);
|
|
4285
|
+
db.exec(`CREATE INDEX idx_insights_scope ON insights(scope)`);
|
|
4286
|
+
db.exec(`CREATE INDEX idx_insights_status ON insights(status)`);
|
|
4287
|
+
db.exec(`CREATE INDEX idx_insights_created ON insights(created_at)`);
|
|
4288
|
+
db.exec(`CREATE INDEX idx_insights_updated ON insights(updated_at)`);
|
|
4289
|
+
db.exec(`CREATE UNIQUE INDEX idx_insights_promoted_from ON insights(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
4290
|
+
// ========================================================
|
|
4291
|
+
// 1-F: models テーブル再構築(embedding → l1_embedding)
|
|
4292
|
+
// ========================================================
|
|
4293
|
+
db.exec(`ALTER TABLE models RENAME TO models_old`);
|
|
4294
|
+
db.exec(`
|
|
4295
|
+
CREATE TABLE models (
|
|
4296
|
+
id TEXT PRIMARY KEY,
|
|
4297
|
+
title TEXT NOT NULL,
|
|
4298
|
+
description TEXT,
|
|
4299
|
+
l1_content TEXT,
|
|
4300
|
+
l2_core_thesis TEXT,
|
|
4301
|
+
l2_principles TEXT NOT NULL DEFAULT '[]',
|
|
4302
|
+
l2_trigger_conditions TEXT NOT NULL DEFAULT '[]',
|
|
4303
|
+
l2_resolution_steps TEXT NOT NULL DEFAULT '[]',
|
|
4304
|
+
l2_applicable_context TEXT,
|
|
4305
|
+
non_goals TEXT NOT NULL DEFAULT '[]',
|
|
4306
|
+
open_questions TEXT NOT NULL DEFAULT '[]',
|
|
4307
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4308
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4309
|
+
confidence REAL NOT NULL DEFAULT 0.8 CHECK(confidence >= 0.0 AND confidence <= 1.0),
|
|
4310
|
+
search_summary TEXT,
|
|
4311
|
+
l1_embedding BLOB,
|
|
4312
|
+
supporting_episode_ids TEXT NOT NULL DEFAULT '[]',
|
|
4313
|
+
supporting_claim_ids TEXT NOT NULL DEFAULT '[]',
|
|
4314
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
4315
|
+
promoted_from_store TEXT,
|
|
4316
|
+
promoted_from_id TEXT,
|
|
4317
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','archived','promoted')),
|
|
4318
|
+
source_tool TEXT,
|
|
4319
|
+
session_id TEXT,
|
|
4320
|
+
client_name TEXT,
|
|
4321
|
+
client_version TEXT,
|
|
4322
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now')),
|
|
4323
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
|
|
4324
|
+
)
|
|
4325
|
+
`);
|
|
4326
|
+
db.exec(`
|
|
4327
|
+
INSERT INTO models (
|
|
4328
|
+
id, title, description, l1_content,
|
|
4329
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
4330
|
+
non_goals, open_questions,
|
|
4331
|
+
tags, scope, confidence, search_summary, l1_embedding,
|
|
4332
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
4333
|
+
promoted_from_store, promoted_from_id,
|
|
4334
|
+
status, source_tool, session_id, client_name, client_version,
|
|
4335
|
+
created_at, updated_at
|
|
4336
|
+
)
|
|
4337
|
+
SELECT
|
|
4338
|
+
id, title, description, l1_content,
|
|
4339
|
+
l2_core_thesis, l2_principles, l2_trigger_conditions, l2_resolution_steps, l2_applicable_context,
|
|
4340
|
+
non_goals, open_questions,
|
|
4341
|
+
tags, scope, confidence, search_summary, embedding,
|
|
4342
|
+
supporting_episode_ids, supporting_claim_ids, evidence_refs,
|
|
4343
|
+
promoted_from_store, promoted_from_id,
|
|
4344
|
+
status, source_tool, session_id, client_name, client_version,
|
|
4345
|
+
created_at, updated_at
|
|
4346
|
+
FROM models_old
|
|
4347
|
+
`);
|
|
4348
|
+
db.exec(`DROP TABLE models_old`);
|
|
4349
|
+
db.exec(`CREATE INDEX idx_models_scope ON models(scope)`);
|
|
4350
|
+
db.exec(`CREATE INDEX idx_models_status ON models(status)`);
|
|
4351
|
+
db.exec(`CREATE INDEX idx_models_created ON models(created_at)`);
|
|
4352
|
+
db.exec(`CREATE INDEX idx_models_updated ON models(updated_at)`);
|
|
4353
|
+
db.exec(`CREATE UNIQUE INDEX idx_models_promoted_from ON models(promoted_from_store, promoted_from_id) WHERE promoted_from_store IS NOT NULL`);
|
|
4354
|
+
// ========================================================
|
|
4355
|
+
// 1-G: user_memos テーブル再構築(embedding → l1_embedding)
|
|
4356
|
+
// ========================================================
|
|
4357
|
+
db.exec(`ALTER TABLE user_memos RENAME TO user_memos_old`);
|
|
4358
|
+
db.exec(`
|
|
4359
|
+
CREATE TABLE user_memos (
|
|
4360
|
+
id TEXT PRIMARY KEY,
|
|
4361
|
+
title TEXT NOT NULL,
|
|
4362
|
+
content TEXT NOT NULL,
|
|
4363
|
+
usage_policy TEXT NOT NULL DEFAULT 'on_request'
|
|
4364
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
4365
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4366
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4367
|
+
search_summary TEXT,
|
|
4368
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
4369
|
+
CHECK(status IN ('active','archived')),
|
|
4370
|
+
l1_embedding BLOB,
|
|
4371
|
+
client_name TEXT,
|
|
4372
|
+
client_version TEXT,
|
|
4373
|
+
source_tool TEXT,
|
|
4374
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4375
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4376
|
+
)
|
|
4377
|
+
`);
|
|
4378
|
+
db.exec(`
|
|
4379
|
+
INSERT INTO user_memos (
|
|
4380
|
+
id, title, content, usage_policy, tags, scope, search_summary,
|
|
4381
|
+
status, l1_embedding, client_name, client_version, source_tool,
|
|
4382
|
+
created_at, updated_at
|
|
4383
|
+
)
|
|
4384
|
+
SELECT
|
|
4385
|
+
id, title, content, usage_policy, tags, scope, search_summary,
|
|
4386
|
+
status, embedding, client_name, client_version, source_tool,
|
|
4387
|
+
created_at, updated_at
|
|
4388
|
+
FROM user_memos_old
|
|
4389
|
+
`);
|
|
4390
|
+
db.exec(`DROP TABLE user_memos_old`);
|
|
4391
|
+
db.exec(`CREATE INDEX idx_user_memos_status ON user_memos(status)`);
|
|
4392
|
+
db.exec(`CREATE INDEX idx_user_memos_scope ON user_memos(scope)`);
|
|
4393
|
+
db.exec(`CREATE INDEX idx_user_memos_usage_policy ON user_memos(usage_policy)`);
|
|
4394
|
+
db.exec(`CREATE INDEX idx_user_memos_updated ON user_memos(updated_at)`);
|
|
4395
|
+
// ========================================================
|
|
4396
|
+
// 1-H: user_plans テーブル再構築(embedding → l1_embedding)
|
|
4397
|
+
// ========================================================
|
|
4398
|
+
db.exec(`ALTER TABLE user_plans RENAME TO user_plans_old`);
|
|
4399
|
+
db.exec(`
|
|
4400
|
+
CREATE TABLE user_plans (
|
|
4401
|
+
id TEXT PRIMARY KEY,
|
|
4402
|
+
title TEXT NOT NULL,
|
|
4403
|
+
content TEXT NOT NULL,
|
|
4404
|
+
usage_policy TEXT NOT NULL DEFAULT 'auto'
|
|
4405
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
4406
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4407
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4408
|
+
search_summary TEXT,
|
|
4409
|
+
user_input TEXT,
|
|
4410
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
4411
|
+
CHECK(status IN ('active','completed','archived')),
|
|
4412
|
+
l1_embedding BLOB,
|
|
4413
|
+
client_name TEXT,
|
|
4414
|
+
client_version TEXT,
|
|
4415
|
+
source_tool TEXT,
|
|
4416
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4417
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4418
|
+
)
|
|
4419
|
+
`);
|
|
4420
|
+
db.exec(`
|
|
4421
|
+
INSERT INTO user_plans (
|
|
4422
|
+
id, title, content, usage_policy, tags, scope, search_summary,
|
|
4423
|
+
user_input, status, l1_embedding, client_name, client_version, source_tool,
|
|
4424
|
+
created_at, updated_at
|
|
4425
|
+
)
|
|
4426
|
+
SELECT
|
|
4427
|
+
id, title, content, usage_policy, tags, scope, search_summary,
|
|
4428
|
+
user_input, status, embedding, client_name, client_version, source_tool,
|
|
4429
|
+
created_at, updated_at
|
|
4430
|
+
FROM user_plans_old
|
|
4431
|
+
`);
|
|
4432
|
+
db.exec(`DROP TABLE user_plans_old`);
|
|
4433
|
+
db.exec(`CREATE INDEX idx_user_plans_status ON user_plans(status)`);
|
|
4434
|
+
db.exec(`CREATE INDEX idx_user_plans_scope ON user_plans(scope)`);
|
|
4435
|
+
db.exec(`CREATE INDEX idx_user_plans_usage_policy ON user_plans(usage_policy)`);
|
|
4436
|
+
db.exec(`CREATE INDEX idx_user_plans_updated ON user_plans(updated_at)`);
|
|
4437
|
+
// ========================================================
|
|
4438
|
+
// 1-I: user_issues テーブル再構築(embedding → l1_embedding)
|
|
4439
|
+
// ========================================================
|
|
4440
|
+
db.exec(`ALTER TABLE user_issues RENAME TO user_issues_old`);
|
|
4441
|
+
db.exec(`
|
|
4442
|
+
CREATE TABLE user_issues (
|
|
4443
|
+
id TEXT PRIMARY KEY,
|
|
4444
|
+
title TEXT NOT NULL,
|
|
4445
|
+
content TEXT NOT NULL,
|
|
4446
|
+
entries TEXT NOT NULL DEFAULT '[]',
|
|
4447
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
4448
|
+
CHECK(priority IN ('low','medium','high','critical')),
|
|
4449
|
+
usage_policy TEXT NOT NULL DEFAULT 'on_request'
|
|
4450
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
4451
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4452
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4453
|
+
search_summary TEXT,
|
|
4454
|
+
user_input TEXT,
|
|
4455
|
+
status TEXT NOT NULL DEFAULT 'open'
|
|
4456
|
+
CHECK(status IN ('open','closed','archived')),
|
|
4457
|
+
l1_embedding BLOB,
|
|
4458
|
+
client_name TEXT,
|
|
4459
|
+
client_version TEXT,
|
|
4460
|
+
source_tool TEXT,
|
|
4461
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4462
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4463
|
+
)
|
|
4464
|
+
`);
|
|
4465
|
+
db.exec(`
|
|
4466
|
+
INSERT INTO user_issues (
|
|
4467
|
+
id, title, content, entries, priority, usage_policy, tags, scope,
|
|
4468
|
+
search_summary, user_input, status, l1_embedding,
|
|
4469
|
+
client_name, client_version, source_tool,
|
|
4470
|
+
created_at, updated_at
|
|
4471
|
+
)
|
|
4472
|
+
SELECT
|
|
4473
|
+
id, title, content, entries, priority, usage_policy, tags, scope,
|
|
4474
|
+
search_summary, user_input, status, embedding,
|
|
4475
|
+
client_name, client_version, source_tool,
|
|
4476
|
+
created_at, updated_at
|
|
4477
|
+
FROM user_issues_old
|
|
4478
|
+
`);
|
|
4479
|
+
db.exec(`DROP TABLE user_issues_old`);
|
|
4480
|
+
db.exec(`CREATE INDEX idx_user_issues_status ON user_issues(status)`);
|
|
4481
|
+
db.exec(`CREATE INDEX idx_user_issues_scope ON user_issues(scope)`);
|
|
4482
|
+
db.exec(`CREATE INDEX idx_user_issues_priority ON user_issues(priority)`);
|
|
4483
|
+
db.exec(`CREATE INDEX idx_user_issues_updated ON user_issues(updated_at)`);
|
|
4484
|
+
// ========================================================
|
|
4485
|
+
// 1-J: unified_search_items テーブル再構築(embedding → l1_embedding)
|
|
4486
|
+
// ========================================================
|
|
4487
|
+
// 既存トリガーをすべて削除(テーブル再構築のため)
|
|
4488
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_insert`);
|
|
4489
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_update`);
|
|
4490
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_delete`);
|
|
4491
|
+
for (const table of ["claims", "episodes", "decisions", "theories", "insights", "models", "user_memos", "user_plans", "user_issues"]) {
|
|
4492
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_insert`);
|
|
4493
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_update`);
|
|
4494
|
+
db.exec(`DROP TRIGGER IF EXISTS ${table}_unified_delete`);
|
|
4495
|
+
}
|
|
4496
|
+
db.exec(`
|
|
4497
|
+
CREATE TABLE unified_search_items_new (
|
|
4498
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('claim','episode','decision','theory','insight','model','user_memo','user_plan','user_issue')),
|
|
4499
|
+
entity_id TEXT NOT NULL,
|
|
4500
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
4501
|
+
category TEXT,
|
|
4502
|
+
title_summary TEXT NOT NULL,
|
|
4503
|
+
search_text TEXT NOT NULL,
|
|
4504
|
+
search_summary TEXT,
|
|
4505
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4506
|
+
l1_embedding BLOB,
|
|
4507
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4508
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4509
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
4510
|
+
)
|
|
4511
|
+
`);
|
|
4512
|
+
db.exec(`
|
|
4513
|
+
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)
|
|
4514
|
+
SELECT entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, embedding, created_at, updated_at
|
|
4515
|
+
FROM unified_search_items
|
|
4516
|
+
`);
|
|
4517
|
+
db.exec(`DROP TABLE unified_search_items`);
|
|
4518
|
+
db.exec(`ALTER TABLE unified_search_items_new RENAME TO unified_search_items`);
|
|
4519
|
+
db.exec(`CREATE INDEX idx_unified_search_scope ON unified_search_items(scope)`);
|
|
4520
|
+
db.exec(`CREATE INDEX idx_unified_search_entity_type ON unified_search_items(entity_type)`);
|
|
4521
|
+
db.exec(`CREATE INDEX idx_unified_search_updated ON unified_search_items(updated_at)`);
|
|
4522
|
+
db.exec(`CREATE INDEX idx_unified_search_entity_id ON unified_search_items(entity_id)`);
|
|
4523
|
+
// unified_search_fts 再作成
|
|
4524
|
+
db.exec(`DROP TABLE IF EXISTS unified_search_fts`);
|
|
4525
|
+
db.exec(`
|
|
4526
|
+
CREATE VIRTUAL TABLE unified_search_fts USING fts5(
|
|
4527
|
+
title_summary, search_text, search_summary,
|
|
4528
|
+
content='unified_search_items',
|
|
4529
|
+
content_rowid='rowid',
|
|
4530
|
+
tokenize='trigram'
|
|
4531
|
+
)
|
|
4532
|
+
`);
|
|
4533
|
+
// FTS同期トリガー再作成
|
|
4534
|
+
db.exec(`
|
|
4535
|
+
CREATE TRIGGER unified_search_fts_insert AFTER INSERT ON unified_search_items BEGIN
|
|
4536
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
4537
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
4538
|
+
END
|
|
4539
|
+
`);
|
|
4540
|
+
db.exec(`
|
|
4541
|
+
CREATE TRIGGER unified_search_fts_update AFTER UPDATE ON unified_search_items BEGIN
|
|
4542
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
4543
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
4544
|
+
INSERT INTO unified_search_fts(rowid, title_summary, search_text, search_summary)
|
|
4545
|
+
VALUES (new.rowid, new.title_summary, new.search_text, new.search_summary);
|
|
4546
|
+
END
|
|
4547
|
+
`);
|
|
4548
|
+
db.exec(`
|
|
4549
|
+
CREATE TRIGGER unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
4550
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
4551
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
4552
|
+
END
|
|
4553
|
+
`);
|
|
4554
|
+
// ========================================================
|
|
4555
|
+
// 1-K: claims_fts 再構築(l1_content カラム追加)
|
|
4556
|
+
// ========================================================
|
|
4557
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_fts_insert`);
|
|
4558
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_fts_update`);
|
|
4559
|
+
db.exec(`DROP TRIGGER IF EXISTS claims_fts_delete`);
|
|
4560
|
+
db.exec(`DROP TABLE IF EXISTS claims_fts`);
|
|
4561
|
+
db.exec(`
|
|
4562
|
+
CREATE VIRTUAL TABLE claims_fts USING fts5(
|
|
4563
|
+
subject, predicate, object, evidence, l1_content, search_summary,
|
|
4564
|
+
content='claims',
|
|
4565
|
+
content_rowid='rowid',
|
|
4566
|
+
tokenize='trigram'
|
|
4567
|
+
)
|
|
4568
|
+
`);
|
|
4569
|
+
db.exec(`
|
|
4570
|
+
CREATE TRIGGER claims_fts_insert AFTER INSERT ON claims BEGIN
|
|
4571
|
+
INSERT INTO claims_fts(rowid, subject, predicate, object, evidence, l1_content, search_summary)
|
|
4572
|
+
VALUES (new.rowid, new.subject, new.predicate, new.object, new.evidence, new.l1_content, new.search_summary);
|
|
4573
|
+
END
|
|
4574
|
+
`);
|
|
4575
|
+
db.exec(`
|
|
4576
|
+
CREATE TRIGGER claims_fts_update AFTER UPDATE ON claims BEGIN
|
|
4577
|
+
INSERT INTO claims_fts(claims_fts, rowid, subject, predicate, object, evidence, l1_content, search_summary)
|
|
4578
|
+
VALUES ('delete', old.rowid, old.subject, old.predicate, old.object, old.evidence, old.l1_content, old.search_summary);
|
|
4579
|
+
INSERT INTO claims_fts(rowid, subject, predicate, object, evidence, l1_content, search_summary)
|
|
4580
|
+
VALUES (new.rowid, new.subject, new.predicate, new.object, new.evidence, new.l1_content, new.search_summary);
|
|
4581
|
+
END
|
|
4582
|
+
`);
|
|
4583
|
+
db.exec(`
|
|
4584
|
+
CREATE TRIGGER claims_fts_delete AFTER DELETE ON claims BEGIN
|
|
4585
|
+
INSERT INTO claims_fts(claims_fts, rowid, subject, predicate, object, evidence, l1_content, search_summary)
|
|
4586
|
+
VALUES ('delete', old.rowid, old.subject, old.predicate, old.object, old.evidence, old.l1_content, old.search_summary);
|
|
4587
|
+
END
|
|
4588
|
+
`);
|
|
4589
|
+
// ========================================================
|
|
4590
|
+
// 1-L: 他エンティティの FTS 再構築(テーブル再構築でトリガーが消えたため)
|
|
4591
|
+
// ========================================================
|
|
4592
|
+
// --- theories_fts ---
|
|
4593
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_insert`);
|
|
4594
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_update`);
|
|
4595
|
+
db.exec(`DROP TRIGGER IF EXISTS theories_fts_delete`);
|
|
4596
|
+
db.exec(`DROP TABLE IF EXISTS theories_fts`);
|
|
4597
|
+
db.exec(`
|
|
4598
|
+
CREATE VIRTUAL TABLE theories_fts USING fts5(
|
|
4599
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
4600
|
+
content='theories', content_rowid='rowid', tokenize='trigram'
|
|
4601
|
+
)
|
|
4602
|
+
`);
|
|
4603
|
+
db.exec(`
|
|
4604
|
+
CREATE TRIGGER theories_fts_insert AFTER INSERT ON theories BEGIN
|
|
4605
|
+
INSERT INTO theories_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4606
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
4607
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
4608
|
+
END
|
|
4609
|
+
`);
|
|
4610
|
+
db.exec(`
|
|
4611
|
+
CREATE TRIGGER theories_fts_update AFTER UPDATE ON theories BEGIN
|
|
4612
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4613
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
4614
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
4615
|
+
INSERT INTO theories_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4616
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
4617
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
4618
|
+
END
|
|
4619
|
+
`);
|
|
4620
|
+
db.exec(`
|
|
4621
|
+
CREATE TRIGGER theories_fts_delete AFTER DELETE ON theories BEGIN
|
|
4622
|
+
INSERT INTO theories_fts(theories_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4623
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
4624
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
4625
|
+
END
|
|
4626
|
+
`);
|
|
4627
|
+
// --- models_fts ---
|
|
4628
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_insert`);
|
|
4629
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_update`);
|
|
4630
|
+
db.exec(`DROP TRIGGER IF EXISTS models_fts_delete`);
|
|
4631
|
+
db.exec(`DROP TABLE IF EXISTS models_fts`);
|
|
4632
|
+
db.exec(`
|
|
4633
|
+
CREATE VIRTUAL TABLE models_fts USING fts5(
|
|
4634
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
4635
|
+
content='models', content_rowid='rowid', tokenize='trigram'
|
|
4636
|
+
)
|
|
4637
|
+
`);
|
|
4638
|
+
db.exec(`
|
|
4639
|
+
CREATE TRIGGER models_fts_insert AFTER INSERT ON models BEGIN
|
|
4640
|
+
INSERT INTO models_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4641
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
4642
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
4643
|
+
END
|
|
4644
|
+
`);
|
|
4645
|
+
db.exec(`
|
|
4646
|
+
CREATE TRIGGER models_fts_update AFTER UPDATE ON models BEGIN
|
|
4647
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4648
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
4649
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
4650
|
+
INSERT INTO models_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4651
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
4652
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
4653
|
+
END
|
|
4654
|
+
`);
|
|
4655
|
+
db.exec(`
|
|
4656
|
+
CREATE TRIGGER models_fts_delete AFTER DELETE ON models BEGIN
|
|
4657
|
+
INSERT INTO models_fts(models_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4658
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
4659
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
4660
|
+
END
|
|
4661
|
+
`);
|
|
4662
|
+
// --- insights_fts ---
|
|
4663
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_insert`);
|
|
4664
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_update`);
|
|
4665
|
+
db.exec(`DROP TRIGGER IF EXISTS insights_fts_delete`);
|
|
4666
|
+
db.exec(`DROP TABLE IF EXISTS insights_fts`);
|
|
4667
|
+
db.exec(`
|
|
4668
|
+
CREATE VIRTUAL TABLE insights_fts USING fts5(
|
|
4669
|
+
title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary,
|
|
4670
|
+
content='insights', content_rowid='rowid', tokenize='trigram'
|
|
4671
|
+
)
|
|
4672
|
+
`);
|
|
4673
|
+
db.exec(`
|
|
4674
|
+
CREATE TRIGGER insights_fts_insert AFTER INSERT ON insights BEGIN
|
|
4675
|
+
INSERT INTO insights_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4676
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
4677
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
4678
|
+
END
|
|
4679
|
+
`);
|
|
4680
|
+
db.exec(`
|
|
4681
|
+
CREATE TRIGGER insights_fts_update AFTER UPDATE ON insights BEGIN
|
|
4682
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4683
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
4684
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
4685
|
+
INSERT INTO insights_fts(rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4686
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_core_thesis,
|
|
4687
|
+
${jc("new.l2_principles")}, ${jc("new.evidence_refs")}, ${jc("new.tags")}, new.search_summary);
|
|
4688
|
+
END
|
|
4689
|
+
`);
|
|
4690
|
+
db.exec(`
|
|
4691
|
+
CREATE TRIGGER insights_fts_delete AFTER DELETE ON insights BEGIN
|
|
4692
|
+
INSERT INTO insights_fts(insights_fts, rowid, title, description, l1_content, l2_core_thesis, l2_principles, evidence_refs, tags, search_summary)
|
|
4693
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_core_thesis,
|
|
4694
|
+
${jc("old.l2_principles")}, ${jc("old.evidence_refs")}, ${jc("old.tags")}, old.search_summary);
|
|
4695
|
+
END
|
|
4696
|
+
`);
|
|
4697
|
+
// --- episodes_fts ---
|
|
4698
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_insert`);
|
|
4699
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_update`);
|
|
4700
|
+
db.exec(`DROP TRIGGER IF EXISTS episodes_fts_delete`);
|
|
4701
|
+
db.exec(`DROP TABLE IF EXISTS episodes_fts`);
|
|
4702
|
+
db.exec(`
|
|
4703
|
+
CREATE VIRTUAL TABLE episodes_fts USING fts5(
|
|
4704
|
+
title, l1_content, l2_context, l2_trigger, l2_principles, l2_problems, l2_desires, l2_decisions, l2_outcomes, search_summary, user_input,
|
|
4705
|
+
content='episodes', content_rowid='rowid', tokenize='trigram'
|
|
4706
|
+
)
|
|
4707
|
+
`);
|
|
4708
|
+
db.exec(`
|
|
4709
|
+
CREATE TRIGGER episodes_fts_insert AFTER INSERT ON episodes BEGIN
|
|
4710
|
+
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)
|
|
4711
|
+
VALUES (new.rowid, new.title, new.l1_content, new.l2_context, new.l2_trigger,
|
|
4712
|
+
${jc("new.l2_principles")}, ${jc("new.l2_problems")}, ${jc("new.l2_desires")},
|
|
4713
|
+
${jc("new.l2_decisions")}, ${jc("new.l2_outcomes")},
|
|
4714
|
+
new.search_summary, new.user_input);
|
|
4715
|
+
END
|
|
4716
|
+
`);
|
|
4717
|
+
db.exec(`
|
|
4718
|
+
CREATE TRIGGER episodes_fts_update AFTER UPDATE ON episodes BEGIN
|
|
4719
|
+
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)
|
|
4720
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, old.l2_context, old.l2_trigger,
|
|
4721
|
+
${jc("old.l2_principles")}, ${jc("old.l2_problems")}, ${jc("old.l2_desires")},
|
|
4722
|
+
${jc("old.l2_decisions")}, ${jc("old.l2_outcomes")},
|
|
4723
|
+
old.search_summary, old.user_input);
|
|
4724
|
+
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)
|
|
4725
|
+
VALUES (new.rowid, new.title, new.l1_content, new.l2_context, new.l2_trigger,
|
|
4726
|
+
${jc("new.l2_principles")}, ${jc("new.l2_problems")}, ${jc("new.l2_desires")},
|
|
4727
|
+
${jc("new.l2_decisions")}, ${jc("new.l2_outcomes")},
|
|
4728
|
+
new.search_summary, new.user_input);
|
|
4729
|
+
END
|
|
4730
|
+
`);
|
|
4731
|
+
db.exec(`
|
|
4732
|
+
CREATE TRIGGER episodes_fts_delete AFTER DELETE ON episodes BEGIN
|
|
4733
|
+
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)
|
|
4734
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, old.l2_context, old.l2_trigger,
|
|
4735
|
+
${jc("old.l2_principles")}, ${jc("old.l2_problems")}, ${jc("old.l2_desires")},
|
|
4736
|
+
${jc("old.l2_decisions")}, ${jc("old.l2_outcomes")},
|
|
4737
|
+
old.search_summary, old.user_input);
|
|
4738
|
+
END
|
|
4739
|
+
`);
|
|
4740
|
+
// --- decisions_fts ---
|
|
4741
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_insert`);
|
|
4742
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_update`);
|
|
4743
|
+
db.exec(`DROP TRIGGER IF EXISTS decisions_fts_delete`);
|
|
4744
|
+
db.exec(`DROP TABLE IF EXISTS decisions_fts`);
|
|
4745
|
+
db.exec(`
|
|
4746
|
+
CREATE VIRTUAL TABLE decisions_fts USING fts5(
|
|
4747
|
+
title, description, l1_content, l2_reasoning, search_summary, user_input,
|
|
4748
|
+
content='decisions', content_rowid='rowid', tokenize='trigram'
|
|
4749
|
+
)
|
|
4750
|
+
`);
|
|
4751
|
+
db.exec(`
|
|
4752
|
+
CREATE TRIGGER decisions_fts_insert AFTER INSERT ON decisions BEGIN
|
|
4753
|
+
INSERT INTO decisions_fts(rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
4754
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_reasoning, new.search_summary, new.user_input);
|
|
4755
|
+
END
|
|
4756
|
+
`);
|
|
4757
|
+
db.exec(`
|
|
4758
|
+
CREATE TRIGGER decisions_fts_update AFTER UPDATE ON decisions BEGIN
|
|
4759
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
4760
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_reasoning, old.search_summary, old.user_input);
|
|
4761
|
+
INSERT INTO decisions_fts(rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
4762
|
+
VALUES (new.rowid, new.title, new.description, new.l1_content, new.l2_reasoning, new.search_summary, new.user_input);
|
|
4763
|
+
END
|
|
4764
|
+
`);
|
|
4765
|
+
db.exec(`
|
|
4766
|
+
CREATE TRIGGER decisions_fts_delete AFTER DELETE ON decisions BEGIN
|
|
4767
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, title, description, l1_content, l2_reasoning, search_summary, user_input)
|
|
4768
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.l1_content, old.l2_reasoning, old.search_summary, old.user_input);
|
|
4769
|
+
END
|
|
4770
|
+
`);
|
|
4771
|
+
// --- user_memos_fts ---
|
|
4772
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_fts_insert`);
|
|
4773
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_fts_update`);
|
|
4774
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_fts_delete`);
|
|
4775
|
+
db.exec(`DROP TABLE IF EXISTS user_memos_fts`);
|
|
4776
|
+
db.exec(`
|
|
4777
|
+
CREATE VIRTUAL TABLE user_memos_fts USING fts5(
|
|
4778
|
+
title, content, tags, search_summary,
|
|
4779
|
+
content='user_memos', content_rowid='rowid', tokenize='trigram'
|
|
4780
|
+
)
|
|
4781
|
+
`);
|
|
4782
|
+
db.exec(`
|
|
4783
|
+
CREATE TRIGGER user_memos_fts_insert AFTER INSERT ON user_memos BEGIN
|
|
4784
|
+
INSERT INTO user_memos_fts(rowid, title, content, tags, search_summary)
|
|
4785
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
4786
|
+
END
|
|
4787
|
+
`);
|
|
4788
|
+
db.exec(`
|
|
4789
|
+
CREATE TRIGGER user_memos_fts_update AFTER UPDATE ON user_memos BEGIN
|
|
4790
|
+
INSERT INTO user_memos_fts(user_memos_fts, rowid, title, content, tags, search_summary)
|
|
4791
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
4792
|
+
INSERT INTO user_memos_fts(rowid, title, content, tags, search_summary)
|
|
4793
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
4794
|
+
END
|
|
4795
|
+
`);
|
|
4796
|
+
db.exec(`
|
|
4797
|
+
CREATE TRIGGER user_memos_fts_delete AFTER DELETE ON user_memos BEGIN
|
|
4798
|
+
INSERT INTO user_memos_fts(user_memos_fts, rowid, title, content, tags, search_summary)
|
|
4799
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
4800
|
+
END
|
|
4801
|
+
`);
|
|
4802
|
+
// --- user_plans_fts ---
|
|
4803
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_fts_insert`);
|
|
4804
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_fts_update`);
|
|
4805
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_fts_delete`);
|
|
4806
|
+
db.exec(`DROP TABLE IF EXISTS user_plans_fts`);
|
|
4807
|
+
db.exec(`
|
|
4808
|
+
CREATE VIRTUAL TABLE user_plans_fts USING fts5(
|
|
4809
|
+
title, content, tags, search_summary,
|
|
4810
|
+
content='user_plans', content_rowid='rowid', tokenize='trigram'
|
|
4811
|
+
)
|
|
4812
|
+
`);
|
|
4813
|
+
db.exec(`
|
|
4814
|
+
CREATE TRIGGER user_plans_fts_insert AFTER INSERT ON user_plans BEGIN
|
|
4815
|
+
INSERT INTO user_plans_fts(rowid, title, content, tags, search_summary)
|
|
4816
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
4817
|
+
END
|
|
4818
|
+
`);
|
|
4819
|
+
db.exec(`
|
|
4820
|
+
CREATE TRIGGER user_plans_fts_update AFTER UPDATE ON user_plans BEGIN
|
|
4821
|
+
INSERT INTO user_plans_fts(user_plans_fts, rowid, title, content, tags, search_summary)
|
|
4822
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
4823
|
+
INSERT INTO user_plans_fts(rowid, title, content, tags, search_summary)
|
|
4824
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.tags")}, new.search_summary);
|
|
4825
|
+
END
|
|
4826
|
+
`);
|
|
4827
|
+
db.exec(`
|
|
4828
|
+
CREATE TRIGGER user_plans_fts_delete AFTER DELETE ON user_plans BEGIN
|
|
4829
|
+
INSERT INTO user_plans_fts(user_plans_fts, rowid, title, content, tags, search_summary)
|
|
4830
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.tags")}, old.search_summary);
|
|
4831
|
+
END
|
|
4832
|
+
`);
|
|
4833
|
+
// --- user_issues_fts ---
|
|
4834
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_fts_insert`);
|
|
4835
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_fts_update`);
|
|
4836
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_fts_delete`);
|
|
4837
|
+
db.exec(`DROP TABLE IF EXISTS user_issues_fts`);
|
|
4838
|
+
db.exec(`
|
|
4839
|
+
CREATE VIRTUAL TABLE user_issues_fts USING fts5(
|
|
4840
|
+
title, content, entries, tags, search_summary,
|
|
4841
|
+
content='user_issues', content_rowid='rowid', tokenize='trigram'
|
|
4842
|
+
)
|
|
4843
|
+
`);
|
|
4844
|
+
db.exec(`
|
|
4845
|
+
CREATE TRIGGER user_issues_fts_insert AFTER INSERT ON user_issues BEGIN
|
|
4846
|
+
INSERT INTO user_issues_fts(rowid, title, content, entries, tags, search_summary)
|
|
4847
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.entries")}, ${jc("new.tags")}, new.search_summary);
|
|
4848
|
+
END
|
|
4849
|
+
`);
|
|
4850
|
+
db.exec(`
|
|
4851
|
+
CREATE TRIGGER user_issues_fts_update AFTER UPDATE ON user_issues BEGIN
|
|
4852
|
+
INSERT INTO user_issues_fts(user_issues_fts, rowid, title, content, entries, tags, search_summary)
|
|
4853
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.entries")}, ${jc("old.tags")}, old.search_summary);
|
|
4854
|
+
INSERT INTO user_issues_fts(rowid, title, content, entries, tags, search_summary)
|
|
4855
|
+
VALUES (new.rowid, new.title, new.content, ${jc("new.entries")}, ${jc("new.tags")}, new.search_summary);
|
|
4856
|
+
END
|
|
4857
|
+
`);
|
|
4858
|
+
db.exec(`
|
|
4859
|
+
CREATE TRIGGER user_issues_fts_delete AFTER DELETE ON user_issues BEGIN
|
|
4860
|
+
INSERT INTO user_issues_fts(user_issues_fts, rowid, title, content, entries, tags, search_summary)
|
|
4861
|
+
VALUES ('delete', old.rowid, old.title, old.content, ${jc("old.entries")}, ${jc("old.tags")}, old.search_summary);
|
|
4862
|
+
END
|
|
4863
|
+
`);
|
|
4864
|
+
// 2026-03-03 修正 (Issue #64): claims の search に l1_content を追加
|
|
4865
|
+
const v23Entities = [
|
|
4866
|
+
{
|
|
4867
|
+
type: "claim", table: "claims",
|
|
4868
|
+
category: (p) => `${p}category`,
|
|
4869
|
+
title: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object`,
|
|
4870
|
+
search: (p) => `${p}subject || ' ' || ${p}predicate || ' ' || ${p}object || ' ' || ${coal(`${p}evidence`)} || ' ' || ${coal(`${p}falsifier`)} || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
4871
|
+
tags: () => `'[]'`,
|
|
4872
|
+
},
|
|
4873
|
+
{
|
|
4874
|
+
type: "episode", table: "episodes",
|
|
4875
|
+
category: () => `NULL`,
|
|
4876
|
+
title: (p) => `${p}title`,
|
|
4877
|
+
search: (p) => `${p}title || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${coal(`${p}l2_context`)} || ' ' || ${coal(`${p}l2_trigger`)} || ' ' || ${jc(`${p}l2_problems`)} || ' ' || ${jc(`${p}l2_desires`)} || ' ' || ${jc(`${p}l2_decisions`)} || ' ' || ${jc(`${p}l2_outcomes`)} || ' ' || ${jc(`${p}l2_principles`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)} || ' ' || ${coal(`${p}user_input`)}`,
|
|
4878
|
+
tags: (p) => `${p}tags`,
|
|
4879
|
+
},
|
|
4880
|
+
{
|
|
4881
|
+
type: "decision", table: "decisions",
|
|
4882
|
+
category: () => `NULL`,
|
|
4883
|
+
title: (p) => `${p}title`,
|
|
4884
|
+
search: (p) => `${p}title || ' ' || ${p}description || ' ' || ${coal(`${p}l1_content`)} || ' ' || ${p}l2_reasoning || ' ' || ${jc(`${p}l2_alternatives`)} || ' ' || ${coal(`${p}search_summary`)} || ' ' || ${coal(`${p}user_input`)}`,
|
|
4885
|
+
tags: () => `'[]'`,
|
|
4886
|
+
},
|
|
4887
|
+
{
|
|
4888
|
+
type: "theory", table: "theories",
|
|
4889
|
+
category: () => `NULL`,
|
|
4890
|
+
title: (p) => `${p}title`,
|
|
4891
|
+
search: (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}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
4892
|
+
tags: (p) => `${p}tags`,
|
|
4893
|
+
},
|
|
4894
|
+
{
|
|
4895
|
+
type: "insight", table: "insights",
|
|
4896
|
+
category: () => `NULL`,
|
|
4897
|
+
title: (p) => `${p}title`,
|
|
4898
|
+
search: (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`)}`,
|
|
4899
|
+
tags: (p) => `${p}tags`,
|
|
4900
|
+
},
|
|
4901
|
+
{
|
|
4902
|
+
type: "model", table: "models",
|
|
4903
|
+
category: () => `NULL`,
|
|
4904
|
+
title: (p) => `${p}title`,
|
|
4905
|
+
search: (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}non_goals`)} || ' ' || ${jc(`${p}open_questions`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
4906
|
+
tags: (p) => `${p}tags`,
|
|
4907
|
+
},
|
|
4908
|
+
{
|
|
4909
|
+
type: "user_memo", table: "user_memos",
|
|
4910
|
+
category: () => `NULL`,
|
|
4911
|
+
title: (p) => `${p}title`,
|
|
4912
|
+
search: (p) => `${p}title || ' ' || ${p}content || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
4913
|
+
tags: (p) => `${p}tags`,
|
|
4914
|
+
statusCondition: "AND new.usage_policy != 'human_directed'",
|
|
4915
|
+
},
|
|
4916
|
+
{
|
|
4917
|
+
type: "user_plan", table: "user_plans",
|
|
4918
|
+
category: () => `NULL`,
|
|
4919
|
+
title: (p) => `${p}title`,
|
|
4920
|
+
search: (p) => `${p}title || ' ' || ${p}content || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
4921
|
+
tags: (p) => `${p}tags`,
|
|
4922
|
+
statusCondition: "AND new.usage_policy != 'human_directed'",
|
|
4923
|
+
},
|
|
4924
|
+
{
|
|
4925
|
+
type: "user_issue", table: "user_issues",
|
|
4926
|
+
category: () => `NULL`,
|
|
4927
|
+
title: (p) => `${p}title`,
|
|
4928
|
+
search: (p) => `${p}title || ' ' || ${p}content || ' ' || ${jc(`${p}entries`)} || ' ' || ${jc(`${p}tags`)} || ' ' || ${coal(`${p}search_summary`)}`,
|
|
4929
|
+
tags: (p) => `${p}tags`,
|
|
4930
|
+
statusField: "open",
|
|
4931
|
+
},
|
|
4932
|
+
];
|
|
4933
|
+
for (const e of v23Entities) {
|
|
4934
|
+
const activeStatus = e.statusField ?? "active";
|
|
4935
|
+
const insertWhen = e.statusCondition
|
|
4936
|
+
? `WHEN new.status = '${activeStatus}' ${e.statusCondition}`
|
|
4937
|
+
: `WHEN new.status = '${activeStatus}'`;
|
|
4938
|
+
const updateWhere = e.statusCondition
|
|
4939
|
+
? `WHERE new.status = '${activeStatus}' ${e.statusCondition}`
|
|
4940
|
+
: `WHERE new.status = '${activeStatus}'`;
|
|
4941
|
+
db.exec(`
|
|
4942
|
+
CREATE TRIGGER ${e.table}_unified_insert AFTER INSERT ON ${e.table}
|
|
4943
|
+
${insertWhen} BEGIN
|
|
4944
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
4945
|
+
VALUES ('${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, new.search_summary, ${e.tags("new.")}, new.created_at, new.updated_at);
|
|
4946
|
+
END
|
|
4947
|
+
`);
|
|
4948
|
+
db.exec(`
|
|
4949
|
+
CREATE TRIGGER ${e.table}_unified_update AFTER UPDATE ON ${e.table} BEGIN
|
|
4950
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
4951
|
+
INSERT INTO unified_search_items(${columns})
|
|
4952
|
+
SELECT '${e.type}', new.id, new.scope, ${e.category("new.")}, ${e.title("new.")}, ${e.search("new.")}, new.search_summary, ${e.tags("new.")}, new.created_at, new.updated_at
|
|
4953
|
+
${updateWhere};
|
|
4954
|
+
END
|
|
4955
|
+
`);
|
|
4956
|
+
db.exec(`
|
|
4957
|
+
CREATE TRIGGER ${e.table}_unified_delete AFTER DELETE ON ${e.table} BEGIN
|
|
4958
|
+
DELETE FROM unified_search_items WHERE entity_type = '${e.type}' AND entity_id = old.id;
|
|
4959
|
+
END
|
|
4960
|
+
`);
|
|
4961
|
+
}
|
|
4962
|
+
// ========================================================
|
|
4963
|
+
// 1-N: FTS リビルド + unified_search_items 再挿入 + バージョン記録
|
|
4964
|
+
// ========================================================
|
|
4965
|
+
db.exec(`INSERT INTO claims_fts(claims_fts) VALUES('rebuild')`);
|
|
4966
|
+
db.exec(`INSERT INTO theories_fts(theories_fts) VALUES('rebuild')`);
|
|
4967
|
+
db.exec(`INSERT INTO models_fts(models_fts) VALUES('rebuild')`);
|
|
4968
|
+
db.exec(`INSERT INTO insights_fts(insights_fts) VALUES('rebuild')`);
|
|
4969
|
+
db.exec(`INSERT INTO episodes_fts(episodes_fts) VALUES('rebuild')`);
|
|
4970
|
+
db.exec(`INSERT INTO decisions_fts(decisions_fts) VALUES('rebuild')`);
|
|
4971
|
+
db.exec(`INSERT INTO user_memos_fts(user_memos_fts) VALUES('rebuild')`);
|
|
4972
|
+
db.exec(`INSERT INTO user_plans_fts(user_plans_fts) VALUES('rebuild')`);
|
|
4973
|
+
db.exec(`INSERT INTO user_issues_fts(user_issues_fts) VALUES('rebuild')`);
|
|
4974
|
+
// unified_search_items の全エンティティを再構築(claims の search_text に l1_content が追加されたため)
|
|
4975
|
+
// DELETE 時に空の FTS への 'delete' 発行で malformed エラーになるため、トリガーを一時的に外す
|
|
4976
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_delete`);
|
|
4977
|
+
db.exec(`DELETE FROM unified_search_items`);
|
|
4978
|
+
for (const e of v23Entities) {
|
|
4979
|
+
const activeStatus = e.statusField ?? "active";
|
|
4980
|
+
const usagePolicyFilter = e.statusCondition ? " AND usage_policy != 'human_directed'" : "";
|
|
4981
|
+
db.exec(`
|
|
4982
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
4983
|
+
SELECT '${e.type}', id, scope, ${e.category("")}, ${e.title("")}, ${e.search("")}, search_summary, ${e.tags("")}, created_at, updated_at
|
|
4984
|
+
FROM ${e.table}
|
|
4985
|
+
WHERE status = '${activeStatus}'${usagePolicyFilter}
|
|
4986
|
+
`);
|
|
4987
|
+
}
|
|
4988
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
4989
|
+
// DELETE 前に外した FTS delete トリガーを再作成
|
|
4990
|
+
db.exec(`
|
|
4991
|
+
CREATE TRIGGER unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
4992
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
4993
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
4994
|
+
END
|
|
4995
|
+
`);
|
|
4996
|
+
// --- 外部キー整合性チェック ---
|
|
4997
|
+
const fkViolations = db.pragma("foreign_key_check");
|
|
4998
|
+
if (fkViolations.length > 0) {
|
|
4999
|
+
throw new Error(`V23マイグレーション後に外部キー違反が検出されました: ${JSON.stringify(fkViolations)}`);
|
|
5000
|
+
}
|
|
5001
|
+
// --- スキーマバージョン記録 ---
|
|
5002
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (23)`);
|
|
5003
|
+
});
|
|
5004
|
+
transaction();
|
|
5005
|
+
}
|
|
5006
|
+
finally {
|
|
5007
|
+
// legacy_alter_table を元に戻す(V23 で ON にしたため)
|
|
5008
|
+
db.pragma("legacy_alter_table = OFF");
|
|
5009
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
5010
|
+
db.pragma("foreign_keys = ON");
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
5014
|
+
/**
|
|
5015
|
+
* V24: user_memo/plan/issue の content → l1_content リネーム + user_memos に user_input 追加 (Issue #54)
|
|
5016
|
+
*/
|
|
5017
|
+
function applyV24(db) {
|
|
5018
|
+
const fkState = db.pragma("foreign_keys");
|
|
5019
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
5020
|
+
db.pragma("foreign_keys = OFF");
|
|
5021
|
+
}
|
|
5022
|
+
db.pragma("legacy_alter_table = ON");
|
|
5023
|
+
try {
|
|
5024
|
+
const transaction = db.transaction(() => {
|
|
5025
|
+
const jc = (col) => `replace(replace(replace(${col}, '["',''), '"]',''), '","',' ')`;
|
|
5026
|
+
const coal = (col) => `COALESCE(${col}, '')`;
|
|
5027
|
+
const columns = "entity_type, entity_id, scope, category, title_summary, search_text, search_summary, tags, created_at, updated_at";
|
|
5028
|
+
// ========================================================
|
|
5029
|
+
// 1. user_memos: content → l1_content, user_input 追加
|
|
5030
|
+
// ========================================================
|
|
5031
|
+
db.exec(`ALTER TABLE user_memos RENAME TO user_memos_old`);
|
|
5032
|
+
db.exec(`
|
|
5033
|
+
CREATE TABLE user_memos (
|
|
5034
|
+
id TEXT PRIMARY KEY,
|
|
5035
|
+
title TEXT NOT NULL,
|
|
5036
|
+
l1_content TEXT NOT NULL,
|
|
5037
|
+
usage_policy TEXT NOT NULL DEFAULT 'on_request'
|
|
5038
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
5039
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
5040
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
5041
|
+
search_summary TEXT,
|
|
5042
|
+
user_input TEXT,
|
|
5043
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
5044
|
+
CHECK(status IN ('active','archived')),
|
|
5045
|
+
l1_embedding BLOB,
|
|
5046
|
+
client_name TEXT,
|
|
5047
|
+
client_version TEXT,
|
|
5048
|
+
source_tool TEXT,
|
|
5049
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
5050
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
5051
|
+
)
|
|
5052
|
+
`);
|
|
5053
|
+
db.exec(`
|
|
5054
|
+
INSERT INTO user_memos (
|
|
5055
|
+
id, title, l1_content, usage_policy, tags, scope, search_summary, user_input,
|
|
5056
|
+
status, l1_embedding, client_name, client_version, source_tool,
|
|
5057
|
+
created_at, updated_at
|
|
5058
|
+
)
|
|
5059
|
+
SELECT
|
|
5060
|
+
id, title, content, usage_policy, tags, scope, search_summary, NULL,
|
|
5061
|
+
status, l1_embedding, client_name, client_version, source_tool,
|
|
5062
|
+
created_at, updated_at
|
|
5063
|
+
FROM user_memos_old
|
|
5064
|
+
`);
|
|
5065
|
+
db.exec(`DROP TABLE user_memos_old`);
|
|
5066
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_memos_status ON user_memos(status)`);
|
|
5067
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_memos_scope ON user_memos(scope)`);
|
|
5068
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_memos_usage_policy ON user_memos(usage_policy)`);
|
|
5069
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_memos_updated ON user_memos(updated_at)`);
|
|
5070
|
+
// ========================================================
|
|
5071
|
+
// 2. user_plans: content → l1_content
|
|
5072
|
+
// ========================================================
|
|
5073
|
+
db.exec(`ALTER TABLE user_plans RENAME TO user_plans_old`);
|
|
5074
|
+
db.exec(`
|
|
5075
|
+
CREATE TABLE user_plans (
|
|
5076
|
+
id TEXT PRIMARY KEY,
|
|
5077
|
+
title TEXT NOT NULL,
|
|
5078
|
+
l1_content TEXT NOT NULL,
|
|
5079
|
+
usage_policy TEXT NOT NULL DEFAULT 'auto'
|
|
5080
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
5081
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
5082
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
5083
|
+
search_summary TEXT,
|
|
5084
|
+
user_input TEXT,
|
|
5085
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
5086
|
+
CHECK(status IN ('active','completed','archived')),
|
|
5087
|
+
l1_embedding BLOB,
|
|
5088
|
+
client_name TEXT,
|
|
5089
|
+
client_version TEXT,
|
|
5090
|
+
source_tool TEXT,
|
|
5091
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
5092
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
5093
|
+
)
|
|
5094
|
+
`);
|
|
5095
|
+
db.exec(`
|
|
5096
|
+
INSERT INTO user_plans (
|
|
5097
|
+
id, title, l1_content, usage_policy, tags, scope, search_summary, user_input,
|
|
5098
|
+
status, l1_embedding, client_name, client_version, source_tool,
|
|
5099
|
+
created_at, updated_at
|
|
5100
|
+
)
|
|
5101
|
+
SELECT
|
|
5102
|
+
id, title, content, usage_policy, tags, scope, search_summary, user_input,
|
|
5103
|
+
status, l1_embedding, client_name, client_version, source_tool,
|
|
5104
|
+
created_at, updated_at
|
|
5105
|
+
FROM user_plans_old
|
|
5106
|
+
`);
|
|
5107
|
+
db.exec(`DROP TABLE user_plans_old`);
|
|
5108
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_plans_status ON user_plans(status)`);
|
|
5109
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_plans_scope ON user_plans(scope)`);
|
|
5110
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_plans_usage_policy ON user_plans(usage_policy)`);
|
|
5111
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_plans_updated ON user_plans(updated_at)`);
|
|
5112
|
+
// ========================================================
|
|
5113
|
+
// 3. user_issues: content → l1_content
|
|
5114
|
+
// ========================================================
|
|
5115
|
+
db.exec(`ALTER TABLE user_issues RENAME TO user_issues_old`);
|
|
5116
|
+
db.exec(`
|
|
5117
|
+
CREATE TABLE user_issues (
|
|
5118
|
+
id TEXT PRIMARY KEY,
|
|
5119
|
+
title TEXT NOT NULL,
|
|
5120
|
+
l1_content TEXT NOT NULL,
|
|
5121
|
+
entries TEXT NOT NULL DEFAULT '[]',
|
|
5122
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
5123
|
+
CHECK(priority IN ('low','medium','high','critical')),
|
|
5124
|
+
usage_policy TEXT NOT NULL DEFAULT 'on_request'
|
|
5125
|
+
CHECK(usage_policy IN ('auto','on_request','human_directed')),
|
|
5126
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
5127
|
+
scope TEXT NOT NULL DEFAULT 'global',
|
|
5128
|
+
search_summary TEXT,
|
|
5129
|
+
user_input TEXT,
|
|
5130
|
+
status TEXT NOT NULL DEFAULT 'open'
|
|
5131
|
+
CHECK(status IN ('open','closed','archived')),
|
|
5132
|
+
l1_embedding BLOB,
|
|
5133
|
+
client_name TEXT,
|
|
5134
|
+
client_version TEXT,
|
|
5135
|
+
source_tool TEXT,
|
|
5136
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
5137
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
5138
|
+
)
|
|
5139
|
+
`);
|
|
5140
|
+
db.exec(`
|
|
5141
|
+
INSERT INTO user_issues (
|
|
5142
|
+
id, title, l1_content, entries, priority, usage_policy, tags, scope, search_summary, user_input,
|
|
5143
|
+
status, l1_embedding, client_name, client_version, source_tool,
|
|
5144
|
+
created_at, updated_at
|
|
5145
|
+
)
|
|
5146
|
+
SELECT
|
|
5147
|
+
id, title, content, entries, priority, usage_policy, tags, scope, search_summary, user_input,
|
|
5148
|
+
status, l1_embedding, client_name, client_version, source_tool,
|
|
5149
|
+
created_at, updated_at
|
|
5150
|
+
FROM user_issues_old
|
|
5151
|
+
`);
|
|
5152
|
+
db.exec(`DROP TABLE user_issues_old`);
|
|
5153
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_issues_status ON user_issues(status)`);
|
|
5154
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_issues_scope ON user_issues(scope)`);
|
|
5155
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_issues_priority ON user_issues(priority)`);
|
|
5156
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_issues_updated ON user_issues(updated_at)`);
|
|
5157
|
+
// ========================================================
|
|
5158
|
+
// 4. FTS 再構築(content → l1_content, user_memos に user_input 追加)
|
|
5159
|
+
// ========================================================
|
|
5160
|
+
// --- user_memos_fts ---
|
|
5161
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_fts_insert`);
|
|
5162
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_fts_update`);
|
|
5163
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_fts_delete`);
|
|
5164
|
+
db.exec(`DROP TABLE IF EXISTS user_memos_fts`);
|
|
5165
|
+
db.exec(`
|
|
5166
|
+
CREATE VIRTUAL TABLE user_memos_fts USING fts5(
|
|
5167
|
+
title, l1_content, tags, search_summary, user_input,
|
|
5168
|
+
content='user_memos', content_rowid='rowid', tokenize='trigram'
|
|
5169
|
+
)
|
|
5170
|
+
`);
|
|
5171
|
+
db.exec(`
|
|
5172
|
+
CREATE TRIGGER user_memos_fts_insert AFTER INSERT ON user_memos BEGIN
|
|
5173
|
+
INSERT INTO user_memos_fts(rowid, title, l1_content, tags, search_summary, user_input)
|
|
5174
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.tags")}, new.search_summary, new.user_input);
|
|
5175
|
+
END
|
|
5176
|
+
`);
|
|
5177
|
+
db.exec(`
|
|
5178
|
+
CREATE TRIGGER user_memos_fts_update AFTER UPDATE ON user_memos BEGIN
|
|
5179
|
+
INSERT INTO user_memos_fts(user_memos_fts, rowid, title, l1_content, tags, search_summary, user_input)
|
|
5180
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.tags")}, old.search_summary, old.user_input);
|
|
5181
|
+
INSERT INTO user_memos_fts(rowid, title, l1_content, tags, search_summary, user_input)
|
|
5182
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.tags")}, new.search_summary, new.user_input);
|
|
5183
|
+
END
|
|
5184
|
+
`);
|
|
5185
|
+
db.exec(`
|
|
5186
|
+
CREATE TRIGGER user_memos_fts_delete AFTER DELETE ON user_memos BEGIN
|
|
5187
|
+
INSERT INTO user_memos_fts(user_memos_fts, rowid, title, l1_content, tags, search_summary, user_input)
|
|
5188
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.tags")}, old.search_summary, old.user_input);
|
|
5189
|
+
END
|
|
5190
|
+
`);
|
|
5191
|
+
// --- user_plans_fts ---
|
|
5192
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_fts_insert`);
|
|
5193
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_fts_update`);
|
|
5194
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_fts_delete`);
|
|
5195
|
+
db.exec(`DROP TABLE IF EXISTS user_plans_fts`);
|
|
5196
|
+
db.exec(`
|
|
5197
|
+
CREATE VIRTUAL TABLE user_plans_fts USING fts5(
|
|
5198
|
+
title, l1_content, tags, search_summary,
|
|
5199
|
+
content='user_plans', content_rowid='rowid', tokenize='trigram'
|
|
5200
|
+
)
|
|
5201
|
+
`);
|
|
5202
|
+
db.exec(`
|
|
5203
|
+
CREATE TRIGGER user_plans_fts_insert AFTER INSERT ON user_plans BEGIN
|
|
5204
|
+
INSERT INTO user_plans_fts(rowid, title, l1_content, tags, search_summary)
|
|
5205
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.tags")}, new.search_summary);
|
|
5206
|
+
END
|
|
5207
|
+
`);
|
|
5208
|
+
db.exec(`
|
|
5209
|
+
CREATE TRIGGER user_plans_fts_update AFTER UPDATE ON user_plans BEGIN
|
|
5210
|
+
INSERT INTO user_plans_fts(user_plans_fts, rowid, title, l1_content, tags, search_summary)
|
|
5211
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.tags")}, old.search_summary);
|
|
5212
|
+
INSERT INTO user_plans_fts(rowid, title, l1_content, tags, search_summary)
|
|
5213
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.tags")}, new.search_summary);
|
|
5214
|
+
END
|
|
5215
|
+
`);
|
|
5216
|
+
db.exec(`
|
|
5217
|
+
CREATE TRIGGER user_plans_fts_delete AFTER DELETE ON user_plans BEGIN
|
|
5218
|
+
INSERT INTO user_plans_fts(user_plans_fts, rowid, title, l1_content, tags, search_summary)
|
|
5219
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.tags")}, old.search_summary);
|
|
5220
|
+
END
|
|
5221
|
+
`);
|
|
5222
|
+
// --- user_issues_fts ---
|
|
5223
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_fts_insert`);
|
|
5224
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_fts_update`);
|
|
5225
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_fts_delete`);
|
|
5226
|
+
db.exec(`DROP TABLE IF EXISTS user_issues_fts`);
|
|
5227
|
+
db.exec(`
|
|
5228
|
+
CREATE VIRTUAL TABLE user_issues_fts USING fts5(
|
|
5229
|
+
title, l1_content, entries, tags, search_summary,
|
|
5230
|
+
content='user_issues', content_rowid='rowid', tokenize='trigram'
|
|
5231
|
+
)
|
|
5232
|
+
`);
|
|
5233
|
+
db.exec(`
|
|
5234
|
+
CREATE TRIGGER user_issues_fts_insert AFTER INSERT ON user_issues BEGIN
|
|
5235
|
+
INSERT INTO user_issues_fts(rowid, title, l1_content, entries, tags, search_summary)
|
|
5236
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.entries")}, ${jc("new.tags")}, new.search_summary);
|
|
5237
|
+
END
|
|
5238
|
+
`);
|
|
5239
|
+
db.exec(`
|
|
5240
|
+
CREATE TRIGGER user_issues_fts_update AFTER UPDATE ON user_issues BEGIN
|
|
5241
|
+
INSERT INTO user_issues_fts(user_issues_fts, rowid, title, l1_content, entries, tags, search_summary)
|
|
5242
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.entries")}, ${jc("old.tags")}, old.search_summary);
|
|
5243
|
+
INSERT INTO user_issues_fts(rowid, title, l1_content, entries, tags, search_summary)
|
|
5244
|
+
VALUES (new.rowid, new.title, new.l1_content, ${jc("new.entries")}, ${jc("new.tags")}, new.search_summary);
|
|
5245
|
+
END
|
|
5246
|
+
`);
|
|
5247
|
+
db.exec(`
|
|
5248
|
+
CREATE TRIGGER user_issues_fts_delete AFTER DELETE ON user_issues BEGIN
|
|
5249
|
+
INSERT INTO user_issues_fts(user_issues_fts, rowid, title, l1_content, entries, tags, search_summary)
|
|
5250
|
+
VALUES ('delete', old.rowid, old.title, old.l1_content, ${jc("old.entries")}, ${jc("old.tags")}, old.search_summary);
|
|
5251
|
+
END
|
|
5252
|
+
`);
|
|
5253
|
+
// FTS リビルド
|
|
5254
|
+
db.exec(`INSERT INTO user_memos_fts(user_memos_fts) VALUES('rebuild')`);
|
|
5255
|
+
db.exec(`INSERT INTO user_plans_fts(user_plans_fts) VALUES('rebuild')`);
|
|
5256
|
+
db.exec(`INSERT INTO user_issues_fts(user_issues_fts) VALUES('rebuild')`);
|
|
5257
|
+
// ========================================================
|
|
5258
|
+
// 5. unified_search_items トリガー再構築(user_memo/plan/issue の content → l1_content, user_memo に user_input 追加)
|
|
5259
|
+
// ========================================================
|
|
5260
|
+
// user_memos トリガー
|
|
5261
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_unified_insert`);
|
|
5262
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_unified_update`);
|
|
5263
|
+
db.exec(`DROP TRIGGER IF EXISTS user_memos_unified_delete`);
|
|
5264
|
+
db.exec(`
|
|
5265
|
+
CREATE TRIGGER user_memos_unified_insert AFTER INSERT ON user_memos
|
|
5266
|
+
WHEN new.status = 'active' AND new.usage_policy != 'human_directed' BEGIN
|
|
5267
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
5268
|
+
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);
|
|
5269
|
+
END
|
|
5270
|
+
`);
|
|
5271
|
+
db.exec(`
|
|
5272
|
+
CREATE TRIGGER user_memos_unified_update AFTER UPDATE ON user_memos BEGIN
|
|
5273
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_memo' AND entity_id = old.id;
|
|
5274
|
+
INSERT INTO unified_search_items(${columns})
|
|
5275
|
+
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
|
|
5276
|
+
WHERE new.status = 'active' AND new.usage_policy != 'human_directed';
|
|
5277
|
+
END
|
|
5278
|
+
`);
|
|
5279
|
+
db.exec(`
|
|
5280
|
+
CREATE TRIGGER user_memos_unified_delete AFTER DELETE ON user_memos BEGIN
|
|
5281
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_memo' AND entity_id = old.id;
|
|
5282
|
+
END
|
|
5283
|
+
`);
|
|
5284
|
+
// user_plans トリガー
|
|
5285
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_unified_insert`);
|
|
5286
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_unified_update`);
|
|
5287
|
+
db.exec(`DROP TRIGGER IF EXISTS user_plans_unified_delete`);
|
|
5288
|
+
db.exec(`
|
|
5289
|
+
CREATE TRIGGER user_plans_unified_insert AFTER INSERT ON user_plans
|
|
5290
|
+
WHEN new.status = 'active' AND new.usage_policy != 'human_directed' BEGIN
|
|
5291
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
5292
|
+
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);
|
|
5293
|
+
END
|
|
5294
|
+
`);
|
|
5295
|
+
db.exec(`
|
|
5296
|
+
CREATE TRIGGER user_plans_unified_update AFTER UPDATE ON user_plans BEGIN
|
|
5297
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_plan' AND entity_id = old.id;
|
|
5298
|
+
INSERT INTO unified_search_items(${columns})
|
|
5299
|
+
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
|
|
5300
|
+
WHERE new.status = 'active' AND new.usage_policy != 'human_directed';
|
|
5301
|
+
END
|
|
5302
|
+
`);
|
|
5303
|
+
db.exec(`
|
|
5304
|
+
CREATE TRIGGER user_plans_unified_delete AFTER DELETE ON user_plans BEGIN
|
|
5305
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_plan' AND entity_id = old.id;
|
|
5306
|
+
END
|
|
5307
|
+
`);
|
|
5308
|
+
// user_issues トリガー
|
|
5309
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_unified_insert`);
|
|
5310
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_unified_update`);
|
|
5311
|
+
db.exec(`DROP TRIGGER IF EXISTS user_issues_unified_delete`);
|
|
5312
|
+
db.exec(`
|
|
5313
|
+
CREATE TRIGGER user_issues_unified_insert AFTER INSERT ON user_issues
|
|
5314
|
+
WHEN new.status = 'open' BEGIN
|
|
5315
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
5316
|
+
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);
|
|
5317
|
+
END
|
|
5318
|
+
`);
|
|
5319
|
+
db.exec(`
|
|
5320
|
+
CREATE TRIGGER user_issues_unified_update AFTER UPDATE ON user_issues BEGIN
|
|
5321
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_issue' AND entity_id = old.id;
|
|
5322
|
+
INSERT INTO unified_search_items(${columns})
|
|
5323
|
+
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
|
|
5324
|
+
WHERE new.status = 'open';
|
|
5325
|
+
END
|
|
5326
|
+
`);
|
|
5327
|
+
db.exec(`
|
|
5328
|
+
CREATE TRIGGER user_issues_unified_delete AFTER DELETE ON user_issues BEGIN
|
|
5329
|
+
DELETE FROM unified_search_items WHERE entity_type = 'user_issue' AND entity_id = old.id;
|
|
5330
|
+
END
|
|
5331
|
+
`);
|
|
5332
|
+
// unified_search_items を再挿入(user_memo/plan/issue の search_text 更新のため)
|
|
5333
|
+
// DELETE 時のFTS 'delete' malformed エラー防止: トリガーを一時的に外す
|
|
5334
|
+
db.exec(`DROP TRIGGER IF EXISTS unified_search_fts_delete`);
|
|
5335
|
+
db.exec(`DELETE FROM unified_search_items WHERE entity_type IN ('user_memo', 'user_plan', 'user_issue')`);
|
|
5336
|
+
db.exec(`
|
|
5337
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
5338
|
+
SELECT 'user_memo', id, scope, NULL, title,
|
|
5339
|
+
title || ' ' || l1_content || ' ' || ${coal("user_input")} || ' ' || ${jc("tags")} || ' ' || ${coal("search_summary")},
|
|
5340
|
+
search_summary, tags, created_at, updated_at
|
|
5341
|
+
FROM user_memos
|
|
5342
|
+
WHERE status = 'active' AND usage_policy != 'human_directed'
|
|
5343
|
+
`);
|
|
5344
|
+
db.exec(`
|
|
5345
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
5346
|
+
SELECT 'user_plan', id, scope, NULL, title,
|
|
5347
|
+
title || ' ' || l1_content || ' ' || ${jc("tags")} || ' ' || ${coal("search_summary")},
|
|
5348
|
+
search_summary, tags, created_at, updated_at
|
|
5349
|
+
FROM user_plans
|
|
5350
|
+
WHERE status = 'active' AND usage_policy != 'human_directed'
|
|
5351
|
+
`);
|
|
5352
|
+
db.exec(`
|
|
5353
|
+
INSERT OR REPLACE INTO unified_search_items(${columns})
|
|
5354
|
+
SELECT 'user_issue', id, scope, NULL, title,
|
|
5355
|
+
title || ' ' || l1_content || ' ' || ${jc("entries")} || ' ' || ${jc("tags")} || ' ' || ${coal("search_summary")},
|
|
5356
|
+
search_summary, tags, created_at, updated_at
|
|
5357
|
+
FROM user_issues
|
|
5358
|
+
WHERE status = 'open'
|
|
5359
|
+
`);
|
|
5360
|
+
db.exec(`INSERT INTO unified_search_fts(unified_search_fts) VALUES('rebuild')`);
|
|
5361
|
+
// DELETE トリガーを再作成
|
|
5362
|
+
db.exec(`
|
|
5363
|
+
CREATE TRIGGER unified_search_fts_delete AFTER DELETE ON unified_search_items BEGIN
|
|
5364
|
+
INSERT INTO unified_search_fts(unified_search_fts, rowid, title_summary, search_text, search_summary)
|
|
5365
|
+
VALUES ('delete', old.rowid, old.title_summary, old.search_text, old.search_summary);
|
|
5366
|
+
END
|
|
5367
|
+
`);
|
|
5368
|
+
// --- 外部キー整合性チェック ---
|
|
5369
|
+
const fkViolations = db.pragma("foreign_key_check");
|
|
5370
|
+
if (fkViolations.length > 0) {
|
|
5371
|
+
throw new Error(`V24マイグレーション後に外部キー違反が検出されました: ${JSON.stringify(fkViolations)}`);
|
|
5372
|
+
}
|
|
5373
|
+
// --- スキーマバージョン記録 ---
|
|
5374
|
+
db.exec(`INSERT INTO schema_version (version) VALUES (24)`);
|
|
5375
|
+
});
|
|
5376
|
+
transaction();
|
|
5377
|
+
}
|
|
5378
|
+
finally {
|
|
5379
|
+
db.pragma("legacy_alter_table = OFF");
|
|
5380
|
+
if (fkState.length > 0 && fkState[0].foreign_keys === 1) {
|
|
5381
|
+
db.pragma("foreign_keys = ON");
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
1187
5385
|
/** テーブルに指定カラムが存在するかチェック */
|
|
1188
5386
|
function hasColumn(db, table, column) {
|
|
1189
5387
|
const columns = db.prepare(`PRAGMA table_info(${table})`).all();
|