1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! Palace-memory seam for the SM goal store (DOC-14 §9.4).
//!
//! Why: the goal store treats the SM palace as its source of truth, but it must
//! (a) be UNIT-TESTABLE without the heavy ONNX-backed Memory Palace, and (b)
//! depend on an ABSTRACTION rather than the concrete `SmMemory` (which lives
//! behind the `sm-memory` feature) so the store compiles and is testable on the
//! default build. This module is that seam: a tiny async trait the store writes
//! through, implemented by `SmMemory` under `--features sm-memory` and by an
//! in-memory mock in tests. Per §9.4 the palace is durable truth and `goals.json`
//! is a cache rebuilt from it, so the trait surfaces exactly two operations — a
//! tagged write and a tag-scoped enumeration — and nothing else.
//! What: defines [`GoalMemory`] (two async methods returning a `String` error so
//! the trait stays feature-independent) and, behind `sm-memory`, implements it for
//! [`super::super::memory::SmMemory`] by delegating to its `remember_tagged` /
//! `list_tagged` scoped-write/enumerate methods.
//! Test: the mock impl in `goals/store_tests.rs` exercises the store; the real
//! `SmMemory` impl is covered by the `#[ignore]`-free seam delegation plus the
//! SM-4 scope tests it reuses.
use async_trait;
/// The stable palace tag every SM goal entry carries (DOC-14 §9.4).
///
/// Why: rebuilding the cache enumerates goal entries by an exact tag (not fuzzy
/// recall), so the write and the enumerate must agree on one constant. Isolating
/// it here makes the contract auditable and impossible to typo apart.
/// What: the `"sm-goal"` tag attached to every persisted goal drawer and used as
/// the [`GoalMemory::list_goals`] filter.
/// Test: `goals/store_tests.rs` round-trips writes and reads through this tag via
/// the mock; the production path reuses the same constant.
pub const GOAL_TAG: &str = "sm-goal";
/// Abstraction over the SM palace for goal persistence (the source of truth).
///
/// Why: decouples [`super::store::SmGoalStore`] from the concrete, feature-gated
/// `SmMemory` (Dependency Inversion) so the store is testable with a mock and
/// builds without the `sm-memory` feature. The two methods are the complete set
/// the store needs: persist a serialised goal (tagged for later enumeration) and
/// enumerate every persisted goal deterministically on startup.
/// What: an async trait with `remember_goal` (write one tagged JSON entry) and
/// `list_goals` (return every entry's raw JSON for `tag`). Errors are `String`s so
/// the trait carries no dependency on the feature-gated `SmMemoryError`.
/// Test: the mock in `goals/store_tests.rs`; the `SmMemory` impl below.
/// `SmMemory` is the production [`GoalMemory`]: the dedicated SM palace.
///
/// Why: in the daemon the goal store's source of truth is the real SM palace
/// (§9.4). Implementing the seam directly on `SmMemory` keeps writes scoped to the
/// SM palace by construction (SM-4 guarantees) while the store stays
/// palace-agnostic.
/// What: delegates `remember_goal` → `SmMemory::remember_tagged` and `list_goals`
/// → `SmMemory::list_tagged`, mapping the structured `SmMemoryError` to a message.
/// Gated behind `sm-memory` because `SmMemory` (and its heavy memory-core dep) is.
/// Test: exercised whenever the store is built over a real `SmMemory` (the
/// `sm-memory` feature build); the SM-4 scope tests cover the underlying writes.