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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! 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.
/// A no-op [`GoalMemory`]: persists nothing, enumerates nothing (#1477).
///
/// Why: without the `sm-memory` feature there is no SM palace, but the goal store
/// (SM-6) is feature-INDEPENDENT and works over any [`GoalMemory`]. Backing it with
/// this no-op seam lets `sm.goals.*` and the delegation loop DEGRADE GRACEFULLY to
/// in-memory, non-durable operation on the default build — the headline SM-8
/// capability functions, it simply does not persist across a restart — instead of
/// returning "unavailable". This mirrors the recall no-op pattern in `agent::chat`
/// (memory-dependent work becomes an absent no-op, never a hard failure). Durable
/// goal persistence remains behind `--features sm-memory`.
/// What: `remember_goal` succeeds without storing; `list_goals` always returns an
/// empty set (so a fresh-start store rebuilds to empty). Always compiled.
/// Test: `store_tests.rs` exercises the store over a mock seam; the wiring that
/// selects this seam on the default build is covered by `daemon::sm_stdio::tests`
/// (the no-feature `sm.goals.*` / `sm.delegate` branches).
;
/// `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.