@unblocklabs/unblock-memory 0.2.3 → 0.2.5
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 +68 -11
- package/dist/src/config.d.ts +1 -0
- package/dist/src/config.js +9 -5
- package/dist/src/manager.d.ts +1 -0
- package/dist/src/manager.js +4 -0
- package/dist/src/plugin.js +16 -2
- package/dist/src/runtime.d.ts +5 -1
- package/dist/src/runtime.js +6 -3
- package/openclaw.plugin.json +10 -1
- package/package.json +3 -3
- package/skills/memory-curator/SKILL.md +78 -0
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Workspace-native memory for OpenClaw, powered internally by `@unblocklabs/qmd`.
|
|
4
4
|
It keeps one warm QMD store per agent and exposes the standard `memory_search`
|
|
5
|
-
and `memory_get` tools. Search uses semantic chunking v2 and QMD
|
|
6
|
-
a reranker.
|
|
5
|
+
and `memory_get` tools. Search uses semantic chunking v2 and direct QMD vector
|
|
6
|
+
search without query expansion or a reranker, so only the embedding model loads.
|
|
7
7
|
|
|
8
8
|
Optional memory analysis uses those same stored vectors in the same SQLite
|
|
9
9
|
index. It does not re-embed memory, copy vectors, or create another database.
|
|
@@ -39,22 +39,29 @@ directories, or globs into named corpora:
|
|
|
39
39
|
entries: {
|
|
40
40
|
"unblock-memory": {
|
|
41
41
|
config: {
|
|
42
|
+
// Default: avoid repeated model cold starts after idle periods.
|
|
43
|
+
keepEmbeddingModelWarm: true,
|
|
42
44
|
corpora: [
|
|
43
45
|
{
|
|
44
46
|
name: "memory",
|
|
45
47
|
kind: "files",
|
|
46
48
|
paths: ["MEMORY.md", "USER.md", "memory/**/*.md"],
|
|
47
49
|
},
|
|
48
|
-
{
|
|
49
|
-
name: "projects",
|
|
50
|
-
kind: "files",
|
|
51
|
-
paths: ["/absolute/shared/**/*.md"],
|
|
52
|
-
},
|
|
53
50
|
{
|
|
54
51
|
name: "sessions",
|
|
55
52
|
kind: "sessions",
|
|
56
53
|
chatTypes: ["channel", "group"],
|
|
57
54
|
},
|
|
55
|
+
{
|
|
56
|
+
name: "canon",
|
|
57
|
+
kind: "files",
|
|
58
|
+
paths: ["knowledge/canon/**/*.md"],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "reflections",
|
|
62
|
+
kind: "files",
|
|
63
|
+
paths: ["knowledge/reflections/**/*.md"],
|
|
64
|
+
},
|
|
58
65
|
],
|
|
59
66
|
// Optional: omit unless the local analysis worker is installed.
|
|
60
67
|
analysis: {
|
|
@@ -73,8 +80,12 @@ omitted, the plugin creates a `memory` corpus containing `MEMORY.md`, `USER.md`,
|
|
|
73
80
|
and `memory/**/*.md`. Explicit configuration must include exactly one `memory`
|
|
74
81
|
corpus; other unique names may be added for custom material.
|
|
75
82
|
|
|
83
|
+
`keepEmbeddingModelWarm` defaults to `true`, keeping the embedding model and
|
|
84
|
+
context resident after first use. Set it to `false` to restore QMD's five-minute
|
|
85
|
+
idle unload behavior.
|
|
86
|
+
|
|
76
87
|
`memory_search` searches every configured corpus by default. Pass
|
|
77
|
-
`corpora: ["
|
|
88
|
+
`corpora: ["canon"]` to search selected corpora or `corpora: ["all"]` to
|
|
78
89
|
request all of them explicitly. Search results include their corpus name and
|
|
79
90
|
remain readable by passing the returned `qmd://` path to `memory_get`.
|
|
80
91
|
|
|
@@ -107,9 +118,10 @@ channel and group conversations; add `direct` explicitly to include DMs. Run
|
|
|
107
118
|
check its progress or result. Projections are private derived Markdown
|
|
108
119
|
under the agent's `unblock-memory/sessions` state directory and can be rebuilt
|
|
109
120
|
from OpenClaw at any time. Session results include provider, chat type,
|
|
110
|
-
conversation identity, and start time
|
|
111
|
-
clustering index as file memory. This phase does not sync
|
|
112
|
-
on a schedule; refreshes are manual through
|
|
121
|
+
conversation identity, and start time as an ISO 8601 timestamp. They participate
|
|
122
|
+
in the same search and clustering index as file memory. This phase does not sync
|
|
123
|
+
sessions at startup or on a schedule; refreshes are manual through
|
|
124
|
+
`memory_sync_sessions`.
|
|
113
125
|
|
|
114
126
|
Indexes live at `~/.openclaw/agents/<agentId>/unblock-memory/index.sqlite` (or the
|
|
115
127
|
equivalent configured OpenClaw state directory). The first lookup builds the
|
|
@@ -174,5 +186,50 @@ A failed rebuild leaves the stale result intact, while a successful rebuild
|
|
|
174
186
|
atomically replaces it. Analysis is never scheduled automatically. If the worker
|
|
175
187
|
is absent or fails, `memory_search` and `memory_get` continue to work.
|
|
176
188
|
|
|
189
|
+
## Curating canon and reflections
|
|
190
|
+
|
|
191
|
+
The plugin bundles the `memory-curator` skill for turning useful clusters into
|
|
192
|
+
durable knowledge. It becomes available when the plugin is enabled. If the
|
|
193
|
+
agent has an explicit skill allowlist, include `memory-curator`.
|
|
194
|
+
|
|
195
|
+
Keep curated files outside `memory/**` so each file belongs to only one corpus:
|
|
196
|
+
|
|
197
|
+
```text
|
|
198
|
+
knowledge/
|
|
199
|
+
├── canon/
|
|
200
|
+
│ └── gateway-restarts.md
|
|
201
|
+
└── reflections/
|
|
202
|
+
└── 2026-08-26.md
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Canon files are stable topic files updated in place. Each one contains only the
|
|
206
|
+
current affirmative rule or understanding, its update time, current rationale,
|
|
207
|
+
and `qmd://` evidence links. Do not include old procedures, changelogs, or a
|
|
208
|
+
`Supersedes` section: semantic chunking may retrieve those passages without the
|
|
209
|
+
surrounding warning that they are obsolete.
|
|
210
|
+
|
|
211
|
+
Reflection files are daily or timestamped and append-oriented. They hold useful
|
|
212
|
+
patterns, hypotheses, contradictions, and uncertainty, but are not
|
|
213
|
+
authoritative. Both corpora participate in later search and clustering, so a
|
|
214
|
+
later run can reconsider prior reasoning. Repeated derived text is not
|
|
215
|
+
independent corroboration; durable canon still needs underlying source evidence.
|
|
216
|
+
|
|
217
|
+
For a manual run, ask the agent:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
Use $memory-curator to review my memory clusters and curate any durable updates.
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
For recurring curation, use an OpenClaw automation with the same thin message:
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
Use $memory-curator to run the scheduled memory curation cycle.
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The skill lists current clusters, reclusters only when analysis is missing or
|
|
230
|
+
stale, follows representative sources with `memory_get`, and may correctly
|
|
231
|
+
write nothing. Its own writes are indexed for the next cycle; it does not
|
|
232
|
+
recluster recursively in the same run.
|
|
233
|
+
|
|
177
234
|
Existing `unblock-qmd` indexes are derived caches and may be left in place;
|
|
178
235
|
Unblock Memory rebuilds its own index from configured corpora.
|
package/dist/src/config.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export type CorpusConfig = FileCorpusConfig | SessionCorpusConfig;
|
|
|
14
14
|
export declare const DEFAULT_CORPORA: readonly FileCorpusConfig[];
|
|
15
15
|
export type UnblockMemoryConfig = {
|
|
16
16
|
corpora: readonly CorpusConfig[];
|
|
17
|
+
keepEmbeddingModelWarm: boolean;
|
|
17
18
|
analysis: {
|
|
18
19
|
executable?: string;
|
|
19
20
|
};
|
package/dist/src/config.js
CHANGED
|
@@ -65,16 +65,20 @@ function resolveCorpora(value) {
|
|
|
65
65
|
}
|
|
66
66
|
export function resolveConfig(value) {
|
|
67
67
|
if (value === undefined || value === null) {
|
|
68
|
-
return { corpora: DEFAULT_CORPORA, analysis: {} };
|
|
68
|
+
return { corpora: DEFAULT_CORPORA, keepEmbeddingModelWarm: true, analysis: {} };
|
|
69
69
|
}
|
|
70
70
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
71
71
|
throw new Error("unblock-memory config must be an object");
|
|
72
72
|
}
|
|
73
73
|
const config = value;
|
|
74
|
-
assertOnlyKeys(config, ["corpora", "analysis"], "config");
|
|
74
|
+
assertOnlyKeys(config, ["corpora", "keepEmbeddingModelWarm", "analysis"], "config");
|
|
75
75
|
const corpora = resolveCorpora(config.corpora);
|
|
76
|
+
if (config.keepEmbeddingModelWarm !== undefined && typeof config.keepEmbeddingModelWarm !== "boolean") {
|
|
77
|
+
throw new Error("unblock-memory keepEmbeddingModelWarm must be a boolean");
|
|
78
|
+
}
|
|
79
|
+
const keepEmbeddingModelWarm = config.keepEmbeddingModelWarm ?? true;
|
|
76
80
|
if (config.analysis === undefined)
|
|
77
|
-
return { corpora, analysis: {} };
|
|
81
|
+
return { corpora, keepEmbeddingModelWarm, analysis: {} };
|
|
78
82
|
if (!config.analysis || typeof config.analysis !== "object" || Array.isArray(config.analysis)) {
|
|
79
83
|
throw new Error("unblock-memory analysis must be an object");
|
|
80
84
|
}
|
|
@@ -82,9 +86,9 @@ export function resolveConfig(value) {
|
|
|
82
86
|
assertOnlyKeys(analysis, ["executable"], "analysis");
|
|
83
87
|
const configured = analysis.executable;
|
|
84
88
|
if (configured === undefined)
|
|
85
|
-
return { corpora, analysis: {} };
|
|
89
|
+
return { corpora, keepEmbeddingModelWarm, analysis: {} };
|
|
86
90
|
if (typeof configured !== "string" || !configured.trim() || !isAbsolute(configured.trim())) {
|
|
87
91
|
throw new Error("unblock-memory analysis.executable must be an absolute non-empty path");
|
|
88
92
|
}
|
|
89
|
-
return { corpora, analysis: { executable: configured.trim() } };
|
|
93
|
+
return { corpora, keepEmbeddingModelWarm, analysis: { executable: configured.trim() } };
|
|
90
94
|
}
|
package/dist/src/manager.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export declare class QmdMemoryManager implements MemorySearchManagerContract {
|
|
|
31
31
|
workspaceDir: string;
|
|
32
32
|
sources: readonly ResolvedSource[];
|
|
33
33
|
storeFactory?: () => Promise<ManagerStore>;
|
|
34
|
+
keepModelsWarm?: boolean;
|
|
34
35
|
analysisExecutable?: string;
|
|
35
36
|
analysisRunner?: AnalysisRunner;
|
|
36
37
|
sessions?: ManagerSessionConfig;
|
package/dist/src/manager.js
CHANGED
|
@@ -137,6 +137,7 @@ export class QmdMemoryManager {
|
|
|
137
137
|
#workspaceDir;
|
|
138
138
|
#sources;
|
|
139
139
|
#storeFactory;
|
|
140
|
+
#keepModelsWarm;
|
|
140
141
|
#analysisExecutable;
|
|
141
142
|
#analysisRunner;
|
|
142
143
|
#sessions;
|
|
@@ -157,6 +158,7 @@ export class QmdMemoryManager {
|
|
|
157
158
|
this.#workspaceDir = params.workspaceDir;
|
|
158
159
|
this.#sources = new Map(params.sources.map((source) => [source.collection, source]));
|
|
159
160
|
this.#storeFactory = params.storeFactory;
|
|
161
|
+
this.#keepModelsWarm = params.keepModelsWarm ?? true;
|
|
160
162
|
this.#analysisExecutable = params.analysisExecutable;
|
|
161
163
|
this.#analysisRunner = params.analysisRunner ?? runAnalysisWorker;
|
|
162
164
|
this.#sessions = params.sessions;
|
|
@@ -240,6 +242,7 @@ export class QmdMemoryManager {
|
|
|
240
242
|
const { createStore } = await qmdModule;
|
|
241
243
|
const store = await createStore({
|
|
242
244
|
dbPath: this.#dbPath,
|
|
245
|
+
keepModelsWarm: this.#keepModelsWarm,
|
|
243
246
|
config: {
|
|
244
247
|
collections: Object.fromEntries([...this.#sources.values()].map((source) => [
|
|
245
248
|
source.collection,
|
|
@@ -436,6 +439,7 @@ export class QmdMemoryManager {
|
|
|
436
439
|
limit: opts?.maxResults ?? 5,
|
|
437
440
|
minScore: opts?.minScore ?? 0.3,
|
|
438
441
|
allowedPaths,
|
|
442
|
+
expand: false,
|
|
439
443
|
});
|
|
440
444
|
return hits.flatMap((hit) => {
|
|
441
445
|
const collection = /^qmd:\/\/([^/]+)\//.exec(hit.file)?.[1];
|
package/dist/src/plugin.js
CHANGED
|
@@ -58,7 +58,18 @@ function createSearchTool(runtime, ctx) {
|
|
|
58
58
|
minScore,
|
|
59
59
|
signal,
|
|
60
60
|
});
|
|
61
|
-
return jsonResult({
|
|
61
|
+
return jsonResult({
|
|
62
|
+
results: results.map((result) => result.session
|
|
63
|
+
? {
|
|
64
|
+
...result,
|
|
65
|
+
session: {
|
|
66
|
+
...result.session,
|
|
67
|
+
startedAt: new Date(result.session.startedAt).toISOString(),
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
: result),
|
|
71
|
+
provider: "unblock-memory",
|
|
72
|
+
});
|
|
62
73
|
},
|
|
63
74
|
};
|
|
64
75
|
}
|
|
@@ -272,7 +283,10 @@ export function resolveFlushPlan(params = {}) {
|
|
|
272
283
|
}
|
|
273
284
|
export function registerUnblockMemory(api) {
|
|
274
285
|
const config = resolveConfig(api.pluginConfig);
|
|
275
|
-
const runtime = new QmdMemoryRuntime(config.corpora,
|
|
286
|
+
const runtime = new QmdMemoryRuntime(config.corpora, {
|
|
287
|
+
analysisExecutable: config.analysis.executable,
|
|
288
|
+
keepEmbeddingModelWarm: config.keepEmbeddingModelWarm,
|
|
289
|
+
});
|
|
276
290
|
const capability = {
|
|
277
291
|
deterministicRecallToolName: "memory_search",
|
|
278
292
|
supportsPrivateTranscriptRecall: false,
|
package/dist/src/runtime.d.ts
CHANGED
|
@@ -40,7 +40,11 @@ type StoredRunningSessionSync = Extract<StoredSessionSyncStatus, {
|
|
|
40
40
|
export declare function recoverInterruptedSessionSync(directory: string, statusPath: string, stale: StoredRunningSessionSync): Promise<SessionSyncStatus>;
|
|
41
41
|
export declare class QmdMemoryRuntime implements MemoryPluginRuntimeContract {
|
|
42
42
|
#private;
|
|
43
|
-
constructor(corpora: readonly CorpusConfig[],
|
|
43
|
+
constructor(corpora: readonly CorpusConfig[], options?: {
|
|
44
|
+
analysisExecutable?: string;
|
|
45
|
+
keepEmbeddingModelWarm?: boolean;
|
|
46
|
+
stateRoot?: string;
|
|
47
|
+
});
|
|
44
48
|
getMemorySearchManager(params: {
|
|
45
49
|
cfg: OpenClawConfig;
|
|
46
50
|
agentId: string;
|
package/dist/src/runtime.js
CHANGED
|
@@ -65,12 +65,14 @@ export async function recoverInterruptedSessionSync(directory, statusPath, stale
|
|
|
65
65
|
export class QmdMemoryRuntime {
|
|
66
66
|
#corpora;
|
|
67
67
|
#analysisExecutable;
|
|
68
|
+
#keepEmbeddingModelWarm;
|
|
68
69
|
#stateRoot;
|
|
69
70
|
#managers = new Map();
|
|
70
|
-
constructor(corpora,
|
|
71
|
+
constructor(corpora, options = {}) {
|
|
71
72
|
this.#corpora = corpora;
|
|
72
|
-
this.#analysisExecutable = analysisExecutable;
|
|
73
|
-
this.#
|
|
73
|
+
this.#analysisExecutable = options.analysisExecutable;
|
|
74
|
+
this.#keepEmbeddingModelWarm = options.keepEmbeddingModelWarm ?? true;
|
|
75
|
+
this.#stateRoot = options.stateRoot ?? resolveStateDir();
|
|
74
76
|
}
|
|
75
77
|
async getMemorySearchManager(params) {
|
|
76
78
|
let pending = this.#managers.get(params.agentId);
|
|
@@ -203,6 +205,7 @@ export class QmdMemoryRuntime {
|
|
|
203
205
|
workspaceDir,
|
|
204
206
|
dbPath: join(stateDir, "index.sqlite"),
|
|
205
207
|
sources,
|
|
208
|
+
keepModelsWarm: this.#keepEmbeddingModelWarm,
|
|
206
209
|
analysisExecutable: this.#analysisExecutable,
|
|
207
210
|
...(sessionCorpus && sessionSource ? {
|
|
208
211
|
sessions: {
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "unblock-memory",
|
|
3
3
|
"name": "Unblock Memory",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"description": "Indexes, retrieves, and analyzes configured workspace memory with existing QMD vectors.",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"activation": { "onStartup": false },
|
|
8
|
+
"skills": ["./skills"],
|
|
8
9
|
"contracts": { "tools": ["memory_search", "memory_get", "memory_sync_sessions", "memory_sync_status", "memory_recluster", "memory_list_clusters", "memory_fetch_cluster"] },
|
|
9
10
|
"toolMetadata": {
|
|
10
11
|
"memory_sync_sessions": { "sideEffecting": true },
|
|
@@ -14,6 +15,10 @@
|
|
|
14
15
|
"memory_fetch_cluster": { "replaySafe": true }
|
|
15
16
|
},
|
|
16
17
|
"uiHints": {
|
|
18
|
+
"keepEmbeddingModelWarm": {
|
|
19
|
+
"label": "Keep Embedding Model Warm",
|
|
20
|
+
"help": "Keep the QMD embedding model resident after first use. Disable to unload it after five minutes without model activity."
|
|
21
|
+
},
|
|
17
22
|
"corpora": {
|
|
18
23
|
"label": "Memory Corpora",
|
|
19
24
|
"help": "Named groups of exact Markdown files, directories, or globs. Relative paths resolve from each agent workspace."
|
|
@@ -27,6 +32,10 @@
|
|
|
27
32
|
"type": "object",
|
|
28
33
|
"additionalProperties": false,
|
|
29
34
|
"properties": {
|
|
35
|
+
"keepEmbeddingModelWarm": {
|
|
36
|
+
"type": "boolean",
|
|
37
|
+
"default": true
|
|
38
|
+
},
|
|
30
39
|
"corpora": {
|
|
31
40
|
"type": "array",
|
|
32
41
|
"minItems": 1,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unblocklabs/unblock-memory",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Workspace-native memory for OpenClaw, powered by QMD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"access": "public",
|
|
19
19
|
"provenance": true
|
|
20
20
|
},
|
|
21
|
-
"files": ["dist", "README.md", "openclaw.plugin.json"],
|
|
21
|
+
"files": ["dist", "skills", "README.md", "openclaw.plugin.json"],
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "tsc -p tsconfig.build.json",
|
|
24
24
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"preflight": "npm run knip && npm run build && npm run typecheck && npm test && npm run plugin:inspect && npm run plugin:inspect:runtime && npm pack --dry-run"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@unblocklabs/qmd": "https://github.com/unblocklabs-ai/qmd/releases/download/v2.9.
|
|
33
|
+
"@unblocklabs/qmd": "https://github.com/unblocklabs-ai/qmd/releases/download/v2.9.2/unblocklabs-qmd-2.9.2.tgz",
|
|
34
34
|
"chokidar": "5.0.0",
|
|
35
35
|
"picomatch": "^4.0.5",
|
|
36
36
|
"typebox": "1.3.6"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memory-curator
|
|
3
|
+
description: Review Unblock Memory clusters and turn supported current knowledge into canon or exploratory reasoning into reflections.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Curator
|
|
7
|
+
|
|
8
|
+
Use Unblock Memory's semantic clusters to maintain durable workspace knowledge.
|
|
9
|
+
Clusters show similarity, not truth or consensus. Prefer no write over a weak or
|
|
10
|
+
duplicative artifact.
|
|
11
|
+
|
|
12
|
+
## Review clusters
|
|
13
|
+
|
|
14
|
+
1. Call `memory_list_clusters`.
|
|
15
|
+
2. If analysis is missing or stale, call `memory_recluster`, then list again.
|
|
16
|
+
3. Fetch useful clusters with `memory_fetch_cluster`. Treat noise as optional
|
|
17
|
+
review material, not automatically important content.
|
|
18
|
+
4. Follow representative `qmd://` source paths with `memory_get` whenever the
|
|
19
|
+
excerpt lacks context or a conclusion could change durable knowledge.
|
|
20
|
+
5. Distinguish underlying memory or session evidence from earlier canon and
|
|
21
|
+
reflections. Derived artifacts may help locate, challenge, or revise an
|
|
22
|
+
understanding, but repetition does not make them independent evidence.
|
|
23
|
+
6. For each reviewed cluster, choose canon, reflection, or no write.
|
|
24
|
+
|
|
25
|
+
## Write canon
|
|
26
|
+
|
|
27
|
+
Use `knowledge/canon/<stable-topic>.md` for a supported, durable rule or current
|
|
28
|
+
understanding. Update the topic file in place instead of creating dated copies.
|
|
29
|
+
|
|
30
|
+
Every semantic chunk in canon must remain correct if retrieved alone:
|
|
31
|
+
|
|
32
|
+
- State only the current affirmative truth.
|
|
33
|
+
- Remove obsolete instructions rather than preserving them for comparison.
|
|
34
|
+
- Never add `Supersedes`, history, changelog, old-process, or migration sections.
|
|
35
|
+
- Include a human-readable `Updated` timestamp, the current rationale, and
|
|
36
|
+
`qmd://` evidence citations.
|
|
37
|
+
- Preserve uncertainty in the claim itself. If the evidence does not support a
|
|
38
|
+
stable current claim, write a reflection or nothing.
|
|
39
|
+
|
|
40
|
+
A concise shape is sufficient:
|
|
41
|
+
|
|
42
|
+
```markdown
|
|
43
|
+
# Topic
|
|
44
|
+
|
|
45
|
+
Updated: 2026-08-26 14:30 EDT
|
|
46
|
+
|
|
47
|
+
## Current understanding
|
|
48
|
+
|
|
49
|
+
Present-tense rule or facts.
|
|
50
|
+
|
|
51
|
+
## Rationale
|
|
52
|
+
|
|
53
|
+
Why this is the current understanding.
|
|
54
|
+
|
|
55
|
+
## Evidence
|
|
56
|
+
|
|
57
|
+
- qmd://memory/...
|
|
58
|
+
- qmd://sessions/...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Write reflections
|
|
62
|
+
|
|
63
|
+
Use `knowledge/reflections/YYYY-MM-DD.md` for patterns, hypotheses,
|
|
64
|
+
contradictions, open questions, or reasoning worth revisiting. Append a
|
|
65
|
+
timestamped section when the day's file already exists. Label uncertainty
|
|
66
|
+
plainly and include the `qmd://` evidence examined. Reflections are not
|
|
67
|
+
authoritative instructions.
|
|
68
|
+
|
|
69
|
+
## Finish the cycle
|
|
70
|
+
|
|
71
|
+
- Do not rewrite raw memory or session projections.
|
|
72
|
+
- Avoid restating knowledge already captured accurately.
|
|
73
|
+
- Verify new or updated knowledge with `memory_search`, selecting `canon` or
|
|
74
|
+
`reflections` when useful.
|
|
75
|
+
- Report clusters reviewed, files changed, evidence used, uncertainties, and
|
|
76
|
+
intentional skips.
|
|
77
|
+
- Do not recluster again after this cycle's writes. Let them enter the next
|
|
78
|
+
scheduled cycle so the run cannot recursively react to its own output.
|