1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4pub enum MemoryType {
5 Decision,
6 Discovery,
7 Bugfix,
8 Architecture,
9 Lesson,
10 Preference,
11 Procedure,
12 SessionActivity,
13}
14
15impl MemoryType {
16 pub const ALL: [Self; 8] = [
17 Self::Decision,
18 Self::Discovery,
19 Self::Bugfix,
20 Self::Architecture,
21 Self::Lesson,
22 Self::Preference,
23 Self::Procedure,
24 Self::SessionActivity,
25 ];
26
27 pub const fn as_str(self) -> &'static str {
28 match self {
29 Self::Decision => "decision",
30 Self::Discovery => "discovery",
31 Self::Bugfix => "bugfix",
32 Self::Architecture => "architecture",
33 Self::Lesson => "lesson",
34 Self::Preference => "preference",
35 Self::Procedure => "procedure",
36 Self::SessionActivity => "session_activity",
37 }
38 }
39
40 pub const fn label(self) -> &'static str {
41 match self {
42 Self::Decision => "Decisions",
43 Self::Discovery => "Discoveries",
44 Self::Bugfix => "Bug Fixes",
45 Self::Architecture => "Architecture",
46 Self::Lesson => "Lessons",
47 Self::Preference => "Preferences",
48 Self::Procedure => "Procedures",
49 Self::SessionActivity => "Sessions",
50 }
51 }
52
53 pub const fn index_order(self) -> Option<usize> {
54 match self {
55 Self::Decision => Some(0),
56 Self::Bugfix => Some(1),
57 Self::Architecture => Some(2),
58 Self::Discovery => Some(3),
59 Self::Procedure => Some(4),
60 Self::SessionActivity => Some(5),
61 Self::Lesson | Self::Preference => None,
62 }
63 }
64
65 pub const fn is_indexed(self) -> bool {
66 matches!(
67 self,
68 Self::Decision
69 | Self::Bugfix
70 | Self::Architecture
71 | Self::Discovery
72 | Self::Procedure
73 | Self::SessionActivity
74 )
75 }
76
77 pub const fn is_core(self) -> bool {
78 matches!(
79 self,
80 Self::Bugfix | Self::Architecture | Self::Decision | Self::Discovery
81 )
82 }
83
84 pub const fn weight(self) -> f64 {
85 match self {
86 Self::Bugfix => 3.0,
87 Self::Architecture => 2.6,
88 Self::Decision => 2.2,
89 Self::Discovery => 1.8,
90 Self::Lesson | Self::Preference | Self::Procedure | Self::SessionActivity => 0.0,
91 }
92 }
93
94 pub const fn auto_promote(self) -> bool {
95 matches!(
96 self,
97 Self::Architecture | Self::Bugfix | Self::Decision | Self::Discovery
98 )
99 }
100
101 pub fn parse(value: &str) -> Option<Self> {
102 Self::ALL
103 .iter()
104 .copied()
105 .find(|memory_type| memory_type.as_str() == value)
106 }
107
108 pub fn from_observation_type(observation_type: &str) -> Option<Self> {
119 match observation_type.trim().to_ascii_lowercase().as_str() {
120 "bugfix" => Some(Self::Bugfix),
121 "decision" => Some(Self::Decision),
122 "discovery" | "feature" | "refactor" | "change" => Some(Self::Discovery),
123 _ => None,
124 }
125 }
126
127 pub fn supports_observation_type(self, observation_type: &str) -> bool {
133 match Self::from_observation_type(observation_type) {
134 Some(mapped) => {
135 mapped == self || (self == Self::Architecture && mapped == Self::Discovery)
136 }
137 None => false,
138 }
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct Memory {
144 pub id: i64,
145 pub session_id: Option<String>,
146 pub project: String,
147 pub topic_key: Option<String>,
148 pub title: String,
149 pub text: String,
150 pub memory_type: String,
151 pub files: Option<String>,
152 pub created_at_epoch: i64,
153 pub updated_at_epoch: i64,
154 pub status: String,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub branch: Option<String>,
157 #[serde(default = "default_scope")]
158 pub scope: String,
159}
160
161fn default_scope() -> String {
162 "project".to_string()
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct Event {
167 pub id: i64,
168 pub session_id: String,
169 pub project: String,
170 pub event_type: String,
171 pub summary: String,
172 pub detail: Option<String>,
173 pub files: Option<String>,
174 pub exit_code: Option<i32>,
175 pub created_at_epoch: i64,
176}
177
178pub const MEMORY_TYPES: &[&str] = &[
179 MemoryType::Decision.as_str(),
180 MemoryType::Discovery.as_str(),
181 MemoryType::Bugfix.as_str(),
182 MemoryType::Architecture.as_str(),
183 MemoryType::Lesson.as_str(),
184 MemoryType::Preference.as_str(),
185 MemoryType::Procedure.as_str(),
186 MemoryType::SessionActivity.as_str(),
187];
188
189pub const MEMORY_COLS: &str = "id, session_id, project, topic_key, title, content, memory_type, \
190 files, created_at_epoch, updated_at_epoch, status, branch, scope";
191
192pub fn memory_status_filter_sql(column: &str, include_inactive: bool) -> String {
193 if include_inactive {
194 format!("{column} IN ('active', 'stale', 'archived')")
195 } else {
196 format!("{column} = 'active'")
197 }
198}
199
200pub fn memory_current_filter_sql(
201 status_column: &str,
202 expires_column: &str,
203 include_inactive: bool,
204) -> String {
205 if include_inactive {
206 memory_status_filter_sql(status_column, true)
207 } else {
208 format!(
209 "{status_column} = 'active' AND \
210 ({expires_column} IS NULL OR {expires_column} > CAST(strftime('%s', 'now') AS INTEGER))"
211 )
212 }
213}
214
215pub fn memory_state_key_current_filter_sql(table_alias: &str) -> String {
216 let table_alias = table_alias.trim();
217 let qualifier = if table_alias.is_empty() {
218 String::new()
219 } else {
220 format!("{table_alias}.")
221 };
222 format!(
223 "({qualifier}state_key_id IS NULL OR NOT EXISTS (
224 SELECT 1 FROM memory_state_keys sk
225 WHERE sk.id = {qualifier}state_key_id
226 AND sk.current_memory_id IS NOT NULL
227 AND sk.current_memory_id <> {qualifier}id
228 ))"
229 )
230}
231
232pub fn memory_not_superseded_filter_sql(table_alias: &str) -> String {
233 let table_alias = table_alias.trim();
234 let qualifier = if table_alias.is_empty() {
235 String::new()
236 } else {
237 format!("{table_alias}.")
238 };
239 format!(
240 "NOT EXISTS (
241 SELECT 1 FROM memory_edges supersede_edge
242 WHERE supersede_edge.edge_type = 'supersedes'
243 AND supersede_edge.from_memory_id = {qualifier}id
244 )"
245 )
246}
247
248pub fn map_memory_row_pub(row: &rusqlite::Row) -> rusqlite::Result<Memory> {
249 map_memory_row(row)
250}
251
252pub(super) fn map_memory_row(row: &rusqlite::Row) -> rusqlite::Result<Memory> {
253 Ok(Memory {
254 id: row.get(0)?,
255 session_id: row.get(1)?,
256 project: row.get(2)?,
257 topic_key: row.get(3)?,
258 title: row.get(4)?,
259 text: row.get(5)?,
260 memory_type: row.get(6)?,
261 files: row.get(7)?,
262 created_at_epoch: row.get(8)?,
263 updated_at_epoch: row.get(9)?,
264 status: row.get(10)?,
265 branch: row.get(11)?,
266 scope: row
267 .get::<_, Option<String>>(12)?
268 .unwrap_or_else(|| "project".to_string()),
269 })
270}
271
272pub(super) fn map_event_row(row: &rusqlite::Row) -> rusqlite::Result<Event> {
273 Ok(Event {
274 id: row.get(0)?,
275 session_id: row.get(1)?,
276 project: row.get(2)?,
277 event_type: row.get(3)?,
278 summary: row.get(4)?,
279 detail: row.get(5)?,
280 files: row.get(6)?,
281 exit_code: row.get(7)?,
282 created_at_epoch: row.get(8)?,
283 })
284}
285
286#[cfg(test)]
287mod tests {
288 use super::{MemoryType, MEMORY_TYPES};
289
290 #[test]
291 fn memory_types_are_derived_from_canonical_enum_order() {
292 let canonical = MemoryType::ALL
293 .iter()
294 .copied()
295 .map(MemoryType::as_str)
296 .collect::<Vec<_>>();
297
298 assert_eq!(MEMORY_TYPES, canonical.as_slice());
299 }
300
301 #[test]
302 fn architecture_candidate_accepts_discovery_class_observations() {
303 assert!(MemoryType::Architecture.supports_observation_type("discovery"));
306 assert!(MemoryType::Architecture.supports_observation_type("feature"));
307 assert!(MemoryType::Architecture.supports_observation_type("refactor"));
308 assert!(MemoryType::Architecture.supports_observation_type("change"));
309 assert!(!MemoryType::Architecture.supports_observation_type("bugfix"));
311 assert!(!MemoryType::Architecture.supports_observation_type("decision"));
312 }
313
314 #[test]
315 fn auto_promote_types_match_their_observation_equivalents() {
316 assert!(MemoryType::Bugfix.supports_observation_type("bugfix"));
317 assert!(MemoryType::Decision.supports_observation_type("decision"));
318 assert!(MemoryType::Discovery.supports_observation_type("discovery"));
319 assert!(MemoryType::Discovery.supports_observation_type("feature"));
321 assert!(MemoryType::Discovery.supports_observation_type("refactor"));
322 assert!(MemoryType::Discovery.supports_observation_type("change"));
323 assert!(!MemoryType::Bugfix.supports_observation_type("decision"));
325 assert!(!MemoryType::Decision.supports_observation_type("discovery"));
326 assert!(MemoryType::from_observation_type("architecture").is_none());
328 assert!(MemoryType::from_observation_type("nonsense").is_none());
329 }
330
331 #[test]
332 fn procedure_has_context_metadata() {
333 let memory_type = MemoryType::Procedure;
334
335 assert_eq!(memory_type.as_str(), "procedure");
336 assert_eq!(memory_type.label(), "Procedures");
337 assert_eq!(memory_type.index_order(), Some(4));
338 assert!(memory_type.is_indexed());
339 assert!(!memory_type.is_core());
340 assert_eq!(memory_type.weight(), 0.0);
341 assert!(!memory_type.auto_promote());
342 }
343}
344
345#[cfg(test)]
346pub mod tests_helper {
347 use rusqlite::Connection;
348
349 pub fn setup_memory_schema(conn: &Connection) {
350 conn.execute_batch(
351 "CREATE TABLE memories (
352 id INTEGER PRIMARY KEY,
353 session_id TEXT,
354 project TEXT NOT NULL,
355 topic_key TEXT,
356 title TEXT NOT NULL,
357 content TEXT NOT NULL,
358 memory_type TEXT NOT NULL,
359 files TEXT,
360 search_context TEXT,
361 created_at_epoch INTEGER NOT NULL,
362 updated_at_epoch INTEGER NOT NULL,
363 reference_time_epoch INTEGER,
364 status TEXT NOT NULL DEFAULT 'active',
365 branch TEXT,
366 scope TEXT DEFAULT 'project',
367 last_accessed_epoch INTEGER,
368 access_count INTEGER NOT NULL DEFAULT 0,
369 source_project TEXT,
370 target_project TEXT,
371 owner_scope TEXT,
372 owner_key TEXT,
373 topic_domain TEXT,
374 routing_confidence REAL,
375 routing_reason TEXT,
376 context_class TEXT,
377 expires_at_epoch INTEGER,
378 valid_from_epoch INTEGER,
379 valid_to_epoch INTEGER,
380 state_key_id INTEGER,
381 version INTEGER NOT NULL DEFAULT 1,
382 source_candidate_id INTEGER,
383 evidence_event_ids TEXT,
384 confidence REAL,
385 source_trust_class TEXT NOT NULL DEFAULT 'local_tool_output',
386 acknowledged_pattern_id TEXT,
387 acknowledged_pattern_version INTEGER,
388 acknowledged_at_epoch INTEGER
389 );
390 CREATE TABLE memory_state_keys (
391 id INTEGER PRIMARY KEY,
392 owner_scope TEXT NOT NULL,
393 owner_key TEXT NOT NULL,
394 memory_type TEXT NOT NULL,
395 state_key TEXT NOT NULL,
396 state_label TEXT,
397 state_status TEXT NOT NULL DEFAULT 'active',
398 current_memory_id INTEGER,
399 created_at_epoch INTEGER NOT NULL,
400 updated_at_epoch INTEGER NOT NULL,
401 UNIQUE(owner_scope, owner_key, memory_type, state_key)
402 );
403 CREATE TABLE memory_candidates (
404 id INTEGER PRIMARY KEY,
405 project_id INTEGER,
406 scope TEXT,
407 memory_type TEXT,
408 topic_key TEXT,
409 text TEXT,
410 evidence_event_ids TEXT,
411 confidence REAL,
412 risk_class TEXT,
413 review_status TEXT NOT NULL DEFAULT 'pending_review',
414 created_at_epoch INTEGER,
415 updated_at_epoch INTEGER
416 );
417 CREATE TABLE captured_events (
418 id INTEGER PRIMARY KEY
419 );
420 CREATE TABLE memory_embeddings (
421 memory_id INTEGER NOT NULL,
422 embedding BLOB NOT NULL,
423 dimensions INTEGER NOT NULL,
424 model TEXT NOT NULL,
425 content_hash TEXT NOT NULL,
426 updated_at_epoch INTEGER NOT NULL,
427 PRIMARY KEY(memory_id, model, dimensions),
428 FOREIGN KEY(memory_id) REFERENCES memories(id) ON DELETE CASCADE
429 );
430 CREATE INDEX idx_memory_embeddings_model
431 ON memory_embeddings(model, updated_at_epoch);
432 CREATE INDEX idx_memory_embeddings_profile_memory_id
433 ON memory_embeddings(model, dimensions, memory_id);
434 CREATE TABLE context_injection_items (
435 id INTEGER PRIMARY KEY,
436 injection_run_id TEXT NOT NULL,
437 host TEXT NOT NULL,
438 project TEXT NOT NULL,
439 session_id TEXT,
440 injection_key TEXT NOT NULL,
441 hook_source TEXT,
442 context_hash TEXT,
443 output_mode TEXT NOT NULL,
444 decision TEXT NOT NULL,
445 item_kind TEXT NOT NULL,
446 item_id INTEGER,
447 memory_id INTEGER,
448 channel TEXT NOT NULL,
449 score REAL,
450 render_order INTEGER,
451 status TEXT NOT NULL,
452 drop_reason TEXT,
453 title TEXT,
454 provenance TEXT,
455 staleness TEXT,
456 injected_at_epoch INTEGER NOT NULL
457 );
458 CREATE TABLE memory_poisoning_injection_drops (
459 id INTEGER PRIMARY KEY AUTOINCREMENT,
460 memory_id INTEGER NOT NULL,
461 pattern_id TEXT NOT NULL,
462 pattern_version INTEGER NOT NULL,
463 source_trust_class TEXT NOT NULL DEFAULT 'local_tool_output',
464 source_project TEXT,
465 title TEXT,
466 created_at_epoch INTEGER NOT NULL
467 );
468 CREATE INDEX idx_memory_poisoning_drops_created
469 ON memory_poisoning_injection_drops(created_at_epoch DESC, id DESC);
470 CREATE INDEX idx_memory_poisoning_drops_pattern
471 ON memory_poisoning_injection_drops(pattern_id, pattern_version, created_at_epoch DESC);
472 CREATE TABLE memory_citation_events (
473 id INTEGER PRIMARY KEY,
474 host TEXT NOT NULL,
475 project TEXT NOT NULL,
476 session_id TEXT NOT NULL,
477 source TEXT NOT NULL,
478 message_hash TEXT NOT NULL,
479 citation_line_present INTEGER NOT NULL DEFAULT 0,
480 parsed_count INTEGER NOT NULL DEFAULT 0,
481 matched_count INTEGER NOT NULL DEFAULT 0,
482 inserted_count INTEGER NOT NULL DEFAULT 0,
483 status TEXT NOT NULL,
484 created_at_epoch INTEGER NOT NULL,
485 UNIQUE(host, project, session_id, source, message_hash)
486 );
487 CREATE TABLE memory_usage_events (
488 id INTEGER PRIMARY KEY,
489 citation_event_id INTEGER NOT NULL,
490 host TEXT NOT NULL,
491 project TEXT NOT NULL,
492 session_id TEXT NOT NULL,
493 source TEXT NOT NULL,
494 message_hash TEXT NOT NULL,
495 memory_id INTEGER NOT NULL,
496 context_injection_item_id INTEGER,
497 created_at_epoch INTEGER NOT NULL,
498 UNIQUE(host, project, session_id, source, message_hash, memory_id)
499 );
500 CREATE TABLE ai_usage_events (
501 id INTEGER PRIMARY KEY,
502 model TEXT,
503 estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
504 pricing_source TEXT NOT NULL DEFAULT 'remem_static'
505 );
506 CREATE TABLE memory_operation_log (
507 id INTEGER PRIMARY KEY,
508 operation TEXT NOT NULL,
509 planner_version TEXT NOT NULL,
510 actor TEXT NOT NULL,
511 source TEXT NOT NULL,
512 owner_scope TEXT,
513 owner_key TEXT,
514 memory_type TEXT,
515 state_key TEXT,
516 input_topic_key TEXT,
517 source_candidate_id INTEGER,
518 result_memory_id INTEGER,
519 superseded_ids TEXT NOT NULL DEFAULT '[]',
520 conflicting_ids TEXT NOT NULL DEFAULT '[]',
521 noop_reason TEXT,
522 defer_reason TEXT,
523 confidence REAL,
524 reason TEXT,
525 created_at_epoch INTEGER NOT NULL
526 );
527 CREATE INDEX idx_memory_operation_log_state
528 ON memory_operation_log(owner_scope, owner_key, memory_type, state_key, created_at_epoch);
529 CREATE TABLE memory_edges (
530 id INTEGER PRIMARY KEY,
531 edge_type TEXT NOT NULL,
532 from_memory_id INTEGER,
533 to_memory_id INTEGER,
534 state_key_id INTEGER,
535 source_candidate_id INTEGER,
536 evidence_event_ids TEXT,
537 source_operation_id INTEGER,
538 confidence REAL,
539 reason TEXT,
540 created_at_epoch INTEGER NOT NULL,
541 FOREIGN KEY(from_memory_id) REFERENCES memories(id),
542 FOREIGN KEY(to_memory_id) REFERENCES memories(id),
543 FOREIGN KEY(state_key_id) REFERENCES memory_state_keys(id),
544 FOREIGN KEY(source_candidate_id) REFERENCES memory_candidates(id),
545 FOREIGN KEY(source_operation_id) REFERENCES memory_operation_log(id)
546 );
547 CREATE INDEX idx_memory_edges_from
548 ON memory_edges(from_memory_id, edge_type);
549 CREATE INDEX idx_memory_edges_to
550 ON memory_edges(to_memory_id, edge_type);
551 CREATE INDEX idx_memory_edges_state
552 ON memory_edges(state_key_id, edge_type, created_at_epoch);
553 CREATE TABLE dream_cluster_decisions (
554 id INTEGER PRIMARY KEY AUTOINCREMENT,
555 project TEXT NOT NULL,
556 memory_type TEXT NOT NULL,
557 cluster_signature TEXT NOT NULL,
558 decision TEXT NOT NULL CHECK(decision IN ('merged', 'no_merge', 'defer', 'failed')),
559 reason TEXT,
560 member_ids_json TEXT NOT NULL,
561 cluster_size INTEGER NOT NULL,
562 next_review_epoch INTEGER,
563 source_memory_id INTEGER,
564 source_operation_id INTEGER,
565 created_at_epoch INTEGER NOT NULL,
566 updated_at_epoch INTEGER NOT NULL,
567 last_seen_epoch INTEGER NOT NULL,
568 UNIQUE(project, memory_type, cluster_signature),
569 FOREIGN KEY(source_memory_id) REFERENCES memories(id),
570 FOREIGN KEY(source_operation_id) REFERENCES memory_operation_log(id)
571 );
572 CREATE INDEX idx_dream_cluster_decisions_review
573 ON dream_cluster_decisions(project, decision, next_review_epoch);
574 CREATE INDEX idx_dream_cluster_decisions_signature
575 ON dream_cluster_decisions(project, memory_type, cluster_signature);
576 CREATE TABLE events (
577 id INTEGER PRIMARY KEY,
578 session_id TEXT NOT NULL,
579 project TEXT NOT NULL,
580 event_type TEXT NOT NULL,
581 summary TEXT NOT NULL,
582 detail TEXT,
583 files TEXT,
584 exit_code INTEGER,
585 created_at_epoch INTEGER NOT NULL,
586 retention_class TEXT NOT NULL DEFAULT 'audit'
587 CHECK (retention_class IN ('ephemeral', 'audit'))
588 );
589 CREATE TABLE IF NOT EXISTS entities (
590 id INTEGER PRIMARY KEY,
591 canonical_name TEXT NOT NULL COLLATE NOCASE,
592 entity_type TEXT,
593 mention_count INTEGER DEFAULT 1,
594 created_at_epoch INTEGER NOT NULL DEFAULT 0,
595 UNIQUE(canonical_name)
596 );
597 CREATE TABLE IF NOT EXISTS memory_entities (
598 memory_id INTEGER NOT NULL,
599 entity_id INTEGER NOT NULL,
600 PRIMARY KEY(memory_id, entity_id)
601 );
602 CREATE TABLE IF NOT EXISTS memory_lessons (
603 memory_id INTEGER PRIMARY KEY,
604 confidence REAL NOT NULL DEFAULT 0.7,
605 reinforcement_count INTEGER NOT NULL DEFAULT 1,
606 source_evidence TEXT,
607 last_reinforced_at_epoch INTEGER NOT NULL,
608 stale_after_epoch INTEGER,
609 outcome_kind TEXT NOT NULL DEFAULT 'unknown'
610 CHECK (outcome_kind IN ('unknown', 'success', 'failure', 'recovery', 'correction', 'revert')),
611 success_count INTEGER NOT NULL DEFAULT 0 CHECK (success_count >= 0),
612 failure_count INTEGER NOT NULL DEFAULT 0 CHECK (failure_count >= 0),
613 recovery_count INTEGER NOT NULL DEFAULT 0 CHECK (recovery_count >= 0),
614 correction_count INTEGER NOT NULL DEFAULT 0 CHECK (correction_count >= 0),
615 revert_count INTEGER NOT NULL DEFAULT 0 CHECK (revert_count >= 0)
616 );
617 CREATE TABLE IF NOT EXISTS memory_suppressions (
618 id INTEGER PRIMARY KEY,
619 owner_scope TEXT,
620 owner_key TEXT,
621 target_kind TEXT NOT NULL,
622 target_id INTEGER,
623 target_value TEXT,
624 reason TEXT NOT NULL,
625 actor TEXT NOT NULL,
626 status TEXT NOT NULL,
627 created_at_epoch INTEGER NOT NULL,
628 updated_at_epoch INTEGER NOT NULL
629 );
630 CREATE TABLE IF NOT EXISTS memory_feedback (
631 id INTEGER PRIMARY KEY,
632 target_kind TEXT NOT NULL,
633 target_id INTEGER,
634 target_value TEXT,
635 feedback TEXT NOT NULL,
636 source TEXT NOT NULL,
637 context_injection_item_id INTEGER,
638 session_id TEXT,
639 project TEXT,
640 reason TEXT,
641 created_at_epoch INTEGER NOT NULL
642 );",
643 )
644 .unwrap();
645 conn.execute_batch(include_str!("../migrations/v020_memory_fts_all_status.sql"))
646 .unwrap();
647 conn.execute_batch(include_str!(
652 "../migrations/v072_memory_retrieval_enrichment.sql"
653 ))
654 .unwrap();
655 conn.execute_batch(include_str!(
656 "../migrations/v083_retrieval_enrichment_budget.sql"
657 ))
658 .unwrap();
659 conn.execute_batch(include_str!(
660 "../migrations/v086_memory_activation_boundary.sql"
661 ))
662 .unwrap();
663 conn.execute_batch(include_str!(
664 "../migrations/v087_activation_result_trust.sql"
665 ))
666 .unwrap();
667 conn.execute_batch(include_str!(
668 "../migrations/v088_activation_legacy_trust.sql"
669 ))
670 .unwrap();
671 conn.execute_batch(include_str!(
672 "../migrations/v089_supplemental_local_copy_receipt.sql"
673 ))
674 .unwrap();
675 conn.execute_batch(include_str!("../migrations/v090_scope_cleanup_receipt.sql"))
676 .unwrap();
677 }
678}