@jungjaehoon/mama-core 1.4.2 → 1.6.0
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 +48 -10
- package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
- package/db/migrations/027-create-entity-review-support.sql +44 -0
- package/db/migrations/028-create-entity-audit-tables.sql +45 -0
- package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
- package/db/migrations/030-case-first-memory-substrate.sql +901 -0
- package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
- package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
- package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
- package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
- package/db/migrations/035-create-twin-edges.sql +63 -0
- package/db/migrations/036-create-agent-situation-packets.sql +61 -0
- package/dist/agent-graph/alias-write.d.ts +3 -0
- package/dist/agent-graph/alias-write.js +231 -0
- package/dist/agent-graph/entity-resolve.d.ts +3 -0
- package/dist/agent-graph/entity-resolve.js +275 -0
- package/dist/agent-graph/errors.d.ts +4 -0
- package/dist/agent-graph/errors.js +11 -0
- package/dist/agent-graph/graph-query.d.ts +5 -0
- package/dist/agent-graph/graph-query.js +359 -0
- package/dist/agent-graph/index.d.ts +6 -0
- package/dist/agent-graph/index.js +22 -0
- package/dist/agent-graph/types.d.ts +162 -0
- package/dist/agent-graph/types.js +3 -0
- package/dist/agent-situation/builder.d.ts +8 -0
- package/dist/agent-situation/builder.js +239 -0
- package/dist/agent-situation/cache-key.d.ts +4 -0
- package/dist/agent-situation/cache-key.js +64 -0
- package/dist/agent-situation/index.d.ts +7 -0
- package/dist/agent-situation/index.js +28 -0
- package/dist/agent-situation/packet-store.d.ts +27 -0
- package/dist/agent-situation/packet-store.js +228 -0
- package/dist/agent-situation/ranking-policy.d.ts +18 -0
- package/dist/agent-situation/ranking-policy.js +75 -0
- package/dist/agent-situation/source-readers.d.ts +52 -0
- package/dist/agent-situation/source-readers.js +300 -0
- package/dist/agent-situation/types.d.ts +195 -0
- package/dist/agent-situation/types.js +3 -0
- package/dist/canonicalize.d.ts +38 -0
- package/dist/canonicalize.js +106 -0
- package/dist/cases/case-links.d.ts +116 -0
- package/dist/cases/case-links.js +794 -0
- package/dist/cases/composition-overrides.d.ts +90 -0
- package/dist/cases/composition-overrides.js +724 -0
- package/dist/cases/corrections.d.ts +156 -0
- package/dist/cases/corrections.js +1045 -0
- package/dist/cases/freshness.d.ts +57 -0
- package/dist/cases/freshness.js +283 -0
- package/dist/cases/live-state.d.ts +48 -0
- package/dist/cases/live-state.js +256 -0
- package/dist/cases/membership-explain.d.ts +56 -0
- package/dist/cases/membership-explain.js +140 -0
- package/dist/cases/membership-matcher.d.ts +23 -0
- package/dist/cases/membership-matcher.js +193 -0
- package/dist/cases/merge-split.d.ts +61 -0
- package/dist/cases/merge-split.js +368 -0
- package/dist/cases/role-inference.d.ts +10 -0
- package/dist/cases/role-inference.js +85 -0
- package/dist/cases/search-rollup.d.ts +27 -0
- package/dist/cases/search-rollup.js +309 -0
- package/dist/cases/sqlite-transaction.d.ts +10 -0
- package/dist/cases/sqlite-transaction.js +62 -0
- package/dist/cases/store.d.ts +74 -0
- package/dist/cases/store.js +816 -0
- package/dist/cases/target-ref.d.ts +25 -0
- package/dist/cases/target-ref.js +22 -0
- package/dist/cases/timeline-range.d.ts +6 -0
- package/dist/cases/timeline-range.js +322 -0
- package/dist/cases/tombstone-sweeper.d.ts +13 -0
- package/dist/cases/tombstone-sweeper.js +128 -0
- package/dist/cases/types.d.ts +244 -0
- package/dist/cases/types.js +47 -0
- package/dist/cases/wiki-page-index.d.ts +50 -0
- package/dist/cases/wiki-page-index.js +350 -0
- package/dist/connectors/event-index.d.ts +45 -0
- package/dist/connectors/event-index.js +381 -0
- package/dist/connectors/raw-query.d.ts +18 -0
- package/dist/connectors/raw-query.js +305 -0
- package/dist/connectors/types.d.ts +109 -0
- package/dist/connectors/types.js +3 -0
- package/dist/db-adapter/node-sqlite-adapter.d.ts +6 -0
- package/dist/db-adapter/node-sqlite-adapter.js +131 -0
- package/dist/db-manager.d.ts +12 -1
- package/dist/db-manager.js +60 -5
- package/dist/decision-formatter.d.ts +2 -0
- package/dist/decision-formatter.js +11 -8
- package/dist/edges/ref-validation.d.ts +8 -0
- package/dist/edges/ref-validation.js +262 -0
- package/dist/edges/store.d.ts +10 -0
- package/dist/edges/store.js +343 -0
- package/dist/edges/types.d.ts +86 -0
- package/dist/edges/types.js +18 -0
- package/dist/embedding-server/mobile/daemon.d.ts +1 -0
- package/dist/embedding-server/mobile/daemon.js +11 -1
- package/dist/embeddings.js +13 -0
- package/dist/entities/audit-metrics.d.ts +73 -0
- package/dist/entities/audit-metrics.js +128 -0
- package/dist/entities/candidate-generator.d.ts +9 -0
- package/dist/entities/candidate-generator.js +436 -0
- package/dist/entities/entity-impact.d.ts +41 -0
- package/dist/entities/entity-impact.js +140 -0
- package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
- package/dist/entities/entity-linked-decision-counts.js +29 -0
- package/dist/entities/entity-list.d.ts +23 -0
- package/dist/entities/entity-list.js +99 -0
- package/dist/entities/entity-orphan-list.d.ts +26 -0
- package/dist/entities/entity-orphan-list.js +91 -0
- package/dist/entities/entity-search.d.ts +21 -0
- package/dist/entities/entity-search.js +150 -0
- package/dist/entities/errors.d.ts +65 -0
- package/dist/entities/errors.js +124 -0
- package/dist/entities/exact-merge-backfill.d.ts +11 -0
- package/dist/entities/exact-merge-backfill.js +161 -0
- package/dist/entities/lineage-backfill.d.ts +12 -0
- package/dist/entities/lineage-backfill.js +101 -0
- package/dist/entities/lineage-store.d.ts +51 -0
- package/dist/entities/lineage-store.js +308 -0
- package/dist/entities/normalization.d.ts +14 -0
- package/dist/entities/normalization.js +63 -0
- package/dist/entities/policy-store.d.ts +17 -0
- package/dist/entities/policy-store.js +263 -0
- package/dist/entities/policy-types.d.ts +72 -0
- package/dist/entities/policy-types.js +59 -0
- package/dist/entities/projection.d.ts +8 -0
- package/dist/entities/projection.js +82 -0
- package/dist/entities/provenance-query.d.ts +33 -0
- package/dist/entities/provenance-query.js +167 -0
- package/dist/entities/read-identity.d.ts +31 -0
- package/dist/entities/read-identity.js +264 -0
- package/dist/entities/recall-bridge.d.ts +5 -0
- package/dist/entities/recall-bridge.js +171 -0
- package/dist/entities/resolution-engine.d.ts +15 -0
- package/dist/entities/resolution-engine.js +69 -0
- package/dist/entities/rollback-preview.d.ts +39 -0
- package/dist/entities/rollback-preview.js +235 -0
- package/dist/entities/source-locator.d.ts +3 -0
- package/dist/entities/source-locator.js +32 -0
- package/dist/entities/store.d.ts +75 -0
- package/dist/entities/store.js +465 -0
- package/dist/entities/types.d.ts +162 -0
- package/dist/entities/types.js +30 -0
- package/dist/index.d.ts +62 -3
- package/dist/index.js +116 -26
- package/dist/mama-api.d.ts +53 -35
- package/dist/mama-api.js +437 -78
- package/dist/memory/api.d.ts +26 -38
- package/dist/memory/api.js +772 -68
- package/dist/memory/event-store.d.ts +3 -0
- package/dist/memory/event-store.js +21 -2
- package/dist/memory/provenance-audit.d.ts +22 -0
- package/dist/memory/provenance-audit.js +112 -0
- package/dist/memory/provenance-query.d.ts +11 -0
- package/dist/memory/provenance-query.js +193 -0
- package/dist/memory/provenance.d.ts +29 -0
- package/dist/memory/provenance.js +136 -0
- package/dist/memory/scope-backfill.d.ts +17 -0
- package/dist/memory/scope-backfill.js +77 -0
- package/dist/memory/types.d.ts +97 -16
- package/dist/model-runs/store.d.ts +13 -0
- package/dist/model-runs/store.js +457 -0
- package/dist/model-runs/tool-trace-store.d.ts +4 -0
- package/dist/model-runs/tool-trace-store.js +114 -0
- package/dist/model-runs/types.d.ts +69 -0
- package/dist/model-runs/types.js +5 -0
- package/dist/runtime/trusted-provenance.d.ts +3 -0
- package/dist/runtime/trusted-provenance.js +6 -0
- package/dist/search/feedback-store.d.ts +59 -0
- package/dist/search/feedback-store.js +211 -0
- package/dist/search/question-type.d.ts +5 -0
- package/dist/search/question-type.js +41 -0
- package/dist/search/ranker-features.d.ts +23 -0
- package/dist/search/ranker-features.js +104 -0
- package/dist/search/ranker-rescore.d.ts +31 -0
- package/dist/search/ranker-rescore.js +226 -0
- package/dist/search/ranker-trainer.d.ts +94 -0
- package/dist/search/ranker-trainer.js +860 -0
- package/dist/search/search-quality.d.ts +36 -0
- package/dist/search/search-quality.js +29 -0
- package/dist/test-utils.d.ts +14 -0
- package/dist/test-utils.js +52 -0
- package/package.json +28 -3
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
-- Migration 030: Case-First Memory Substrate (Phase 1 + 2 + 3)
|
|
2
|
+
-- Consolidated from pre-ship migrations 030..049.
|
|
3
|
+
-- Covers: entity substrate (decision_entity_sources, entity_lineage,
|
|
4
|
+
-- entity_policy), event_datetime column + backfill + created_at fill,
|
|
5
|
+
-- entity observation source-locator refactor, case_truth +
|
|
6
|
+
-- case_memberships + case_corrections + case_proposal_queue,
|
|
7
|
+
-- wiki page search index, entity_timeline_events.role,
|
|
8
|
+
-- connector_event_index, learned ranker + search feedback,
|
|
9
|
+
-- case_links + tombstones, case promotion/freshness columns,
|
|
10
|
+
-- case membership explanation columns.
|
|
11
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md
|
|
12
|
+
|
|
13
|
+
-- ================================================================
|
|
14
|
+
-- Entity substrate — decision_entity_sources
|
|
15
|
+
-- ================================================================
|
|
16
|
+
CREATE TABLE IF NOT EXISTS decision_entity_sources (
|
|
17
|
+
decision_id TEXT NOT NULL,
|
|
18
|
+
entity_observation_id TEXT NOT NULL,
|
|
19
|
+
relation_type TEXT NOT NULL DEFAULT 'support',
|
|
20
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
21
|
+
FOREIGN KEY (decision_id) REFERENCES decisions(id) ON DELETE CASCADE,
|
|
22
|
+
FOREIGN KEY (entity_observation_id) REFERENCES entity_observations(id) ON DELETE CASCADE
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_decision_entity_sources_unique
|
|
26
|
+
ON decision_entity_sources(decision_id, entity_observation_id, relation_type);
|
|
27
|
+
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_decision_entity_sources_decision
|
|
29
|
+
ON decision_entity_sources(decision_id);
|
|
30
|
+
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_decision_entity_sources_observation
|
|
32
|
+
ON decision_entity_sources(entity_observation_id);
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
-- ================================================================
|
|
36
|
+
-- Entity substrate — lineage tables
|
|
37
|
+
-- ================================================================
|
|
38
|
+
CREATE TABLE IF NOT EXISTS entity_ingest_runs (
|
|
39
|
+
id TEXT PRIMARY KEY,
|
|
40
|
+
connector TEXT NOT NULL,
|
|
41
|
+
run_kind TEXT NOT NULL CHECK (run_kind IN ('live', 'replay', 'backfill')),
|
|
42
|
+
status TEXT NOT NULL CHECK (status IN ('running', 'complete', 'failed')),
|
|
43
|
+
scope_key TEXT NOT NULL,
|
|
44
|
+
source_window_start INTEGER,
|
|
45
|
+
source_window_end INTEGER,
|
|
46
|
+
raw_count INTEGER NOT NULL DEFAULT 0,
|
|
47
|
+
observation_count INTEGER NOT NULL DEFAULT 0,
|
|
48
|
+
candidate_count INTEGER NOT NULL DEFAULT 0,
|
|
49
|
+
reviewable_count INTEGER NOT NULL DEFAULT 0,
|
|
50
|
+
audit_run_id TEXT,
|
|
51
|
+
audit_classification TEXT CHECK (audit_classification IN ('improved', 'stable', 'regressed', 'inconclusive')),
|
|
52
|
+
error_reason TEXT,
|
|
53
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
54
|
+
completed_at INTEGER,
|
|
55
|
+
FOREIGN KEY (audit_run_id) REFERENCES entity_audit_runs(id) ON DELETE SET NULL
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_entity_ingest_runs_connector_created_at
|
|
59
|
+
ON entity_ingest_runs(connector, created_at DESC);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_entity_ingest_runs_status_created_at
|
|
61
|
+
ON entity_ingest_runs(status, created_at DESC);
|
|
62
|
+
|
|
63
|
+
CREATE TABLE IF NOT EXISTS entity_lineage_links (
|
|
64
|
+
id TEXT PRIMARY KEY,
|
|
65
|
+
canonical_entity_id TEXT NOT NULL,
|
|
66
|
+
entity_observation_id TEXT NOT NULL,
|
|
67
|
+
source_entity_id TEXT,
|
|
68
|
+
contribution_kind TEXT NOT NULL CHECK (
|
|
69
|
+
contribution_kind IN ('seed', 'merge_adopt', 'manual_attach', 'rollback_restore')
|
|
70
|
+
),
|
|
71
|
+
run_id TEXT,
|
|
72
|
+
candidate_id TEXT,
|
|
73
|
+
review_action_id TEXT,
|
|
74
|
+
status TEXT NOT NULL CHECK (status IN ('active', 'superseded', 'rolled_back')),
|
|
75
|
+
capture_mode TEXT NOT NULL CHECK (capture_mode IN ('direct', 'backfilled')),
|
|
76
|
+
confidence REAL NOT NULL DEFAULT 1,
|
|
77
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
78
|
+
superseded_at INTEGER,
|
|
79
|
+
FOREIGN KEY (canonical_entity_id) REFERENCES entity_nodes(id) ON DELETE CASCADE,
|
|
80
|
+
FOREIGN KEY (entity_observation_id) REFERENCES entity_observations(id) ON DELETE CASCADE,
|
|
81
|
+
FOREIGN KEY (source_entity_id) REFERENCES entity_nodes(id) ON DELETE SET NULL,
|
|
82
|
+
FOREIGN KEY (run_id) REFERENCES entity_ingest_runs(id) ON DELETE SET NULL,
|
|
83
|
+
FOREIGN KEY (candidate_id) REFERENCES entity_resolution_candidates(id) ON DELETE SET NULL,
|
|
84
|
+
FOREIGN KEY (review_action_id) REFERENCES entity_merge_actions(id) ON DELETE SET NULL,
|
|
85
|
+
CHECK (confidence >= 0 AND confidence <= 1)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
CREATE INDEX IF NOT EXISTS idx_entity_lineage_links_entity_status_created_at
|
|
89
|
+
ON entity_lineage_links(canonical_entity_id, status, created_at DESC);
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_entity_lineage_links_observation
|
|
91
|
+
ON entity_lineage_links(entity_observation_id);
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_entity_lineage_links_run
|
|
93
|
+
ON entity_lineage_links(run_id);
|
|
94
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_entity_lineage_active_pair
|
|
95
|
+
ON entity_lineage_links(canonical_entity_id, entity_observation_id)
|
|
96
|
+
WHERE status = 'active';
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
-- ================================================================
|
|
100
|
+
-- Entity substrate — policy tables
|
|
101
|
+
-- ================================================================
|
|
102
|
+
CREATE TABLE IF NOT EXISTS entity_policy (
|
|
103
|
+
policy_key TEXT PRIMARY KEY,
|
|
104
|
+
policy_kind TEXT NOT NULL,
|
|
105
|
+
value_json TEXT NOT NULL,
|
|
106
|
+
version INTEGER NOT NULL,
|
|
107
|
+
created_at INTEGER NOT NULL,
|
|
108
|
+
updated_at INTEGER NOT NULL
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
CREATE TABLE IF NOT EXISTS entity_role_bindings (
|
|
112
|
+
actor_id TEXT PRIMARY KEY,
|
|
113
|
+
role TEXT NOT NULL,
|
|
114
|
+
created_at INTEGER NOT NULL,
|
|
115
|
+
updated_at INTEGER NOT NULL
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
CREATE TABLE IF NOT EXISTS entity_policy_proposals (
|
|
119
|
+
proposal_id TEXT PRIMARY KEY,
|
|
120
|
+
policy_key TEXT NOT NULL,
|
|
121
|
+
policy_kind TEXT NOT NULL,
|
|
122
|
+
proposed_value_json TEXT NOT NULL,
|
|
123
|
+
proposer_actor TEXT NOT NULL,
|
|
124
|
+
approver_actor TEXT,
|
|
125
|
+
reason TEXT NOT NULL,
|
|
126
|
+
status TEXT NOT NULL,
|
|
127
|
+
created_at INTEGER NOT NULL,
|
|
128
|
+
approved_at INTEGER
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
CREATE INDEX IF NOT EXISTS idx_entity_policy_kind
|
|
132
|
+
ON entity_policy(policy_kind, updated_at DESC);
|
|
133
|
+
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_entity_policy_proposals_status_created
|
|
135
|
+
ON entity_policy_proposals(status, created_at DESC);
|
|
136
|
+
|
|
137
|
+
CREATE INDEX IF NOT EXISTS idx_entity_policy_proposals_policy_key
|
|
138
|
+
ON entity_policy_proposals(policy_key, created_at DESC);
|
|
139
|
+
|
|
140
|
+
-- ================================================================
|
|
141
|
+
-- Temporal — add event_datetime column
|
|
142
|
+
-- ================================================================
|
|
143
|
+
-- Migration 033: Add event_datetime column to decisions table
|
|
144
|
+
-- Stores source event timestamp in milliseconds when known.
|
|
145
|
+
ALTER TABLE decisions ADD COLUMN event_datetime INTEGER;
|
|
146
|
+
|
|
147
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_event_datetime ON decisions(event_datetime);
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
-- ================================================================
|
|
151
|
+
-- Temporal — backfill from linked observations
|
|
152
|
+
-- ================================================================
|
|
153
|
+
-- Migration 034: Backfill event_datetime for existing decisions
|
|
154
|
+
-- Priority:
|
|
155
|
+
-- 1) Latest linked observation timestamp when provenance exists
|
|
156
|
+
-- 2) event_date at 00:00:00 UTC when only date is known
|
|
157
|
+
|
|
158
|
+
UPDATE decisions
|
|
159
|
+
SET event_datetime = (
|
|
160
|
+
SELECT MAX(COALESCE(o.timestamp_observed, o.created_at))
|
|
161
|
+
FROM decision_entity_sources des
|
|
162
|
+
JOIN entity_observations o ON o.id = des.entity_observation_id
|
|
163
|
+
WHERE des.decision_id = decisions.id
|
|
164
|
+
)
|
|
165
|
+
WHERE event_datetime IS NULL
|
|
166
|
+
AND EXISTS (
|
|
167
|
+
SELECT 1
|
|
168
|
+
FROM decision_entity_sources des
|
|
169
|
+
JOIN entity_observations o ON o.id = des.entity_observation_id
|
|
170
|
+
WHERE des.decision_id = decisions.id
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
UPDATE decisions
|
|
174
|
+
SET event_datetime = CAST(strftime('%s', event_date || 'T00:00:00Z') AS INTEGER) * 1000
|
|
175
|
+
WHERE event_datetime IS NULL
|
|
176
|
+
AND event_date IS NOT NULL;
|
|
177
|
+
|
|
178
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_event_datetime_backfill ON decisions(event_datetime);
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
-- ================================================================
|
|
182
|
+
-- Temporal — final fill from created_at
|
|
183
|
+
-- ================================================================
|
|
184
|
+
-- Migration 035: Fill any remaining event_datetime gaps from created_at
|
|
185
|
+
-- This is the final fallback for decisions that have neither provenance-linked
|
|
186
|
+
-- observation timestamps nor an explicit event_date.
|
|
187
|
+
|
|
188
|
+
UPDATE decisions
|
|
189
|
+
SET event_datetime = created_at
|
|
190
|
+
WHERE event_datetime IS NULL;
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
-- ================================================================
|
|
194
|
+
-- Source locator — add column + seed from raw_db_ref
|
|
195
|
+
-- ================================================================
|
|
196
|
+
ALTER TABLE entity_observations ADD COLUMN source_locator TEXT;
|
|
197
|
+
|
|
198
|
+
UPDATE entity_observations
|
|
199
|
+
SET source_locator = source_raw_db_ref
|
|
200
|
+
WHERE source_locator IS NULL;
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
-- ================================================================
|
|
204
|
+
-- Source locator — switch identity + triggers
|
|
205
|
+
-- ================================================================
|
|
206
|
+
UPDATE entity_observations
|
|
207
|
+
SET source_locator = COALESCE(source_locator, source_raw_db_ref, '')
|
|
208
|
+
WHERE source_locator IS NULL OR source_locator = '';
|
|
209
|
+
|
|
210
|
+
DROP INDEX IF EXISTS idx_entity_observations_source_record;
|
|
211
|
+
|
|
212
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_entity_observations_source_record
|
|
213
|
+
ON entity_observations(
|
|
214
|
+
source_connector,
|
|
215
|
+
source_locator,
|
|
216
|
+
source_raw_record_id,
|
|
217
|
+
observation_type
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
CREATE INDEX IF NOT EXISTS idx_entity_observations_source_locator_record
|
|
221
|
+
ON entity_observations(source_connector, source_locator, source_raw_record_id);
|
|
222
|
+
|
|
223
|
+
CREATE TRIGGER IF NOT EXISTS trg_entity_observations_backfill_source_locator_insert
|
|
224
|
+
AFTER INSERT ON entity_observations
|
|
225
|
+
WHEN (NEW.source_locator IS NULL OR NEW.source_locator = '')
|
|
226
|
+
AND COALESCE(NEW.source_raw_db_ref, '') <> ''
|
|
227
|
+
BEGIN
|
|
228
|
+
UPDATE entity_observations
|
|
229
|
+
SET source_locator = NEW.source_raw_db_ref
|
|
230
|
+
WHERE id = NEW.id;
|
|
231
|
+
END;
|
|
232
|
+
|
|
233
|
+
CREATE TRIGGER IF NOT EXISTS trg_entity_observations_backfill_source_locator_update
|
|
234
|
+
AFTER UPDATE ON entity_observations
|
|
235
|
+
WHEN (NEW.source_locator IS NULL OR NEW.source_locator = '')
|
|
236
|
+
AND COALESCE(NEW.source_raw_db_ref, '') <> ''
|
|
237
|
+
BEGIN
|
|
238
|
+
UPDATE entity_observations
|
|
239
|
+
SET source_locator = NEW.source_raw_db_ref
|
|
240
|
+
WHERE id = NEW.id;
|
|
241
|
+
END;
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
-- ================================================================
|
|
245
|
+
-- Source locator — drop legacy raw_db_ref
|
|
246
|
+
-- ================================================================
|
|
247
|
+
UPDATE entity_observations
|
|
248
|
+
SET source_locator = COALESCE(source_locator, source_raw_db_ref, '')
|
|
249
|
+
WHERE source_locator IS NULL OR source_locator = '';
|
|
250
|
+
|
|
251
|
+
DROP TRIGGER IF EXISTS trg_entity_observations_backfill_source_locator_insert;
|
|
252
|
+
DROP TRIGGER IF EXISTS trg_entity_observations_backfill_source_locator_update;
|
|
253
|
+
|
|
254
|
+
ALTER TABLE entity_observations DROP COLUMN source_raw_db_ref;
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
-- ================================================================
|
|
258
|
+
-- Phase 1 — case_truth
|
|
259
|
+
-- ================================================================
|
|
260
|
+
-- Migration 039: Create case_truth table
|
|
261
|
+
-- Phase 1 of MAMA Case-First Memory System
|
|
262
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md §5.2
|
|
263
|
+
|
|
264
|
+
CREATE TABLE IF NOT EXISTS case_truth (
|
|
265
|
+
case_id TEXT PRIMARY KEY, -- UUID, also persisted in wiki frontmatter
|
|
266
|
+
current_wiki_path TEXT, -- nullable (merged cases retain no current page)
|
|
267
|
+
title TEXT NOT NULL,
|
|
268
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','blocked','resolved','stale','archived','merged','split')),
|
|
269
|
+
status_reason TEXT,
|
|
270
|
+
primary_actors TEXT, -- JSON: [{entity_id, role}]
|
|
271
|
+
blockers TEXT, -- JSON: [{text, source_decision_id?, source_event_id?}]
|
|
272
|
+
last_activity_at TEXT, -- ISO 8601 — fast-writer field
|
|
273
|
+
canonical_case_id TEXT REFERENCES case_truth(case_id), -- merge survivor pointer
|
|
274
|
+
split_from_case_id TEXT REFERENCES case_truth(case_id), -- split parent reference
|
|
275
|
+
wiki_path_history TEXT, -- JSON: [{path, valid_from, valid_to}]
|
|
276
|
+
scope_refs TEXT, -- JSON: [{kind, id}]
|
|
277
|
+
confidence TEXT, -- high | medium | low
|
|
278
|
+
compiled_at TEXT, -- wiki-compiler last write
|
|
279
|
+
state_updated_at TEXT, -- memory-agent last write (status / last_activity)
|
|
280
|
+
created_at TEXT NOT NULL,
|
|
281
|
+
updated_at TEXT NOT NULL
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_case_truth_current_path
|
|
285
|
+
ON case_truth(current_wiki_path COLLATE NOCASE)
|
|
286
|
+
WHERE current_wiki_path IS NOT NULL;
|
|
287
|
+
|
|
288
|
+
CREATE INDEX IF NOT EXISTS idx_case_truth_status ON case_truth(status);
|
|
289
|
+
CREATE INDEX IF NOT EXISTS idx_case_truth_canonical ON case_truth(canonical_case_id);
|
|
290
|
+
CREATE INDEX IF NOT EXISTS idx_case_truth_last_activity ON case_truth(last_activity_at DESC);
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
-- ================================================================
|
|
294
|
+
-- Phase 1 — case_memberships
|
|
295
|
+
-- ================================================================
|
|
296
|
+
-- Migration 040: Create case_memberships table
|
|
297
|
+
-- Phase 1 of MAMA Case-First Memory System
|
|
298
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md §5.3
|
|
299
|
+
--
|
|
300
|
+
-- Locked tombstone encoding (§0d Option B): (status='stale', user_locked=1).
|
|
301
|
+
-- CHECK enum stays at 5 values; no 'stale_locked'.
|
|
302
|
+
|
|
303
|
+
CREATE TABLE IF NOT EXISTS case_memberships (
|
|
304
|
+
case_id TEXT NOT NULL REFERENCES case_truth(case_id),
|
|
305
|
+
source_type TEXT NOT NULL CHECK(source_type IN ('decision','event','observation','artifact')),
|
|
306
|
+
source_id TEXT NOT NULL,
|
|
307
|
+
role TEXT, -- soft-typed: primary | supporting | contradicting | ...
|
|
308
|
+
confidence REAL CHECK(confidence IS NULL OR (confidence >= 0 AND confidence <= 1)),
|
|
309
|
+
reason TEXT,
|
|
310
|
+
status TEXT NOT NULL CHECK(status IN ('active','candidate','removed','excluded','stale')),
|
|
311
|
+
added_by TEXT NOT NULL CHECK(added_by IN ('wiki-compiler','memory-agent','user-correction')),
|
|
312
|
+
added_at TEXT NOT NULL,
|
|
313
|
+
updated_at TEXT NOT NULL,
|
|
314
|
+
user_locked INTEGER NOT NULL DEFAULT 0,
|
|
315
|
+
PRIMARY KEY (case_id, source_type, source_id)
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
CREATE INDEX IF NOT EXISTS idx_case_memberships_source
|
|
319
|
+
ON case_memberships(source_type, source_id, status);
|
|
320
|
+
CREATE INDEX IF NOT EXISTS idx_case_memberships_case_status
|
|
321
|
+
ON case_memberships(case_id, status);
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
-- ================================================================
|
|
325
|
+
-- Phase 1 — case_corrections
|
|
326
|
+
-- ================================================================
|
|
327
|
+
-- Migration 041: Create case_corrections table
|
|
328
|
+
-- Phase 1 of MAMA Case-First Memory System
|
|
329
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md §5.4
|
|
330
|
+
--
|
|
331
|
+
-- Option C (Phase 0 §0f): target_ref_json TEXT (readable canonical JSON) +
|
|
332
|
+
-- target_ref_hash BLOB (32-byte SHA-256 over canonical JSON).
|
|
333
|
+
-- Partial UNIQUE index on (case_id, target_ref_hash) WHERE active lock ensures
|
|
334
|
+
-- one active lock per (case, target).
|
|
335
|
+
--
|
|
336
|
+
-- Phase 1 SCAFFOLD ONLY: the table ships empty. Write flows
|
|
337
|
+
-- (apply/revert/supersede helpers, HITL UX, recompile lock-respect) are
|
|
338
|
+
-- Phase 2 per spec §14 L811 amendment.
|
|
339
|
+
|
|
340
|
+
CREATE TABLE IF NOT EXISTS case_corrections (
|
|
341
|
+
correction_id TEXT PRIMARY KEY, -- UUID
|
|
342
|
+
case_id TEXT NOT NULL REFERENCES case_truth(case_id),
|
|
343
|
+
target_kind TEXT NOT NULL CHECK(target_kind IN ('case_field','membership','wiki_section')),
|
|
344
|
+
target_ref_json TEXT NOT NULL, -- canonical JSON (human-readable)
|
|
345
|
+
target_ref_hash BLOB NOT NULL CHECK(length(target_ref_hash) = 32), -- SHA-256 32 bytes
|
|
346
|
+
field_name TEXT, -- redundant convenience for target_kind='case_field'
|
|
347
|
+
old_value_json TEXT, -- snapshot at correction time
|
|
348
|
+
new_value_json TEXT NOT NULL, -- user's intended value
|
|
349
|
+
reason TEXT NOT NULL,
|
|
350
|
+
is_lock_active INTEGER NOT NULL DEFAULT 1,
|
|
351
|
+
superseded_by TEXT REFERENCES case_corrections(correction_id),
|
|
352
|
+
reverted_at TEXT,
|
|
353
|
+
applied_by TEXT NOT NULL,
|
|
354
|
+
applied_at TEXT NOT NULL
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
CREATE INDEX IF NOT EXISTS idx_case_corrections_case_active
|
|
358
|
+
ON case_corrections(case_id, is_lock_active, reverted_at);
|
|
359
|
+
|
|
360
|
+
-- Partial UNIQUE: at most one active lock per (case_id, target_ref_hash).
|
|
361
|
+
-- Reverted or superseded rows do not block new active locks on the same target.
|
|
362
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_case_corrections_active_target
|
|
363
|
+
ON case_corrections(case_id, target_ref_hash)
|
|
364
|
+
WHERE is_lock_active = 1 AND reverted_at IS NULL;
|
|
365
|
+
|
|
366
|
+
CREATE INDEX IF NOT EXISTS idx_case_corrections_target_kind
|
|
367
|
+
ON case_corrections(case_id, target_kind);
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
-- ================================================================
|
|
371
|
+
-- Phase 1 — case_proposal_queue
|
|
372
|
+
-- ================================================================
|
|
373
|
+
-- Migration 042: Create case_proposal_queue table
|
|
374
|
+
-- Phase 1 of MAMA Case-First Memory System
|
|
375
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md §5.7
|
|
376
|
+
--
|
|
377
|
+
-- Acceptance-layer queue for LLM proposals rejected by the deterministic
|
|
378
|
+
-- acceptance layer (§4.2). Phase 1 SCAFFOLD + quarantine writes only; HITL
|
|
379
|
+
-- resolution UX is Phase 2.
|
|
380
|
+
--
|
|
381
|
+
-- payload_fingerprint is a SHA-256 (32 bytes) over a STABLE per-kind canonical
|
|
382
|
+
-- JSON subset — NOT over the full proposed_payload (full LLM output varies
|
|
383
|
+
-- across runs and would defeat dedup). Per-kind subsets live in the
|
|
384
|
+
-- acceptance-layer implementation; see spec §5.7 write rules.
|
|
385
|
+
|
|
386
|
+
CREATE TABLE IF NOT EXISTS case_proposal_queue (
|
|
387
|
+
proposal_id TEXT PRIMARY KEY, -- UUID
|
|
388
|
+
project TEXT NOT NULL,
|
|
389
|
+
proposal_kind TEXT NOT NULL CHECK(proposal_kind IN (
|
|
390
|
+
'ambiguous_slug',
|
|
391
|
+
'duplicate_frontmatter',
|
|
392
|
+
'missing_frontmatter',
|
|
393
|
+
'unknown_case_id',
|
|
394
|
+
'stale_case_id',
|
|
395
|
+
'merged_target',
|
|
396
|
+
'archived_target',
|
|
397
|
+
'lock_conflict',
|
|
398
|
+
'corrupt_frontmatter',
|
|
399
|
+
'quarantined_accepted_case'
|
|
400
|
+
)),
|
|
401
|
+
proposed_payload TEXT NOT NULL, -- JSON: full LLM proposal
|
|
402
|
+
payload_fingerprint BLOB NOT NULL CHECK(length(payload_fingerprint) = 32),
|
|
403
|
+
conflicting_case_id TEXT,
|
|
404
|
+
detected_at TEXT NOT NULL,
|
|
405
|
+
resolved_at TEXT,
|
|
406
|
+
resolution TEXT CHECK(resolution IS NULL OR resolution IN ('accepted','rejected','modified','merged')),
|
|
407
|
+
resolution_note TEXT
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
CREATE INDEX IF NOT EXISTS idx_case_proposal_queue_unresolved
|
|
411
|
+
ON case_proposal_queue(project, resolved_at);
|
|
412
|
+
CREATE INDEX IF NOT EXISTS idx_case_proposal_queue_kind
|
|
413
|
+
ON case_proposal_queue(proposal_kind, resolved_at);
|
|
414
|
+
|
|
415
|
+
-- Composite UNIQUE for idempotent re-detection. COALESCE allows dedup even
|
|
416
|
+
-- when conflicting_case_id is NULL (e.g. corrupt_frontmatter).
|
|
417
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_case_proposal_queue_dedup
|
|
418
|
+
ON case_proposal_queue(
|
|
419
|
+
project,
|
|
420
|
+
proposal_kind,
|
|
421
|
+
COALESCE(conflicting_case_id, ''),
|
|
422
|
+
payload_fingerprint
|
|
423
|
+
)
|
|
424
|
+
WHERE resolved_at IS NULL;
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
-- ================================================================
|
|
428
|
+
-- Phase 1 — wiki_page_search_index
|
|
429
|
+
-- ================================================================
|
|
430
|
+
-- Migration 043: Wiki page search index for mama_search integration
|
|
431
|
+
-- Phase 1 of MAMA Case-First Memory System
|
|
432
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md §7.1
|
|
433
|
+
--
|
|
434
|
+
-- Tables: wiki_page_index (metadata), wiki_page_embeddings (vectors),
|
|
435
|
+
-- wiki_pages_fts (FTS5 inverted index). FTS5 is REGULAR (not contentless,
|
|
436
|
+
-- not external-content) with `page_id UNINDEXED` for app-side hit mapping.
|
|
437
|
+
-- Sync triggers use standard INSERT / DELETE+INSERT / DELETE.
|
|
438
|
+
--
|
|
439
|
+
-- App-layer writes to wiki_page_index MUST NOT use INSERT OR REPLACE
|
|
440
|
+
-- (that toggles rowids and leaves FTS pointing at deleted rows). Use
|
|
441
|
+
-- INSERT ... ON CONFLICT(page_id) DO UPDATE SET ... so the UPDATE trigger
|
|
442
|
+
-- fires and the FTS stays consistent.
|
|
443
|
+
|
|
444
|
+
CREATE TABLE IF NOT EXISTS wiki_page_index (
|
|
445
|
+
page_id TEXT PRIMARY KEY,
|
|
446
|
+
source_type TEXT NOT NULL DEFAULT 'wiki_page' CHECK(source_type = 'wiki_page'),
|
|
447
|
+
source_locator TEXT NOT NULL,
|
|
448
|
+
case_id TEXT REFERENCES case_truth(case_id),
|
|
449
|
+
title TEXT NOT NULL,
|
|
450
|
+
-- Spec §5.6 enum (original 4 + 'case' added in Phase 1). DB-level CHECK
|
|
451
|
+
-- prevents malformed page_type from entering the search index and later
|
|
452
|
+
-- silently breaking the mama_search roll-up that keys on 'case'.
|
|
453
|
+
page_type TEXT NOT NULL CHECK(page_type IN ('entity','lesson','synthesis','process','case')),
|
|
454
|
+
content TEXT NOT NULL,
|
|
455
|
+
confidence TEXT,
|
|
456
|
+
compiled_at TEXT NOT NULL,
|
|
457
|
+
updated_at TEXT NOT NULL,
|
|
458
|
+
UNIQUE(source_type, source_locator)
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
CREATE TABLE IF NOT EXISTS wiki_page_embeddings (
|
|
462
|
+
page_id TEXT PRIMARY KEY REFERENCES wiki_page_index(page_id) ON DELETE CASCADE,
|
|
463
|
+
embedding BLOB NOT NULL
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS wiki_pages_fts USING fts5(
|
|
467
|
+
page_id UNINDEXED,
|
|
468
|
+
title,
|
|
469
|
+
content,
|
|
470
|
+
source_locator
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
-- INSERT trigger: sync new row into FTS.
|
|
474
|
+
CREATE TRIGGER IF NOT EXISTS trg_wiki_page_index_ai
|
|
475
|
+
AFTER INSERT ON wiki_page_index
|
|
476
|
+
BEGIN
|
|
477
|
+
INSERT INTO wiki_pages_fts(page_id, title, content, source_locator)
|
|
478
|
+
VALUES (NEW.page_id, NEW.title, NEW.content, NEW.source_locator);
|
|
479
|
+
END;
|
|
480
|
+
|
|
481
|
+
-- UPDATE trigger: delete-then-insert keeps FTS tokens in sync on edits.
|
|
482
|
+
-- Only fires when indexed/stored FTS-adjacent columns change; page_id stays
|
|
483
|
+
-- unchanged via the UPSERT contract noted above.
|
|
484
|
+
CREATE TRIGGER IF NOT EXISTS trg_wiki_page_index_au
|
|
485
|
+
AFTER UPDATE OF title, content, source_locator ON wiki_page_index
|
|
486
|
+
BEGIN
|
|
487
|
+
DELETE FROM wiki_pages_fts WHERE page_id = OLD.page_id;
|
|
488
|
+
INSERT INTO wiki_pages_fts(page_id, title, content, source_locator)
|
|
489
|
+
VALUES (NEW.page_id, NEW.title, NEW.content, NEW.source_locator);
|
|
490
|
+
END;
|
|
491
|
+
|
|
492
|
+
-- DELETE trigger: remove all FTS rows for the page.
|
|
493
|
+
CREATE TRIGGER IF NOT EXISTS trg_wiki_page_index_ad
|
|
494
|
+
AFTER DELETE ON wiki_page_index
|
|
495
|
+
BEGIN
|
|
496
|
+
DELETE FROM wiki_pages_fts WHERE page_id = OLD.page_id;
|
|
497
|
+
END;
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
-- ================================================================
|
|
501
|
+
-- Phase 2 — entity_timeline_events.role
|
|
502
|
+
-- ================================================================
|
|
503
|
+
-- Migration 044: Add entity_timeline_events.role
|
|
504
|
+
--
|
|
505
|
+
-- Phase 2 Task 1 (spec §14 item 11, §4 L105-111): add a nullable `role`
|
|
506
|
+
-- column to `entity_timeline_events` so memory-agent role inference
|
|
507
|
+
-- (Phase 2 Task 3) can attach a role to each timeline event without
|
|
508
|
+
-- constraining downstream consumers. Role is soft-typed per spec §5.1
|
|
509
|
+
-- (requester | implementer | reviewer | observer | affected | NULL)
|
|
510
|
+
-- — the CHECK enum is intentionally NOT applied so future spec changes
|
|
511
|
+
-- don't require another migration.
|
|
512
|
+
|
|
513
|
+
ALTER TABLE entity_timeline_events ADD COLUMN role TEXT;
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
-- ================================================================
|
|
517
|
+
-- Phase 3 — connector_event_index
|
|
518
|
+
-- ================================================================
|
|
519
|
+
-- Migration 045: Create connector_event_index and replay-safe connector cursors
|
|
520
|
+
-- Phase 3 of MAMA Case-First Memory System
|
|
521
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md Amendment 9
|
|
522
|
+
|
|
523
|
+
CREATE TABLE IF NOT EXISTS connector_event_index (
|
|
524
|
+
event_index_id TEXT PRIMARY KEY,
|
|
525
|
+
source_connector TEXT NOT NULL,
|
|
526
|
+
source_type TEXT NOT NULL,
|
|
527
|
+
source_id TEXT NOT NULL,
|
|
528
|
+
source_locator TEXT,
|
|
529
|
+
channel TEXT,
|
|
530
|
+
author TEXT,
|
|
531
|
+
title TEXT,
|
|
532
|
+
content TEXT NOT NULL,
|
|
533
|
+
event_datetime INTEGER,
|
|
534
|
+
event_date TEXT,
|
|
535
|
+
source_timestamp_ms INTEGER NOT NULL,
|
|
536
|
+
metadata_json TEXT,
|
|
537
|
+
artifact_locator TEXT,
|
|
538
|
+
artifact_title TEXT,
|
|
539
|
+
content_hash BLOB NOT NULL CHECK(length(content_hash) = 32),
|
|
540
|
+
indexed_at TEXT NOT NULL,
|
|
541
|
+
updated_at TEXT NOT NULL,
|
|
542
|
+
expires_at TEXT
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_connector_event_index_source_identity
|
|
546
|
+
ON connector_event_index(source_connector, source_id);
|
|
547
|
+
|
|
548
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_index_cursor_order
|
|
549
|
+
ON connector_event_index(source_connector, source_timestamp_ms, source_id);
|
|
550
|
+
|
|
551
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_index_event_datetime
|
|
552
|
+
ON connector_event_index(event_datetime, event_index_id);
|
|
553
|
+
|
|
554
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_index_event_date
|
|
555
|
+
ON connector_event_index(event_date, event_datetime, event_index_id);
|
|
556
|
+
|
|
557
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_index_artifact_locator
|
|
558
|
+
ON connector_event_index(artifact_locator)
|
|
559
|
+
WHERE artifact_locator IS NOT NULL;
|
|
560
|
+
|
|
561
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_index_expires_at
|
|
562
|
+
ON connector_event_index(expires_at)
|
|
563
|
+
WHERE expires_at IS NOT NULL;
|
|
564
|
+
|
|
565
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS connector_event_index_fts USING fts5(
|
|
566
|
+
event_index_id UNINDEXED,
|
|
567
|
+
title,
|
|
568
|
+
content,
|
|
569
|
+
author,
|
|
570
|
+
channel,
|
|
571
|
+
tokenize = 'unicode61 remove_diacritics 2'
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
CREATE TRIGGER IF NOT EXISTS trg_connector_event_index_ai
|
|
575
|
+
AFTER INSERT ON connector_event_index
|
|
576
|
+
BEGIN
|
|
577
|
+
INSERT INTO connector_event_index_fts(event_index_id, title, content, author, channel)
|
|
578
|
+
VALUES (NEW.event_index_id, NEW.title, NEW.content, NEW.author, NEW.channel);
|
|
579
|
+
END;
|
|
580
|
+
|
|
581
|
+
CREATE TRIGGER IF NOT EXISTS trg_connector_event_index_au
|
|
582
|
+
AFTER UPDATE OF title, content, author, channel ON connector_event_index
|
|
583
|
+
BEGIN
|
|
584
|
+
DELETE FROM connector_event_index_fts WHERE event_index_id = OLD.event_index_id;
|
|
585
|
+
INSERT INTO connector_event_index_fts(event_index_id, title, content, author, channel)
|
|
586
|
+
VALUES (NEW.event_index_id, NEW.title, NEW.content, NEW.author, NEW.channel);
|
|
587
|
+
END;
|
|
588
|
+
|
|
589
|
+
CREATE TRIGGER IF NOT EXISTS trg_connector_event_index_ad
|
|
590
|
+
AFTER DELETE ON connector_event_index
|
|
591
|
+
BEGIN
|
|
592
|
+
DELETE FROM connector_event_index_fts WHERE event_index_id = OLD.event_index_id;
|
|
593
|
+
END;
|
|
594
|
+
|
|
595
|
+
CREATE TABLE IF NOT EXISTS connector_event_index_cursors (
|
|
596
|
+
connector_name TEXT PRIMARY KEY,
|
|
597
|
+
last_seen_timestamp_ms INTEGER NOT NULL DEFAULT 0,
|
|
598
|
+
last_seen_source_id TEXT NOT NULL DEFAULT '',
|
|
599
|
+
last_sweep_at TEXT,
|
|
600
|
+
last_success_at TEXT,
|
|
601
|
+
last_error TEXT,
|
|
602
|
+
last_error_at TEXT,
|
|
603
|
+
indexed_count INTEGER NOT NULL DEFAULT 0
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
-- ================================================================
|
|
608
|
+
-- Phase 3 — search_feedback + ranker
|
|
609
|
+
-- ================================================================
|
|
610
|
+
-- Migration 046: Create search_feedback and ranker_model_versions
|
|
611
|
+
-- Phase 3 of MAMA Case-First Memory System
|
|
612
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md Amendment 10
|
|
613
|
+
|
|
614
|
+
CREATE TABLE IF NOT EXISTS search_feedback (
|
|
615
|
+
feedback_id TEXT PRIMARY KEY,
|
|
616
|
+
query TEXT NOT NULL,
|
|
617
|
+
query_hash BLOB NOT NULL CHECK(length(query_hash) = 32),
|
|
618
|
+
question_type TEXT NOT NULL CHECK(question_type IN (
|
|
619
|
+
'status',
|
|
620
|
+
'timeline',
|
|
621
|
+
'artifact',
|
|
622
|
+
'correction',
|
|
623
|
+
'decision_reason',
|
|
624
|
+
'how_to',
|
|
625
|
+
'unknown'
|
|
626
|
+
)),
|
|
627
|
+
result_source_type TEXT NOT NULL CHECK(result_source_type IN (
|
|
628
|
+
'decision',
|
|
629
|
+
'checkpoint',
|
|
630
|
+
'wiki_page',
|
|
631
|
+
'case',
|
|
632
|
+
'connector_event'
|
|
633
|
+
)),
|
|
634
|
+
result_source_id TEXT NOT NULL,
|
|
635
|
+
case_id TEXT REFERENCES case_truth(case_id),
|
|
636
|
+
feedback_kind TEXT NOT NULL CHECK(feedback_kind IN ('shown','click','accept','reject','hide')),
|
|
637
|
+
rank_position INTEGER NOT NULL CHECK(rank_position >= 0),
|
|
638
|
+
score_before REAL,
|
|
639
|
+
score_after REAL,
|
|
640
|
+
session_id TEXT,
|
|
641
|
+
actor TEXT NOT NULL,
|
|
642
|
+
metadata_json TEXT,
|
|
643
|
+
created_at TEXT NOT NULL,
|
|
644
|
+
updated_at TEXT
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
CREATE INDEX IF NOT EXISTS idx_search_feedback_query_hash_created
|
|
648
|
+
ON search_feedback(query_hash, created_at DESC);
|
|
649
|
+
|
|
650
|
+
CREATE INDEX IF NOT EXISTS idx_search_feedback_result_created
|
|
651
|
+
ON search_feedback(result_source_type, result_source_id, created_at DESC);
|
|
652
|
+
|
|
653
|
+
CREATE INDEX IF NOT EXISTS idx_search_feedback_case_created
|
|
654
|
+
ON search_feedback(case_id, created_at DESC)
|
|
655
|
+
WHERE case_id IS NOT NULL;
|
|
656
|
+
|
|
657
|
+
CREATE INDEX IF NOT EXISTS idx_search_feedback_kind_created
|
|
658
|
+
ON search_feedback(feedback_kind, created_at DESC);
|
|
659
|
+
|
|
660
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_search_feedback_shown_dedupe
|
|
661
|
+
ON search_feedback(session_id, query_hash, result_source_type, result_source_id)
|
|
662
|
+
WHERE feedback_kind = 'shown' AND session_id IS NOT NULL;
|
|
663
|
+
|
|
664
|
+
CREATE TABLE IF NOT EXISTS ranker_model_versions (
|
|
665
|
+
model_id TEXT PRIMARY KEY,
|
|
666
|
+
model_version TEXT NOT NULL,
|
|
667
|
+
feature_set_version TEXT NOT NULL,
|
|
668
|
+
coefficients_json TEXT NOT NULL,
|
|
669
|
+
metrics_json TEXT NOT NULL,
|
|
670
|
+
training_window_json TEXT NOT NULL,
|
|
671
|
+
baseline_metrics_json TEXT NOT NULL,
|
|
672
|
+
quality_gate_status TEXT NOT NULL CHECK(quality_gate_status IN ('passed','failed','not_run')),
|
|
673
|
+
trained_at TEXT NOT NULL,
|
|
674
|
+
trained_by TEXT,
|
|
675
|
+
active INTEGER NOT NULL DEFAULT 0 CHECK(active IN (0,1))
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ranker_model_versions_single_active
|
|
679
|
+
ON ranker_model_versions(active)
|
|
680
|
+
WHERE active = 1;
|
|
681
|
+
|
|
682
|
+
CREATE INDEX IF NOT EXISTS idx_ranker_model_versions_trained_at
|
|
683
|
+
ON ranker_model_versions(trained_at DESC);
|
|
684
|
+
|
|
685
|
+
CREATE INDEX IF NOT EXISTS idx_ranker_model_versions_feature_set
|
|
686
|
+
ON ranker_model_versions(feature_set_version);
|
|
687
|
+
|
|
688
|
+
CREATE TABLE IF NOT EXISTS search_ranker_settings (
|
|
689
|
+
key TEXT PRIMARY KEY,
|
|
690
|
+
value_json TEXT NOT NULL,
|
|
691
|
+
updated_at TEXT NOT NULL
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
INSERT OR IGNORE INTO search_ranker_settings (key, value_json, updated_at)
|
|
695
|
+
VALUES ('search_feedback_retention_days', '180', strftime('%Y-%m-%dT%H:%M:%fZ','now'));
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
-- ================================================================
|
|
699
|
+
-- Phase 3 — case_links + tombstones
|
|
700
|
+
-- ================================================================
|
|
701
|
+
-- Migration 047: Create case_links and revoked wiki tombstones
|
|
702
|
+
-- Phase 3 of MAMA Case-First Memory System
|
|
703
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md Amendment 11
|
|
704
|
+
|
|
705
|
+
CREATE TABLE IF NOT EXISTS case_links (
|
|
706
|
+
link_id TEXT PRIMARY KEY,
|
|
707
|
+
case_id_from TEXT NOT NULL REFERENCES case_truth(case_id),
|
|
708
|
+
case_id_to TEXT NOT NULL REFERENCES case_truth(case_id),
|
|
709
|
+
link_type TEXT NOT NULL CHECK(link_type IN (
|
|
710
|
+
'related',
|
|
711
|
+
'supersedes-case',
|
|
712
|
+
'subcase-of',
|
|
713
|
+
'blocked-by',
|
|
714
|
+
'duplicate-of'
|
|
715
|
+
)),
|
|
716
|
+
created_at TEXT NOT NULL,
|
|
717
|
+
created_by TEXT NOT NULL,
|
|
718
|
+
confidence REAL CHECK(confidence IS NULL OR (confidence >= 0 AND confidence <= 1)),
|
|
719
|
+
reason_json TEXT,
|
|
720
|
+
source_kind TEXT NOT NULL DEFAULT 'manual' CHECK(source_kind IN (
|
|
721
|
+
'manual',
|
|
722
|
+
'wiki_compiler',
|
|
723
|
+
'hitl_correction',
|
|
724
|
+
'system_backfill'
|
|
725
|
+
)),
|
|
726
|
+
source_ref TEXT,
|
|
727
|
+
source_ref_fingerprint BLOB CHECK(
|
|
728
|
+
source_ref_fingerprint IS NULL OR length(source_ref_fingerprint) = 32
|
|
729
|
+
),
|
|
730
|
+
revoked_at TEXT,
|
|
731
|
+
revoked_by TEXT,
|
|
732
|
+
revoke_reason TEXT,
|
|
733
|
+
CHECK(case_id_from <> case_id_to)
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_case_links_active_unique
|
|
737
|
+
ON case_links(case_id_from, case_id_to, link_type)
|
|
738
|
+
WHERE revoked_at IS NULL;
|
|
739
|
+
|
|
740
|
+
CREATE INDEX IF NOT EXISTS idx_case_links_from_active
|
|
741
|
+
ON case_links(case_id_from, created_at DESC, link_id)
|
|
742
|
+
WHERE revoked_at IS NULL;
|
|
743
|
+
|
|
744
|
+
CREATE INDEX IF NOT EXISTS idx_case_links_to_active
|
|
745
|
+
ON case_links(case_id_to, created_at DESC, link_id)
|
|
746
|
+
WHERE revoked_at IS NULL;
|
|
747
|
+
|
|
748
|
+
CREATE INDEX IF NOT EXISTS idx_case_links_type_active
|
|
749
|
+
ON case_links(link_type, created_at DESC, link_id)
|
|
750
|
+
WHERE revoked_at IS NULL;
|
|
751
|
+
|
|
752
|
+
CREATE INDEX IF NOT EXISTS idx_case_links_source_ref_fingerprint
|
|
753
|
+
ON case_links(source_ref_fingerprint)
|
|
754
|
+
WHERE source_ref_fingerprint IS NOT NULL;
|
|
755
|
+
|
|
756
|
+
CREATE TABLE IF NOT EXISTS case_links_revoked_wiki_tombstones (
|
|
757
|
+
tombstone_id TEXT PRIMARY KEY,
|
|
758
|
+
case_id_from TEXT NOT NULL,
|
|
759
|
+
case_id_to TEXT NOT NULL,
|
|
760
|
+
link_type TEXT NOT NULL,
|
|
761
|
+
source_ref_fingerprint BLOB NOT NULL CHECK(length(source_ref_fingerprint) = 32),
|
|
762
|
+
source_ref TEXT NOT NULL,
|
|
763
|
+
created_at TEXT NOT NULL,
|
|
764
|
+
created_by TEXT NOT NULL,
|
|
765
|
+
revoke_reason TEXT NOT NULL,
|
|
766
|
+
unsuppressed_at TEXT,
|
|
767
|
+
unsuppressed_by TEXT
|
|
768
|
+
);
|
|
769
|
+
|
|
770
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_case_links_wiki_tombstone_active_unique
|
|
771
|
+
ON case_links_revoked_wiki_tombstones(
|
|
772
|
+
case_id_from,
|
|
773
|
+
case_id_to,
|
|
774
|
+
link_type,
|
|
775
|
+
source_ref_fingerprint
|
|
776
|
+
)
|
|
777
|
+
WHERE unsuppressed_at IS NULL;
|
|
778
|
+
|
|
779
|
+
CREATE INDEX IF NOT EXISTS idx_case_links_wiki_tombstone_source
|
|
780
|
+
ON case_links_revoked_wiki_tombstones(case_id_from, unsuppressed_at);
|
|
781
|
+
|
|
782
|
+
INSERT OR IGNORE INTO case_links (
|
|
783
|
+
link_id,
|
|
784
|
+
case_id_from,
|
|
785
|
+
case_id_to,
|
|
786
|
+
link_type,
|
|
787
|
+
created_at,
|
|
788
|
+
created_by,
|
|
789
|
+
confidence,
|
|
790
|
+
reason_json,
|
|
791
|
+
source_kind,
|
|
792
|
+
source_ref,
|
|
793
|
+
source_ref_fingerprint,
|
|
794
|
+
revoked_at,
|
|
795
|
+
revoked_by,
|
|
796
|
+
revoke_reason
|
|
797
|
+
)
|
|
798
|
+
SELECT
|
|
799
|
+
'backfill:canonical:' || case_id,
|
|
800
|
+
case_id,
|
|
801
|
+
canonical_case_id,
|
|
802
|
+
'duplicate-of',
|
|
803
|
+
updated_at,
|
|
804
|
+
'system',
|
|
805
|
+
NULL,
|
|
806
|
+
'{"authority":"case_truth.canonical_case_id"}',
|
|
807
|
+
'system_backfill',
|
|
808
|
+
'case_truth.canonical_case_id',
|
|
809
|
+
NULL,
|
|
810
|
+
NULL,
|
|
811
|
+
NULL,
|
|
812
|
+
NULL
|
|
813
|
+
FROM case_truth
|
|
814
|
+
WHERE canonical_case_id IS NOT NULL
|
|
815
|
+
AND canonical_case_id <> case_id;
|
|
816
|
+
|
|
817
|
+
INSERT OR IGNORE INTO case_links (
|
|
818
|
+
link_id,
|
|
819
|
+
case_id_from,
|
|
820
|
+
case_id_to,
|
|
821
|
+
link_type,
|
|
822
|
+
created_at,
|
|
823
|
+
created_by,
|
|
824
|
+
confidence,
|
|
825
|
+
reason_json,
|
|
826
|
+
source_kind,
|
|
827
|
+
source_ref,
|
|
828
|
+
source_ref_fingerprint,
|
|
829
|
+
revoked_at,
|
|
830
|
+
revoked_by,
|
|
831
|
+
revoke_reason
|
|
832
|
+
)
|
|
833
|
+
SELECT
|
|
834
|
+
'backfill:split:' || case_id,
|
|
835
|
+
case_id,
|
|
836
|
+
split_from_case_id,
|
|
837
|
+
'subcase-of',
|
|
838
|
+
updated_at,
|
|
839
|
+
'system',
|
|
840
|
+
NULL,
|
|
841
|
+
'{"authority":"case_truth.split_from_case_id"}',
|
|
842
|
+
'system_backfill',
|
|
843
|
+
'case_truth.split_from_case_id',
|
|
844
|
+
NULL,
|
|
845
|
+
NULL,
|
|
846
|
+
NULL,
|
|
847
|
+
NULL
|
|
848
|
+
FROM case_truth
|
|
849
|
+
WHERE split_from_case_id IS NOT NULL
|
|
850
|
+
AND split_from_case_id <> case_id;
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
-- ================================================================
|
|
854
|
+
-- Phase 3 — case_truth promotion + freshness
|
|
855
|
+
-- ================================================================
|
|
856
|
+
-- Migration 048: Add case promotion and freshness columns
|
|
857
|
+
-- Phase 3 of MAMA Case-First Memory System
|
|
858
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md Amendments 12 + 13
|
|
859
|
+
|
|
860
|
+
ALTER TABLE case_truth ADD COLUMN canonical_decision_id TEXT;
|
|
861
|
+
ALTER TABLE case_truth ADD COLUMN canonical_event_id TEXT;
|
|
862
|
+
ALTER TABLE case_truth ADD COLUMN promoted_at TEXT;
|
|
863
|
+
ALTER TABLE case_truth ADD COLUMN promoted_by TEXT;
|
|
864
|
+
ALTER TABLE case_truth ADD COLUMN promotion_reason TEXT;
|
|
865
|
+
|
|
866
|
+
ALTER TABLE case_truth ADD COLUMN freshness_score REAL CHECK(
|
|
867
|
+
freshness_score IS NULL OR (freshness_score >= 0 AND freshness_score <= 1)
|
|
868
|
+
);
|
|
869
|
+
ALTER TABLE case_truth ADD COLUMN freshness_state TEXT CHECK(
|
|
870
|
+
freshness_state IS NULL OR freshness_state IN ('fresh','stale','drifted','unknown')
|
|
871
|
+
);
|
|
872
|
+
ALTER TABLE case_truth ADD COLUMN freshness_score_is_drifted INTEGER NOT NULL DEFAULT 0 CHECK(
|
|
873
|
+
freshness_score_is_drifted IN (0,1)
|
|
874
|
+
);
|
|
875
|
+
ALTER TABLE case_truth ADD COLUMN freshness_drift_threshold REAL CHECK(
|
|
876
|
+
freshness_drift_threshold IS NULL OR (
|
|
877
|
+
freshness_drift_threshold >= 0 AND freshness_drift_threshold <= 1
|
|
878
|
+
)
|
|
879
|
+
);
|
|
880
|
+
ALTER TABLE case_truth ADD COLUMN freshness_checked_at TEXT;
|
|
881
|
+
ALTER TABLE case_truth ADD COLUMN freshness_reason_json TEXT;
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
-- ================================================================
|
|
885
|
+
-- Phase 3 — case_memberships explanation columns
|
|
886
|
+
-- ================================================================
|
|
887
|
+
-- Migration 049: Add case membership explanation columns
|
|
888
|
+
-- Phase 3 of MAMA Case-First Memory System
|
|
889
|
+
-- Spec: docs/superpowers/specs/2026-04-17-mama-work-case-first-memory-system-design.md Amendment 14
|
|
890
|
+
|
|
891
|
+
ALTER TABLE case_memberships ADD COLUMN assignment_strategy TEXT;
|
|
892
|
+
ALTER TABLE case_memberships ADD COLUMN score_breakdown_json TEXT;
|
|
893
|
+
ALTER TABLE case_memberships ADD COLUMN source_locator TEXT;
|
|
894
|
+
ALTER TABLE case_memberships ADD COLUMN explanation_updated_at TEXT;
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
-- ================================================================
|
|
898
|
+
-- Final schema_version marker
|
|
899
|
+
-- ================================================================
|
|
900
|
+
INSERT OR IGNORE INTO schema_version (version, description)
|
|
901
|
+
VALUES (30, 'Case-First Memory Substrate consolidated (Phase 1+2+3)');
|