Good additions — folding those in:
- **"Learnings from today that may apply to other projects" → live.** That's the cross-project test passing cleanly; it's exactly what live context is *for*. Captured.
- **Commune-source asymmetry — this is the structural fix for the leak.** Since the live signal is strong only from Self-authored communes and echo communes are inherently project-centric, the rule becomes: **echo communes are project-context-primary and live-context-conservative** (make few/no live edits); Self-authored communes are where live context meaningfully updates. The leak was almost certainly echo communes dumping project-heavy session-log synthesis into live by default. Gating live-context edits by commune source kills it at the source.

So 17.3 direction locked: cross-project discriminator, default-to-project, thin-roster + cross-project-learnings in live, **echo-conservative-on-live**, core-owned directives, rebalance-pass-later.

**Now memformat — and yes, I think it needs *deep* integration, because it's the missing scaffold that makes the whole split robust.**

Here's the connection: the live/project decision is leak-prone because the Psyche makes it **per fragment, constantly** — every sentence it writes, it re-judges live-vs-project, and drifts. memformat fixes that by moving the decision **per topic, rarely**:

**Proposal — memformat topics are tier-tagged, and the context *is* the memformat structure:**
- Each topic in `memformat.xml` carries a **tier tag: `live` or `project`**.
- The Psyche maintains the evolving schema (adds/removes/retiers topics as it learns Self's realm of work — its original purpose) — but **assigns a tier once, when it creates a topic**, applying the cross-project discriminator a single time instead of per-fragment.
- Communes/echo-communes then just **fill topics**; the topic's tier determines which context file + sync tier the content lands in. The two-slice envelope (`<live-context>`/`<project-context>`) becomes the *rendering* of tier-tagged topics, not an independent judgment.
- The **schema itself** (topic list + tiers) is per-agent live context (travels everywhere); **project-tagged topic content** is instantiated per-project.

Why this revives memformat *and* solves 17.3:
- It gives memformat a **load-bearing job** — newer psyches dropped it because it was optional/decorative; if the context literally *is* the topic structure, maintaining it is mandatory.
- It makes the split **structural and stable** — decide tier per-topic (rare, deliberate) instead of per-fragment (constant, leaky). Echo communes filling project-tagged topics *cannot* leak into live by construction.
- Example tiering: `identity / relationship / user-preferences / cross-project-learnings / project-roster` = **live** topics; `focus / decisions / architecture / todos / blockers` = **project** topics (instantiated per project).

So memformat goes from "decorative seed psyches ignore" to "the schema that *defines* the two-tier context, tier-tagged, self-evolving."

Two questions:
1. **Agree on deep memformat integration** as the tier-tagged scaffold for the split — making it load-bearing rather than optional?
2. The evolving-schema mechanic: the Psyche adds/removes/re-tiers topics over time. Any concern there — e.g. should re-tiering an existing topic (project→live or vice versa) trigger a **migration** of that topic's accumulated content between tiers, or only apply going forward? (Re-tier-with-migration is cleaner but more work; re-tier-forward-only is simpler but leaves old content in the old tier.)


1. agree. 2. with migration, yes. --- please use `gh` cli to clone `BigscreenVR/pi-agent-memory` to the parent folder, then give it a look. please evaluate: can we learn anything from that project which should be applied to SPT's context systems?


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


Confirmed: deep memformat integration + re-tier-with-migration. Cloning the repo for evaluation.


Bash

cd "C:/Users/decid/Documents/projects" && gh repo clone BigscreenVR/pi-agent-memory 2>&1 | tail -5 && echo "--- tree ---" && ls -la pi-agent-memory

Clone pi-agent-memory to parent folder


Cloning into 'pi-agent-memory'...
--- tree ---
total 42
drwxr-xr-x 1 decid 197609     0 May 31 17:11 .
drwxr-xr-x 1 decid 197609     0 May 31 17:11 ..
drwxr-xr-x 1 decid 197609     0 May 31 17:11 .git
-rw-r--r-- 1 decid 197609   240 May 31 17:11 .gitattributes
drwxr-xr-x 1 decid 197609     0 May 31 17:11 .pi
-rw-r--r-- 1 decid 197609  7134 May 31 17:11 CONTEXT.md
-rw-r--r-- 1 decid 197609 11122 May 31 17:11 NLSPEC.md
-rw-r--r-- 1 decid 197609  3739 May 31 17:11 README.md
drwxr-xr-x 1 decid 197609     0 May 31 17:11 docs
-rw-r--r-- 1 decid 197609   145 May 31 17:11 package.json
drwxr-xr-x 1 decid 197609     0 May 31 17:11 scripts
drwxr-xr-x 1 decid 197609     0 May 31 17:11 tests
drwxr-xr-x 1 decid 197609     0 May 31 17:11 workflows
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


1. agree. 2. with migration, yes. --- please use `gh` cli to clone `BigscreenVR/pi-agent-memory` to the parent folder, then give it a look. please evaluate: can we learn anything from that project whi…


Design spt-core binary and messaging library architecture


1	# pi-agent-memory
2
3	Project-local memory extension for [pi](https://pi.dev). It gives agents durable, human-editable project memory that can survive across sessions and be retrieved in future work.
4
5	## Why
6
7	Agent memory should be first-class project context, not private state hidden in one user's profile directory. Project facts, hardware configuration, debugging workarounds, architectural decisions, and conversational context often determine whether a fresh agent can be useful. If that knowledge is saved outside the repository, it does not travel with the project, does not transfer between machines, and cannot be shared with other contributors.
8
9	Existing harness-level memory systems tend to trigger too rarely and store memories in user-profile space. In practice, meaningful context is often discovered during normal work: an agent finds a workaround, the user explains an environment constraint, or a short debugging session uncovers durable state. Losing that between sessions tangibly worsens future outcomes.
10
11	This extension treats memory as repo-owned conditioning material. Memories live in `.pi/memory/`, can be reviewed and versioned with the codebase, and can be loaded by future agents to quickly regain project context. The goal is not background personalization; it is durable, shared project knowledge.
12
13	## What it provides
14
15	- A Project Store at `.pi/memory/`
16	- A generated Retrieval Map at `.pi/memory/INDEX.md`
17	- Topic Files under `.pi/memory/topics/`
18	- `/memory` commands for status, extraction, forgetting, and reindexing
19	- A `/memory-recall` prompt template for explicit memory retrieval
20	- Guardrails that block direct mutation of `.pi/memory/` and `.pi/memory-history/` through normal file tools
21
22	V1 intentionally does **not** provide global memory, vector search, background harvesting, or public agent-callable memory write tools.
23
24	## Install
25
26	### Project-local install
27
28	Copy or link the extension into a pi project:
29
30	```bash
31	mkdir -p .pi/extensions
32	cp -R /path/to/pi-agent-memory/.pi/extensions/agent-memory .pi/extensions/agent-memory
33	```
34
35	Then run `/reload` in pi.
36
37	### User/global install
38
39	For use across projects, link the extension into your user pi extensions directory:
40
41	```bash
42	mkdir -p ~/.pi/agent/extensions
43	ln -s /path/to/pi-agent-memory/.pi/extensions/agent-memory ~/.pi/agent/extensions/agent-memory
44	```
45
46	On Windows, use a junction:
47
48	```powershell
49	New-Item -ItemType Junction `
50	  -Path "$env:USERPROFILE\.pi\agent\extensions\agent-memory" `
51	  -Target "H:\pi-agent-memory\.pi\extensions\agent-memory"
52	```
53
54	Then run `/reload` in any open pi session.
55
56	## Commands
57
58	```text
59	/memory status
60	/memory extract
61	/memory forget <memory-id> [expected-hash]
62	/memory reindex [project]
63	/memory-recall <query>
64	```
65
66	### Typical workflow
67
68	1. Work normally in pi.
69	2. When something durable should be remembered, run:
70
71	   ```text
72	   /memory extract
73	   ```
74
75	3. Review and approve proposed Memory Candidates.
76	4. Future agents will see a lightweight memory awareness note when project memories exist.
77	5. For explicit recall, run:
78
79	   ```text
80	   /memory-recall what do we know about testing conventions?
81	   ```
82
83	## Memory Store layout
84
85	```text
86	.pi/
87	  memory/
88	    INDEX.md
89	    topics/
90	      <topic>.md
91	  memory-history/
92	    changes.jsonl
93	```
94
95	Do not edit `.pi/memory/INDEX.md` directly. If Topic Files are edited manually, run:
96
97	```text
98	/memory reindex
99	```
100
101	## Dependencies
102
103	- [Attractor](https://github.com/BigscreenVR/attractor) is a project dependency for workflow-mediated memory approval artifacts under `workflows/`.
104
105	## Development
106
107	Run conformance tests:
108
109	```bash
110	npm test
111	```
112
113	Relevant docs:
114
115	- `CONTEXT.md` — domain language
116	- `NLSPEC.md` — behavior specification
117	- `docs/adr/` — architectural decisions
118

# pi-agent-memory

Project-local memory extension for [pi](https://pi.dev). It gives agents durable, human-editable project memory that can survive across sessions and be retrieved in future work.

## Why

Agent memory should be first-class project context, not private state hidden in one user's profile directory. Project facts, hardware configuration, debugging workarounds, architectural decisions, and conversational context often determine whether a fresh agent can be useful. If that knowledge is saved outside the repository, it does not travel with the project, does not transfer between machines, and cannot be shared with other contributors.

Existing harness-level memory systems tend to trigger too rarely and store memories in user-profile space. In practice, meaningful context is often discovered during normal work: an agent finds a workaround, the user explains an environment constraint, or a short debugging session uncovers durable state. Losing that between sessions tangibly worsens future outcomes.

This extension treats memory as repo-owned conditioning material. Memories live in `.pi/memory/`, can be reviewed and versioned with the codebase, and can be loaded by future agents to quickly regain project context. The goal is not background personalization; it is durable, shared project knowledge.

## What it provides

- A Project Store at `.pi/memory/`
- A generated Retrieval Map at `.pi/memory/INDEX.md`
- Topic Files under `.pi/memory/topics/`
- `/memory` commands for status, extraction, forgetting, and reindexing
- A `/memory-recall` prompt template for explicit memory retrieval
- Guardrails that block direct mutation of `.pi/memory/` and `.pi/memory-history/` through normal file tools

V1 intentionally does **not** provide global memory, vector search, background harvesting, or public agent-callable memory write tools.

## Install

### Project-local install

Copy or link the extension into a pi project:

```bash
mkdir -p .pi/extensions
cp -R /path/to/pi-agent-memory/.pi/extensions/agent-memory .pi/extensions/agent-memory
```

Then run `/reload` in pi.

### User/global install

For use across projects, link the extension into your user pi extensions directory:

```bash
mkdir -p ~/.pi/agent/extensions
ln -s /path/to/pi-agent-memory/.pi/extensions/agent-memory ~/.pi/agent/extensions/agent-memory
```

On Windows, use a junction:

```powershell
New-Item -ItemType Junction `
  -Path "$env:USERPROFILE\.pi\agent\extensions\agent-memory" `
  -Target "H:\pi-agent-memory\.pi\extensions\agent-memory"
```

Then run `/reload` in any open pi session.

## Commands

```text
/memory status
/memory extract
/memory forget <memory-id> [expected-hash]
/memory reindex [project]
/memory-recall <query>
```

### Typical workflow

1. Work normally in pi.
2. When something durable should be remembered, run:

   ```text
   /memory extract
   ```

3. Review and approve proposed Memory Candidates.
4. Future agents will see a lightweight memory awareness note when project memories exist.
5. For explicit recall, run:

   ```text
   /memory-recall what do we know about testing conventions?
   ```

## Memory Store layout

```text
.pi/
  memory/
    INDEX.md
    topics/
      <topic>.md
  memory-history/
    changes.jsonl
```

Do not edit `.pi/memory/INDEX.md` directly. If Topic Files are edited manually, run:

```text
/memory reindex
```

## Dependencies

- [Attractor](https://github.com/BigscreenVR/attractor) is a project dependency for workflow-mediated memory approval artifacts under `workflows/`.

## Development

Run conformance tests:

```bash
npm test
```

Relevant docs:

- `CONTEXT.md` — domain language
- `NLSPEC.md` — behavior specification
- `docs/adr/` — architectural decisions


1	# Agent Memory Extension NLSPEC
2
3	## Purpose
4
5	The Agent Memory extension gives a pi project a durable, human-editable memory store. Memories are project-local facts, decisions, preferences, and conventions that future agents can retrieve with normal file tools.
6
7	V1 intentionally avoids global memory, vector search, autonomous background writes, and public memory mutation tools.
8
9	## Non-goals
10
11	- No Global Store in v1.
12	- No vector database or embeddings.
13	- No custom memory search/read tool.
14	- No always-on background memory extraction.
15	- No public agent-callable memory write tools.
16	- No hard guarantee against shell-based mutation beyond simple obvious command blocking.
17
18	## Repository layout
19
20	```text
21	.pi/
22	  extensions/
23	    agent-memory/
24	      index.ts
25	      prompts/
26	        memory-recall.md
27	  memory/
28	    INDEX.md
29	    topics/
30	      <topic>.md
31	```
32
33	`.pi/memory/` is created lazily only when approved memories are first written.
34
35	## Memory store
36
37	The Project Store lives at `.pi/memory/`.
38
39	It contains:
40
41	- `INDEX.md`: generated Retrieval Map.
42	- `topics/*.md`: human-editable Topic Files.
43
44	### Topic filename normalization
45
46	Candidate topic names are normalized as:
47
48	- lowercase
49	- trimmed
50	- spaces/underscores converted to hyphens
51	- unsafe characters removed
52	- path separators rejected/removed
53	- fallback: `general`
54
55	## Memory Block format
56
57	Each memory is a Markdown section inside a Topic File.
58
59	```md
60	## mem_YYYYMMDD_HHMMSS_xxxx
61
62	Concepts: testing, auth, integration tests
63	Aliases: unit tests, test strategy
64	Applies to: src/auth/**, tests/auth/**
65	Source: user-confirmed
66	Created: 2026-05-27T10:30:12Z
67	Updated: 2026-05-27T10:30:12Z
68	Hash: sha256:<canonical-content-hash>
69
70	Prefer integration tests for auth flows because they catch middleware and session behavior that unit tests miss.
71	```
72
73	Required fields:
74
75	- heading matching `## mem_YYYYMMDD_HHMMSS_xxxx`
76	- non-empty `Concepts:`
77	- `Source:`
78	- `Created:`
79	- non-empty body
80	- `Hash:` for extension-created new blocks; legacy blocks without `Hash:` remain readable but should produce a reindex warning
81
82	Optional fields:
83
84	- `Aliases:`
85	- `Applies to:`
86	- `Updated:`; extension-created memories should include it equal to `Created:`
87
88	`Hash:` is a SHA-256 Canonical Content Hash for future OCC-protected update/forget operations. It is computed from canonicalized semantic fields, excluding `Updated:` and excluding `Hash:` itself. The canonical hash input is line-based:
89
90	```text
91	id:<memory-id>
92	concepts:<comma-joined normalized concepts in stored order>
93	aliases:<comma-joined normalized aliases in stored order>
94	applies_to:<comma-joined normalized applies_to in stored order>
95	source:<source>
96	created:<created>
97	body:<body trimmed with normalized LF line endings>
98	```
99
100	Append operations do not use OCC; appending agents are responsible for consulting relevant memories and avoiding duplicates before proposing a new memory.
101
102	Concepts and aliases are normalized by lowercase + trim only.
103
104	## Topic File format
105
106	A topic file starts with a human-editable warning:
107
108	```md
109	# testing
110
111	Human-editable project memories. Keep each memory as a `## mem_...` block with metadata lines, then run `/memory reindex` after manual edits.
112	```
113
114	New Memory Blocks are appended to the end of the selected Topic File.
115
116	## Retrieval Map format
117
118	`INDEX.md` is generated and overwritten by the extension.
119
120	```md
121	# Memory Retrieval Map
122
123	Generated by the agent-memory extension. Do not edit manually; edit Topic Files and run `/memory reindex`.
124
125	This file helps agents find relevant project memories. Start here, then read topic files under `topics/`.
126
127	## Topics
128
129	### testing
130	Path: topics/testing.md
131	Concepts: testing, auth, integration tests
132	Aliases: unit tests, test strategy
133	Applies to: src/auth/**
134
135	Summary:
136	- Prefer integration tests for auth flows.
137	- Mock external OAuth providers at the boundary.
138	```
139
140	The Retrieval Map is a lightweight retrieval aid, not a duplicate memory database.
141
142	## Prompt template
143
144	The extension bundles `prompts/memory-recall.md` and registers it via `resources_discover`.
145
146	The prompt should:
147
148	- retrieve memories relevant to a user query
149	- prefer using a scout/subagent if available
150	- otherwise use root-agent `read`/`grep`/`find`
151	- start at `.pi/memory/INDEX.md`
152	- read relevant Topic Files as needed
153	- return Memory IDs, topic paths, and short excerpts
154	- if `.pi/memory/INDEX.md` is missing, report: `No project memories exist yet.`
155
156	## Memory awareness
157
158	Writing memories is worthless if a fresh agent never knows they exist. To close the recall loop, the extension surfaces the Project Store at the start of each agent turn.
159
160	On `before_agent_start`, if `.pi/memory/INDEX.md` exists and the store has at least one topic, the extension appends a short awareness note to the chained system prompt. The note:
161
162	- states that durable project memories exist and points at `.pi/memory/INDEX.md`
163	- lists the current topic names
164	- tells the agent to consult the Retrieval Map and read relevant Topic Files before acting on related tasks
165	- reminds the agent not to edit `.pi/memory/` directly, and to use `/memory extract` and `/memory reindex`
166
167	Constraints:
168
169	- The note surfaces topic names and a pointer only; it never inlines memory bodies, so context cost stays bounded regardless of store size.
170	- The note is appended (chained), never replaces an existing system prompt.
171	- The note is skipped while `/memory extract` is running so it cannot pollute the extraction prompt.
172	- If the store does not exist or has no topics, no note is added.
173
174	Manual retrieval via the `memory-recall` prompt template remains available for explicit, deeper retrieval. The awareness note is the lightweight always-on nudge; the prompt template is the on-demand deep retrieval.
175
176	## Tool surface
177
178	### Normal tools
179
180	`memory_stores` is always available.
181
182	It reports:
183
184	```json
185	{
186	  "stores": [
187	    {
188	      "name": "project",
189	      "path": ".pi/memory",
190	      "exists": true,
191	      "indexPath": ".pi/memory/INDEX.md",
192	      "topics": ["testing", "architecture"]
193	    }
194	  ]
195	}
196	```
197
198	### Extraction-only tool
199
200	`memory_candidate` is available only during `/memory extract`.
201
202	It is non-mutating. It records proposed memories for later user review.
203
204	Required input:
205
206	```json
207	{
208	  "topic": "testing",
209	  "body": "Prefer integration tests for auth flows because...",
210	  "concepts": ["testing", "auth"]
211	}
212	```
213
214	Optional input:
215
216	```json
217	{
218	  "aliases": ["integration tests"],
219	  "applies_to": ["src/auth/**"]
220	}
221	```
222
223	Limits:
224
225	- maximum 5 candidates per extraction run
226	- body max 1000 characters
227	- concepts max 8
228	- aliases max 8
229	- applies_to max 8
230
231	If more than 5 candidates are proposed, keep the first 5 and warn.
232
233	No public `memory_add`, `memory_update`, or `memory_search` tools exist in v1. Forgetting is command-only through `/memory forget`.
234
235	## Commands
236
237	### `/memory status`
238
239	Shows Project Store status:
240
241	- path
242	- whether store exists
243	- index path
244	- topic names
245	- stale Retrieval Map warning if any topic file mtime is newer than `INDEX.md`
246
247	If `.pi/memory/` does not exist, show `Project Store: not created yet.` Do not create files.
248
249	### `/memory extract`
250
251	Explicit user-invoked Memory Extraction workflow.
252
253	Input to extraction:
254
255	- current session branch only
256	- user and assistant text only
257	- no tool results
258	- no extension custom entries/checkpoints
259	- existing `INDEX.md` content if present
260
261	Behavior:
262
263	1. Temporarily expose `memory_candidate`.
264	2. Ask the current root model to propose up to 5 add-only Memory Candidates.
265	3. Instruct the model to consult the Retrieval Map and relevant Topic Files before proposing appends, and to avoid duplicates already represented in memory.
266	4. Disable `memory_candidate` after extraction.
267	5. Show candidates one-by-one.
268	6. User may approve, reject, stop, or edit routing/index fields only.
269	7. Routing edit opens a small JSON editor for `topic`, `concepts`, `aliases`, and `applies_to`.
270	8. Invalid routing JSON reopens the editor with an error and cancel option.
271	9. Batch-write all approved candidates at the end.
272	10. Append Memory Blocks to Topic Files.
273	11. Rebuild `INDEX.md` using the same logic as `/memory reindex`.
274	12. If any memories were written, append a non-context `memory-extraction` checkpoint containing created memory IDs and timestamp.
275
276	If no candidates are found, notify the user and offer retry with guidance.
277
278	If `.pi/memory/` is missing, create it lazily only when approved candidates are written.
279
280	Approved extraction memories use:
281
282	```md
283	Source: user-confirmed
284	```
285
286	### `/memory forget <memory-id> [expected-hash]`
287
288	Explicitly removes one Memory Block from its Topic File, then rebuilds `INDEX.md` using the same logic as `/memory reindex`.
289
290	Behavior:
291
292	1. Validate the memory id shape.
293	2. Search Topic Files for the matching Memory Block.
294	3. If `expected-hash` is provided, fail when the current block hash differs.
295	4. Remove only the matching Memory Block section.
296	5. Rebuild `INDEX.md`.
297
298	Legacy Memory Blocks without `Hash:` may be forgotten without an expected hash. Future OCC-aware callers should provide `expected-hash` when available.
299
300	### `/memory reindex [store]`
301
302	V1 only supports the Project Store.
303
304	Accepted forms:
305
306	- `/memory reindex`
307	- `/memory reindex project`
308
309	If `.pi/memory/` does not exist, report `Project Store does not exist; nothing to reindex.`
310
311	Reindex behavior:
312
313	- parse `topics/*.md`
314	- best-effort index valid Memory Blocks
315	- warn on malformed blocks
316	- warn on legacy valid blocks without `Hash:`
317	- warn on blocks whose `Hash:` does not match current canonical content
318	- overwrite `INDEX.md`
319	- no confirmation required
320
321	## Safety rules
322
323	### Built-in file writes
324
325	Built-in `edit` and `write` tool calls targeting `.pi/memory/` are always blocked.
326
327	Internal extension writes are allowed.
328
329	### Bash commands
330
331	If a `bash` command contains `.pi/memory` and appears write/destructive, block it.
332
333	Block patterns include:
334
335	- `>`
336	- `>>`
337	- `rm`
338	- `mv`
339	- `cp`
340	- `sed -i`
341	- `perl -i`
342	- `tee`
343	- `touch`
344	- `mkdir`
345	- `rmdir`
346
347	Likely read/search commands are allowed, such as:
348
349	- `grep`
350	- `rg`
351	- `cat`
352	- `find`
353	- `ls`
354
355	Blocked message:
356
357	> Direct bash mutation of `.pi/memory/` is blocked. Use `/memory extract` to add memories or `/memory reindex` to rebuild the Retrieval Map.
358
359	### Memory Consultation
360
361	When built-in `read` or `grep` touches `.pi/memory/`, show a UI indication that memory was consulted.
362
363	In v1, consultations are UI-only and are not persisted as custom session entries.
364
365	## Human edits
366
367	Humans may edit Topic Files directly outside pi.
368
369	After manual edits, users should run `/memory reindex`.
370
371	`/memory status` warns if topic files appear newer than the Retrieval Map.
372
373	## Concurrency
374
375	Extraction writes, forget, and reindex use a store-level mutation queue/lock, e.g. `.pi/memory/.lock`.
376
377	No rollback is required for failed multi-file writes in v1. Fail with a clear error. Git can be used for recovery.
378
379	## Session persistence
380
381	Successful `/memory extract` writes append a non-context custom entry:
382
383	```json
384	{
385	  "type": "memory-extraction",
386	  "store": "project",
387	  "memoryIds": ["mem_20260527_103012_a7f3"],
388	  "timestamp": "2026-05-27T10:30:12Z"
389	}
390	```
391
392	This checkpoint is not included in future extraction prompts in v1.
393

# Agent Memory Extension NLSPEC

## Purpose

The Agent Memory extension gives a pi project a durable, human-editable memory store. Memories are project-local facts, decisions, preferences, and conventions that future agents can retrieve with normal file tools.

V1 intentionally avoids global memory, vector search, autonomous background writes, and public memory mutation tools.

## Non-goals

- No Global Store in v1.
- No vector database or embeddings.
- No custom memory search/read tool.
- No always-on background memory extraction.
- No public agent-callable memory write tools.
- No hard guarantee against shell-based mutation beyond simple obvious command blocking.

## Repository layout

```text
.pi/
  extensions/
    agent-memory/
      index.ts
      prompts/
        memory-recall.md
  memory/
    INDEX.md
    topics/
      <topic>.md
```

`.pi/memory/` is created lazily only when approved memories are first written.

## Memory store

The Project Store lives at `.pi/memory/`.

It contains:

- `INDEX.md`: generated Retrieval Map.
- `topics/*.md`: human-editable Topic Files.

### Topic filename normalization

Candidate topic names are normalized as:

- lowercase
- trimmed
- spaces/underscores converted to hyphens
- unsafe characters removed
- path separators rejected/removed
- fallback: `general`

## Memory Block format

Each memory is a Markdown section inside a Topic File.

```md
## mem_YYYYMMDD_HHMMSS_xxxx

Concepts: testing, auth, integration tests
Aliases: unit tests, test strategy
Applies to: src/auth/**, tests/auth/**
Source: user-confirmed
Created: 2026-05-27T10:30:12Z
Updated: 2026-05-27T10:30:12Z
Hash: sha256:<canonical-content-hash>

Prefer integration tests for auth flows because they catch middleware and session behavior that unit tests miss.
```

Required fields:

- heading matching `## mem_YYYYMMDD_HHMMSS_xxxx`
- non-empty `Concepts:`
- `Source:`
- `Created:`
- non-empty body
- `Hash:` for extension-created new blocks; legacy blocks without `Hash:` remain readable but should produce a reindex warning

Optional fields:

- `Aliases:`
- `Applies to:`
- `Updated:`; extension-created memories should include it equal to `Created:`

`Hash:` is a SHA-256 Canonical Content Hash for future OCC-protected update/forget operations. It is computed from canonicalized semantic fields, excluding `Updated:` and excluding `Hash:` itself. The canonical hash input is line-based:

```text
id:<memory-id>
concepts:<comma-joined normalized concepts in stored order>
aliases:<comma-joined normalized aliases in stored order>
applies_to:<comma-joined normalized applies_to in stored order>
source:<source>
created:<created>
body:<body trimmed with normalized LF line endings>
```

Append operations do not use OCC; appending agents are responsible for consulting relevant memories and avoiding duplicates before proposing a new memory.

Concepts and aliases are normalized by lowercase + trim only.

## Topic File format

A topic file starts with a human-editable warning:

```md
# testing

Human-editable project memories. Keep each memory as a `## mem_...` block with metadata lines, then run `/memory reindex` after manual edits.
```

New Memory Blocks are appended to the end of the selected Topic File.

## Retrieval Map format

`INDEX.md` is generated and overwritten by the extension.

```md
# Memory Retrieval Map

Generated by the agent-memory extension. Do not edit manually; edit Topic Files and run `/memory reindex`.

This file helps agents find relevant project memories. Start here, then read topic files under `topics/`.

## Topics

### testing
Path: topics/testing.md
Concepts: testing, auth, integration tests
Aliases: unit tests, test strategy
Applies to: src/auth/**

Summary:
- Prefer integration tests for auth flows.
- Mock external OAuth providers at the boundary.
```

The Retrieval Map is a lightweight retrieval aid, not a duplicate memory database.

## Prompt template

The extension bundles `prompts/memory-recall.md` and registers it via `resources_discover`.

The prompt should:

- retrieve memories relevant to a user query
- prefer using a scout/subagent if available
- otherwise use root-agent `read`/`grep`/`find`
- start at `.pi/memory/INDEX.md`
- read relevant Topic Files as needed
- return Memory IDs, topic paths, and short excerpts
- if `.pi/memory/INDEX.md` is missing, report: `No project memories exist yet.`

## Memory awareness

Writing memories is worthless if a fresh agent never knows they exist. To close the recall loop, the extension surfaces the Project Store at the start of each agent turn.

On `before_agent_start`, if `.pi/memory/INDEX.md` exists and the store has at least one topic, the extension appends a short awareness note to the chained system prompt. The note:

- states that durable project memories exist and points at `.pi/memory/INDEX.md`
- lists the current topic names
- tells the agent to consult the Retrieval Map and read relevant Topic Files before acting on related tasks
- reminds the agent not to edit `.pi/memory/` directly, and to use `/memory extract` and `/memory reindex`

Constraints:

- The note surfaces topic names and a pointer only; it never inlines memory bodies, so context cost stays bounded regardless of store size.
- The note is appended (chained), never replaces an existing system prompt.
- The note is skipped while `/memory extract` is running so it cannot pollute the extraction prompt.
- If the store does not exist or has no topics, no note is added.

Manual retrieval via the `memory-recall` prompt template remains available for explicit, deeper retrieval. The awareness note is the lightweight always-on nudge; the prompt template is the on-demand deep retrieval.

## Tool surface

### Normal tools

`memory_stores` is always available.

It reports:

```json
{
  "stores": [
    {
      "name": "project",
      "path": ".pi/memory",
      "exists": true,
      "indexPath": ".pi/memory/INDEX.md",
      "topics": ["testing", "architecture"]
    }
  ]
}
```

### Extraction-only tool

`memory_candidate` is available only during `/memory extract`.

It is non-mutating. It records proposed memories for later user review.

Required input:

```json
{
  "topic": "testing",
  "body": "Prefer integration tests for auth flows because...",
  "concepts": ["testing", "auth"]
}
```

Optional input:

```json
{
  "aliases": ["integration tests"],
  "applies_to": ["src/auth/**"]
}
```

Limits:

- maximum 5 candidates per extraction run
- body max 1000 characters
- concepts max 8
- aliases max 8
- applies_to max 8

If more than 5 candidates are proposed, keep the first 5 and warn.

No public `memory_add`, `memory_update`, or `memory_search` tools exist in v1. Forgetting is command-only through `/memory forget`.

## Commands

### `/memory status`

Shows Project Store status:

- path
- whether store exists
- index path
- topic names
- stale Retrieval Map warning if any topic file mtime is newer than `INDEX.md`

If `.pi/memory/` does not exist, show `Project Store: not created yet.` Do not create files.

### `/memory extract`

Explicit user-invoked Memory Extraction workflow.

Input to extraction:

- current session branch only
- user and assistant text only
- no tool results
- no extension custom entries/checkpoints
- existing `INDEX.md` content if present

Behavior:

1. Temporarily expose `memory_candidate`.
2. Ask the current root model to propose up to 5 add-only Memory Candidates.
3. Instruct the model to consult the Retrieval Map and relevant Topic Files before proposing appends, and to avoid duplicates already represented in memory.
4. Disable `memory_candidate` after extraction.
5. Show candidates one-by-one.
6. User may approve, reject, stop, or edit routing/index fields only.
7. Routing edit opens a small JSON editor for `topic`, `concepts`, `aliases`, and `applies_to`.
8. Invalid routing JSON reopens the editor with an error and cancel option.
9. Batch-write all approved candidates at the end.
10. Append Memory Blocks to Topic Files.
11. Rebuild `INDEX.md` using the same logic as `/memory reindex`.
12. If any memories were written, append a non-context `memory-extraction` checkpoint containing created memory IDs and timestamp.

If no candidates are found, notify the user and offer retry with guidance.

If `.pi/memory/` is missing, create it lazily only when approved candidates are written.

Approved extraction memories use:

```md
Source: user-confirmed
```

### `/memory forget <memory-id> [expected-hash]`

Explicitly removes one Memory Block from its Topic File, then rebuilds `INDEX.md` using the same logic as `/memory reindex`.

Behavior:

1. Validate the memory id shape.
2. Search Topic Files for the matching Memory Block.
3. If `expected-hash` is provided, fail when the current block hash differs.
4. Remove only the matching Memory Block section.
5. Rebuild `INDEX.md`.

Legacy Memory Blocks without `Hash:` may be forgotten without an expected hash. Future OCC-aware callers should provide `expected-hash` when available.

### `/memory reindex [store]`

V1 only supports the Project Store.

Accepted forms:

- `/memory reindex`
- `/memory reindex project`

If `.pi/memory/` does not exist, report `Project Store does not exist; nothing to reindex.`

Reindex behavior:

- parse `topics/*.md`
- best-effort index valid Memory Blocks
- warn on malformed blocks
- warn on legacy valid blocks without `Hash:`
- warn on blocks whose `Hash:` does not match current canonical content
- overwrite `INDEX.md`
- no confirmation required

## Safety rules

### Built-in file writes

Built-in `edit` and `write` tool calls targeting `.pi/memory/` are always blocked.

Internal extension writes are allowed.

### Bash commands

If a `bash` command contains `.pi/memory` and appears write/destructive, block it.

Block patterns include:

- `>`
- `>>`
- `rm`
- `mv`
- `cp`
- `sed -i`
- `perl -i`
- `tee`
- `touch`
- `mkdir`
- `rmdir`

Likely read/search commands are allowed, such as:

- `grep`
- `rg`
- `cat`
- `find`
- `ls`

Blocked message:

> Direct bash mutation of `.pi/memory/` is blocked. Use `/memory extract` to add memories or `/memory reindex` to rebuild the Retrieval Map.

### Memory Consultation

When built-in `read` or `grep` touches `.pi/memory/`, show a UI indication that memory was consulted.

In v1, consultations are UI-only and are not persisted as custom session entries.

## Human edits

Humans may edit Topic Files directly outside pi.

After manual edits, users should run `/memory reindex`.

`/memory status` warns if topic files appear newer than the Retrieval Map.

## Concurrency

Extraction writes, forget, and reindex use a store-level mutation queue/lock, e.g. `.pi/memory/.lock`.

No rollback is required for failed multi-file writes in v1. Fail with a clear error. Git can be used for recovery.

## Session persistence

Successful `/memory extract` writes append a non-context custom entry:

```json
{
  "type": "memory-extraction",
  "store": "project",
  "memoryIds": ["mem_20260527_103012_a7f3"],
  "timestamp": "2026-05-27T10:30:12Z"
}
```

This checkpoint is not included in future extraction prompts in v1.


cd "C:/Users/decid/Documents/projects/pi-agent-memory" && echo "=== docs ===" && ls docs && echo "=== .pi ===" && ls -la .pi && echo "=== scripts ===" && ls scripts && echo "=== workflows ===" && ls workflows && echo "=== tests ===" && ls tests && echo "=== gitattributes ===" && cat .gitattributes

List pi-agent-memory subdirs


=== docs ===
adr
=== .pi ===
total 4
drwxr-xr-x 1 decid 197609 0 May 31 17:11 .
drwxr-xr-x 1 decid 197609 0 May 31 17:11 ..
drwxr-xr-x 1 decid 197609 0 May 31 17:11 extensions
drwxr-xr-x 1 decid 197609 0 May 31 17:11 memory
=== scripts ===
llm_endpoint_probe.py
=== workflows ===
memory-health-dedup.dot
=== tests ===
README.md
conformance
fixtures
=== gitattributes ===
# Normalize all text files to LF in the repository and the working tree.
* text=auto eol=lf

# Binaries: never touch line endings (insurance for future fixtures/assets).
*.png  binary
*.jpg  binary
*.gif  binary
*.webp binary
*.ico  binary
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


1	# Agent Memory
2
3	Agent Memory gives pi agents durable knowledge that can survive beyond a single conversation and be reused in future work.
4
5	## Language
6
7	**Memory**:
8	A durable fact or preference the agent may retrieve and use in future turns. A **Memory** belongs to exactly one **Memory Store** and is created by explicit user instruction, user-confirmed agent proposal, or an automated agent with the required **Memory Capability**.
9	_Avoid_: Note, cache entry, recollection
10
11	**Memory Candidate**:
12	A possible **Memory** identified by the agent but not yet saved. A **Memory Candidate** becomes a **Memory** only after user confirmation or by an agent with the required **Memory Capability**.
13	_Avoid_: Auto-memory, inferred memory
14
15	**Memory Store**:
16	A named storage location containing **Memories**, **Topic Files**, and a **Retrieval Map**. A Memory Store is the boundary for retrieval, storage, and permissions.
17	_Avoid_: Scope, namespace, bucket
18
19	**Memory Capability**:
20	A future-facing permission granted to an agent for memory mutation operations. Memory Capability is not active in v1, where Structured Memory Writes happen only inside explicit user-invoked commands.
21	_Avoid_: Profile, role, trust level, read permission
22
23	**Forget**:
24	To remove a **Memory** from the current Markdown files in a **Memory Store** so it is no longer available for active retrieval. Forgetting does not rewrite git history.
25	_Avoid_: Archive, retire, hide
26
27	**Project Store**:
28	The built-in **Memory Store** for memories that apply to one repository or working directory. The Project Store lives at the fixed project path `.pi/memory/`.
29	_Avoid_: Project Scope, repo memory, local memory
30
31	**Global Store**:
32	A deferred **Memory Store** concept for memories that apply across repositories for the user. Global Store is not part of v1.
33	_Avoid_: Global Scope, user memory, universal memory
34
35	**Retrieval Map**:
36	A lightweight Markdown index that helps agents retrieve relevant **Memories** using text search. A Retrieval Map uses topic summaries, concepts, aliases, relationships, and paths; it does not use vectors or duplicate full memory content.
37	_Avoid_: Vector index, embedding store, full memory mirror
38
39	**Topic File**:
40	A Markdown file containing multiple related **Memories** for a concept area. Topic Files are the canonical storage unit for memories and are created lazily on first write.
41	_Avoid_: Per-memory file, memory blob
42
43	**Memory Block**:
44	A structured Markdown section inside a **Topic File** that represents one **Memory**. A Memory Block uses the stable memory id as its heading, followed by plain metadata lines, a canonical content hash, and body text; it does not have a separate title.
45	_Avoid_: YAML block, database row, titled note
46
47	**Canonical Content Hash**:
48	A SHA-256 version identifier for a **Memory Block** computed from canonicalized semantic fields, excluding `Updated` and excluding the hash field itself. Canonical Content Hashes let Structured Memory Writes detect stale update or forget operations without treating generated metadata churn as a conflict.
49	_Avoid_: Checksum, etag, raw markdown hash
50
51	**Memory Consultation**:
52	A built-in tool access to a **Memory Store** file that indicates the agent has read or searched memory. In v1, Memory Consultation is shown in the UI only and is not persisted as a custom session entry.
53	_Avoid_: Memory read tool, memory search tool
54
55	**Memory Snapshot**:
56	The exact set of **Memory Blocks** and version identifiers an agent has consulted before acting. A Memory Snapshot is the basis for detecting stale memory updates when other agents may have changed the same **Memory Store** concurrently.
57	_Avoid_: Loaded memory, memory in brain, memory context
58
59	**Structured Memory Write**:
60	A change to a **Memory Store** performed through a memory-specific tool that preserves **Memory Block** format and keeps the **Retrieval Map** current. Built-in file write tools are not valid Structured Memory Writes.
61	_Avoid_: Direct edit, manual memory patch
62
63	**Memory Tool Surface**:
64	The set of memory-specific tools available to agents. In v1, the normal Memory Tool Surface exposes `memory_stores` only; `/memory extract` temporarily exposes non-mutating `memory_candidate`. Retrieval uses built-in file tools against the Retrieval Map and Topic Files, and Structured Memory Writes are performed internally by the extension after user approval.
65	_Avoid_: Memory search API, memory read API, public memory write tool
66
67	**Memory Recall Prompt**:
68	A prompt template that asks an existing scout-style agent to retrieve relevant Memories from the Project Store. Memory Recall Prompt reuses general retrieval agents instead of defining a dedicated memory retrieval agent.
69	_Avoid_: Dedicated memory retriever agent, memory search tool
70
71	**Memory Extraction**:
72	An explicit user-invoked workflow that inspects the current session branch, proposes Memory Candidates, and writes approved candidates to the Project Store. Successful Memory Extraction writes a non-context checkpoint entry with the created memory ids.
73	_Avoid_: Background memory harvesting, silent memory write
74
75	**Memory Authority Source**:
76	The mechanism that grants Memory Capabilities to an agent invocation. In v1, installing the project-local extension does not grant mutation authority by default; mutation authority is tied to explicit automation behavior that is still being designed.
77	_Avoid_: Prompt-only permission, model trust profile, model trust config
78
79	**Deduplication**:
80	Surfacing a **Memory Candidate** that restates an existing **Memory** so it is not written twice. Authoritative judgement of "same fact" can require repository context, so v1 Deduplication is a best-effort, store-only flag decided by the human reviewer; autonomous, repo-grounded Deduplication is a responsibility of the future Memory Health agent.
81	_Avoid_: Merge, collapse, dedupe-on-write
82
83	**Memory Health**:
84	The degree to which a **Memory Store**'s **Memories** remain accurate and relevant relative to the current state of the repository. Assessing Memory Health requires investigating the codebase, not just reading the store, which is why it is the work of an autonomous agent rather than a mechanical check.
85	_Avoid_: Freshness, correctness, quality score
86
87	**Memory Staleness**:
88	The condition of a **Memory** whose body no longer reflects the current state of the repository. Memory Staleness shares its root meaning with Retrieval Map staleness (the **Retrieval Map** behind its **Topic Files**) and Hash staleness (a **Memory Block**'s `Hash:` behind its content) — in every case a recorded thing has fallen out of sync with its source of truth — but only Memory Staleness requires repository investigation to detect; the other two are mechanically detectable.
89	_Avoid_: Drift, rot, outdated
90
91	## Example dialogue
92
93	Dev: "Should the agent remember that this repo uses ADRs for hard-to-reverse decisions?"
94	Domain expert: "Yes, that belongs in the Project Store because it applies to this repository."
95
96	Dev: "Should the agent remember that I prefer concise answers?"
97	Domain expert: "Yes, that belongs in the Global Store because it applies across repositories."
98

# Agent Memory

Agent Memory gives pi agents durable knowledge that can survive beyond a single conversation and be reused in future work.

## Language

**Memory**:
A durable fact or preference the agent may retrieve and use in future turns. A **Memory** belongs to exactly one **Memory Store** and is created by explicit user instruction, user-confirmed agent proposal, or an automated agent with the required **Memory Capability**.
_Avoid_: Note, cache entry, recollection

**Memory Candidate**:
A possible **Memory** identified by the agent but not yet saved. A **Memory Candidate** becomes a **Memory** only after user confirmation or by an agent with the required **Memory Capability**.
_Avoid_: Auto-memory, inferred memory

**Memory Store**:
A named storage location containing **Memories**, **Topic Files**, and a **Retrieval Map**. A Memory Store is the boundary for retrieval, storage, and permissions.
_Avoid_: Scope, namespace, bucket

**Memory Capability**:
A future-facing permission granted to an agent for memory mutation operations. Memory Capability is not active in v1, where Structured Memory Writes happen only inside explicit user-invoked commands.
_Avoid_: Profile, role, trust level, read permission

**Forget**:
To remove a **Memory** from the current Markdown files in a **Memory Store** so it is no longer available for active retrieval. Forgetting does not rewrite git history.
_Avoid_: Archive, retire, hide

**Project Store**:
The built-in **Memory Store** for memories that apply to one repository or working directory. The Project Store lives at the fixed project path `.pi/memory/`.
_Avoid_: Project Scope, repo memory, local memory

**Global Store**:
A deferred **Memory Store** concept for memories that apply across repositories for the user. Global Store is not part of v1.
_Avoid_: Global Scope, user memory, universal memory

**Retrieval Map**:
A lightweight Markdown index that helps agents retrieve relevant **Memories** using text search. A Retrieval Map uses topic summaries, concepts, aliases, relationships, and paths; it does not use vectors or duplicate full memory content.
_Avoid_: Vector index, embedding store, full memory mirror

**Topic File**:
A Markdown file containing multiple related **Memories** for a concept area. Topic Files are the canonical storage unit for memories and are created lazily on first write.
_Avoid_: Per-memory file, memory blob

**Memory Block**:
A structured Markdown section inside a **Topic File** that represents one **Memory**. A Memory Block uses the stable memory id as its heading, followed by plain metadata lines, a canonical content hash, and body text; it does not have a separate title.
_Avoid_: YAML block, database row, titled note

**Canonical Content Hash**:
A SHA-256 version identifier for a **Memory Block** computed from canonicalized semantic fields, excluding `Updated` and excluding the hash field itself. Canonical Content Hashes let Structured Memory Writes detect stale update or forget operations without treating generated metadata churn as a conflict.
_Avoid_: Checksum, etag, raw markdown hash

**Memory Consultation**:
A built-in tool access to a **Memory Store** file that indicates the agent has read or searched memory. In v1, Memory Consultation is shown in the UI only and is not persisted as a custom session entry.
_Avoid_: Memory read tool, memory search tool

**Memory Snapshot**:
The exact set of **Memory Blocks** and version identifiers an agent has consulted before acting. A Memory Snapshot is the basis for detecting stale memory updates when other agents may have changed the same **Memory Store** concurrently.
_Avoid_: Loaded memory, memory in brain, memory context

**Structured Memory Write**:
A change to a **Memory Store** performed through a memory-specific tool that preserves **Memory Block** format and keeps the **Retrieval Map** current. Built-in file write tools are not valid Structured Memory Writes.
_Avoid_: Direct edit, manual memory patch

**Memory Tool Surface**:
The set of memory-specific tools available to agents. In v1, the normal Memory Tool Surface exposes `memory_stores` only; `/memory extract` temporarily exposes non-mutating `memory_candidate`. Retrieval uses built-in file tools against the Retrieval Map and Topic Files, and Structured Memory Writes are performed internally by the extension after user approval.
_Avoid_: Memory search API, memory read API, public memory write tool

**Memory Recall Prompt**:
A prompt template that asks an existing scout-style agent to retrieve relevant Memories from the Project Store. Memory Recall Prompt reuses general retrieval agents instead of defining a dedicated memory retrieval agent.
_Avoid_: Dedicated memory retriever agent, memory search tool

**Memory Extraction**:
An explicit user-invoked workflow that inspects the current session branch, proposes Memory Candidates, and writes approved candidates to the Project Store. Successful Memory Extraction writes a non-context checkpoint entry with the created memory ids.
_Avoid_: Background memory harvesting, silent memory write

**Memory Authority Source**:
The mechanism that grants Memory Capabilities to an agent invocation. In v1, installing the project-local extension does not grant mutation authority by default; mutation authority is tied to explicit automation behavior that is still being designed.
_Avoid_: Prompt-only permission, model trust profile, model trust config

**Deduplication**:
Surfacing a **Memory Candidate** that restates an existing **Memory** so it is not written twice. Authoritative judgement of "same fact" can require repository context, so v1 Deduplication is a best-effort, store-only flag decided by the human reviewer; autonomous, repo-grounded Deduplication is a responsibility of the future Memory Health agent.
_Avoid_: Merge, collapse, dedupe-on-write

**Memory Health**:
The degree to which a **Memory Store**'s **Memories** remain accurate and relevant relative to the current state of the repository. Assessing Memory Health requires investigating the codebase, not just reading the store, which is why it is the work of an autonomous agent rather than a mechanical check.
_Avoid_: Freshness, correctness, quality score

**Memory Staleness**:
The condition of a **Memory** whose body no longer reflects the current state of the repository. Memory Staleness shares its root meaning with Retrieval Map staleness (the **Retrieval Map** behind its **Topic Files**) and Hash staleness (a **Memory Block**'s `Hash:` behind its content) — in every case a recorded thing has fallen out of sync with its source of truth — but only Memory Staleness requires repository investigation to detect; the other two are mechanically detectable.
_Avoid_: Drift, rot, outdated

## Example dialogue

Dev: "Should the agent remember that this repo uses ADRs for hard-to-reverse decisions?"
Domain expert: "Yes, that belongs in the Project Store because it applies to this repository."

Dev: "Should the agent remember that I prefer concise answers?"
Domain expert: "Yes, that belongs in the Global Store because it applies across repositories."


cd "C:/Users/decid/Documents/projects/pi-agent-memory" && echo "=== adr ===" && ls docs/adr && echo "=== memory tree ===" && find .pi/memory -type f 2>/dev/null && echo "=== extensions ===" && find .pi/extensions -type f 2>/dev/null | head -20

List ADRs, memory tree, extension files


=== adr ===
0001-memory-health-curation.md
=== memory tree ===
.pi/memory/INDEX.md
.pi/memory/topics/memory-concurrency.md
.pi/memory/topics/temporal-context.md
=== extensions ===
.pi/extensions/agent-memory/index.ts
.pi/extensions/agent-memory/memory-core.ts
.pi/extensions/agent-memory/prompts/memory-recall.md
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Read it thoroughly. **Yes — several strong lessons, plus striking validation of where 17.3 already landed.** pi-agent-memory is a repo-owned, project-local agent memory extension: structured Memory Blocks in Topic Files, a generated Retrieval Map, a bounded awareness note, human-approved extraction.

**1. Validation — their model independently converges on ours.**
Their **Project Store vs Global Store** split *is* our project-context vs live-context split, and their domain dialogue is literally our cross-project discriminator: *"uses ADRs → Project Store (applies to this repo); prefers concise answers → Global Store (across repos)."* Reassuring that two independent designs landed on the same boundary. (They defer Global; SPT ships both day-one.)

**4 techniques worth adopting into SPT's context system:**

**A. Structured blocks → upgrade memformat from "topics" to "topics of blocks."** Their Memory Block = stable id + `Concepts:` + `Aliases:` + `Applies to:` (glob scope) + **`Source:`** + `Created/Updated` + `Hash:` + body. This enriches our memformat redesign hugely:
- `Source:` maps **exactly** to our commune-source asymmetry (Self-authored vs echo) — bake provenance into the block.
- `Concepts/Aliases` make context **retrievable**, not just readable.
- `Applies to:` gives **sub-project scoping** (path globs) — finer than project-level, useful for query-routing.
So memformat topics become tier-tagged *containers* of structured blocks. Strictly better than free-form prose.

**B. Generated Retrieval Map (INDEX.md) → concretely realizes our projects/-aggregation + query-routing.** A generated, lightweight index over topics (summaries + concepts + aliases + paths) — *not* a vector DB, *not* a full mirror. This is the missing concrete mechanism for "any agent synthesizes broader context from other agents in the same project" + "which endpoint owns this task." Generate an INDEX per project (and per agent), regenerated on context change. Adopt.

**C. Bounded awareness note → fix context-injection scaling.** Their sharpest idea: at agent start, inject *topic names + a pointer to INDEX, never inline bodies* — so context cost stays **bounded regardless of store size**, with deep retrieval on demand. Today SPT dumps full psyche-download. The bounded-pointer-then-retrieve pattern scales far better as an agent's context grows. Adopt for the awareness layer.

**D. Canonical Content Hash (OCC) → potentially upgrade our cross-node merge.** They hash each block over canonicalized semantic fields (excluding `Updated`/`Hash`) for stale-write detection. Our merge driver is whole-file-newest-wins (coarse — a stale write to *part* of a file can clobber a fresh write to another part). Per-**block** hashing would give **block-level, stale-aware merge** — finer-grained and safer. Worth considering as a v1.x refinement to the merge driver.

**One conceptual lesson (defer, but record):** their **Memory Health** insight — assessing whether context still matches *repo reality* requires **investigating the codebase**, so it's an autonomous-agent job, distinct from mechanical staleness (hash/index mismatch). This is the natural home for our deferred **rebalance pass** — extend it from "demote leaked live→project" to "verify context still true vs code." Mechanical freshness (newest-wins) stays separate.

**What to skip:** their heavy **human-in-the-loop approval** (extract→review→approve, max-5-candidates) and **bash-mutation guardrails** — those fit pi's "human curates" philosophy; SPT's psyche is *trusted to author its own mind* autonomously. We adopt their *structure*, not their *approval gate*. (Our drop-file-single-writer already covers controlled mutation.)

**Recommendation — fold A+B+C into the memformat/17.3 design now; hold D and Memory-Health for v1.x:**
- memformat topics = tier-tagged containers of **structured blocks** (id + Concepts/Aliases/Applies-to + **Source** + timestamps + hash).
- A generated **Retrieval Map/INDEX** per project + per agent (realizes aggregation/query-routing), regenerated on change.
- **Bounded awareness injection** (topics + pointer) + on-demand deep retrieval, replacing full-context dumps.

Which do you want me to fold into the design — all of A/B/C, and do you want D (block-level OCC merge) promoted to v1 or kept as v1.x? Then I'll write the memformat + 17.3 design (incorporating the confirmed deep integration + migration + these) into a doc and update CONTEXT.


A. agree B. agree C. agree, though in their case they're tailoring memory for projects where multiple agents may be working on different tasks within the same project. SPT focuses largely on the per-agent memory, and ensuring agents can work quickly without taking time to dig through memories. need to find a balance between sparse and dense start-injection. D. agree, defer. --- once we reconcile the scope of C, good to proceed with recommendations and record it all


**C reconciled — dense working set, sparse tail.** Your point is the key distinction: pi optimizes for *multi-agent-per-project digging* (so sparse-pointer makes sense — lots to sift); SPT optimizes for *per-agent, work-fast* (smaller volume, no digging). So the balance:

- **DENSE start-injection of the working set** — full **live context** (per-agent, bounded, always relevant) + the **current project's hot blocks** (current focus, recent decisions, active todos). The agent is immediately productive, zero digging. This is SPT's default because per-agent volume is small and speed is the priority.
- **SPARSE (pointer + INDEX, on-demand) for the long tail** — *other* projects, **cross-agent/cross-project synthesis** (exactly pi's multi-agent-dig case), and cold/archived blocks. Retrieved only when the task calls for it.
- **The hot/cold boundary is psyche-curated** — memformat marks blocks/topics **pinned (dense)** vs **indexed (sparse)**, plus recency. Keeping the hot set tight is part of the psyche maintaining memformat.
- **Tunable** via a working-set **density knob** (global + per-endpoint override, per our config-knob model) — lean vs loaded to taste.

Net: SPT is dense where pi is sparse (per-agent working set), and sparse where pi is sparse (cross-agent/cross-project pile). That directly serves "agents work quickly without digging" while staying bounded as context grows.

Recording all of it now — A/B/C(reconciled), memformat deep-integration + migration, the pi-credited patterns, D + memory-health deferred.


Write

# Psyche context & memory model

> Draft design doc (2026-05-31). Resolves PRD §17.3 (Psyche live/project authoring directives) and the memformat redesign. Governs how the Psyche authors, structures, syncs, and surfaces an agent's context. On-disk layout: `docs/STORAGE.md`. Several patterns are adapted from `BigscreenVR/pi-agent-memory` (credited inline).

## Two-tier context

- **live context** (per-agent, project-independent — the agent's *mind*): identity/persona, relationship + rapport with the user, the user's cross-project preferences & working style, cross-project life threads, **learnings from work that may apply to other projects**, a thin **project roster** (one line per project the agent works on), the agent's self-model. Synced to **all** instances of the endpoint.
- **project context** (per-agent-per-project — what the agent is doing *here*): codebase/architecture, decisions made here, **current focus / next steps / todos**, project conventions, build/test commands, blockers. Synced **only** to same-project instances.

(This split independently matches pi-agent-memory's Project Store vs Global Store and its discriminator dialogue — convergent validation.)

### The discriminator (applied per *topic*, not per fragment)

> **"Would this still be true and relevant if the user opened a *different* project tomorrow?"** Yes → live. No → project. Equivalently: **WHO the agent is** (live) vs **WHAT it's doing here** (project).

### Anti-leak rules

The observed failure is project detail leaking *into* live context (which then wrongly syncs everywhere). Countermeasures:

1. **Default-to-project.** A fragment goes to project context unless it *passes the cross-project test* to be promoted to live. Promotion needs justification; demotion is free. The bias points against the leak.
2. **`current focus / next steps` is project-scoped** — the single biggest leak category. Route it to project unless the focus genuinely spans projects.
3. **Live holds a thin project roster, never project depth** — cross-project *awareness* travels; depth stays per-project.
4. **Commune-source asymmetry** (the structural fix): the live signal is strong only from **Self-authored communes**; **echo communes are project-centric** (they synthesize project-heavy session logs). So **echo communes are project-context-primary and live-context-conservative** — they make few/no live edits. This kills the leak at its source. Provenance is recorded on each block (`Source:`).

### Ownership

The live/project authoring directives are **core-owned** — fed to every Psyche by spt-core (part of `$psyche_prompt` / a core system-prompt fragment), so the split is consistent across all adapters (inconsistent rules → sync chaos). Adapters add only harness mechanics (how to write the drop file).

## memformat (deeply integrated, self-evolving, the split scaffold)

memformat goes from a decorative, ignored seed to the **load-bearing schema that defines the two-tier context**. It makes the live/project decision **per-topic and rare** instead of per-fragment and constant — which is what makes the split stable.

- **Tier-tagged topics.** Each topic carries a tier: `live` or `project`. The tier is decided **once, when the topic is created** (apply the discriminator one time), not per fragment. Communes then just *fill* topics; the tier routes content to the right context file + sync tier. The two-slice envelope (`<live-context>`/`<project-context>`) becomes the *rendering* of tier-tagged topics.
- **Self-evolving.** The Psyche adds/removes/re-tiers topics as it learns Self's realm of work (memformat's original intent). **Re-tiering migrates** the topic's accumulated content between tiers (project→live or vice versa), not forward-only.
- **The schema is live** (per-agent, travels everywhere); **project-tagged topic content** is instantiated per-project.
- Example tiers: `identity / relationship / user-preferences / cross-project-learnings / project-roster` = **live**; `focus / decisions / architecture / todos / blockers` = **project**.

### Structured blocks (adapted from pi-agent-memory)

A memformat topic is a tier-tagged **container of structured blocks**, not free-form prose. Each block:

```
## <stable-block-id>
Concepts: <comma list>          # retrieval keys
Aliases: <comma list>           # retrieval synonyms (optional)
Applies to: <glob paths>        # sub-project scoping (optional)
Source: self-commune | echo-commune | user   # provenance (drives the live-conservative rule)
Created: <iso>
Updated: <iso>
Hash: sha256:<canonical-content-hash>         # for block-level OCC merge (deferred, see below)
Pinned: true|false              # hot (dense-injected) vs indexed (sparse, on-demand)

<body>
```

`Concepts`/`Aliases` make context **retrievable**; `Applies to` enables fine-grained scoping + query-routing; `Source` carries provenance; `Pinned` drives start-injection density (below).

## Retrieval Map (generated INDEX)

A generated, lightweight **INDEX** over topics — summaries + concepts + aliases + paths — **not** a vector DB, **not** a full mirror (adapted from pi-agent-memory's Retrieval Map). One INDEX per **project** (aggregating all agents' project context → realizes cross-agent synthesis + query-routing: "which endpoint owns this task") and per **agent**. Regenerated on context change; the merge driver + INDEX regeneration keep it current.

## Start-injection: dense working set, sparse tail

The balance between immediate productivity and bounded context cost — tuned for SPT's **per-agent, work-fast** priority (vs pi's multi-agent-per-project digging, which is sparse-by-default):

- **DENSE — the working set, injected in full at start:** all **live context** + the **current project's hot blocks** (`Pinned` + recently-updated focus/decisions/todos). The agent is immediately productive, no digging.
- **SPARSE — the long tail, pointer + INDEX, retrieved on demand:** other projects, **cross-agent/cross-project synthesis**, cold/archived blocks. (This is where pi's bounded "topic-names + pointer, never inline bodies" awareness pattern applies — it scales regardless of store size.)
- **Hot/cold curated by the Psyche** via `Pinned` + recency; keeping the hot set tight is part of maintaining memformat.
- **Tunable** via a working-set **density knob** (spt-core global setting + per-endpoint override).

## Deferred

- **Rebalance / Memory-Health pass** (later): the Psyche periodically (a) demotes leaked live→project content, and (b) — the deeper, pi-credited insight — verifies context is still *true vs the current code* (Memory Health), which requires investigating the codebase, so it's an autonomous-agent job, distinct from mechanical staleness (hash/INDEX mismatch).
- **Block-level OCC merge** (v1.x): the per-block `Hash:` (canonical content hash, excluding `Updated`/`Hash`) enables block-level, stale-aware cross-node merge — finer than the v1 whole-file newest-wins driver (`docs/STORAGE.md`). Recorded now (the block format carries `Hash:`), applied later.

## Not adopted

pi-agent-memory's **human-in-the-loop approval** (extract → review → approve, max-5-candidates) and **bash-mutation guardrails** fit its "human curates" philosophy. SPT's Psyche is **trusted to author its own mind autonomously** — so we adopt pi's *structure* (blocks, INDEX, bounded awareness, provenance, OCC) but **not** its approval gate. Controlled mutation is already covered by the drop-file single-writer rule (KNOWN-HAZARDS 6.4).


File created successfully at: C:\Users\decid\Documents\projects\spt-core\docs\CONTEXT-MEMORY.md (file state is current in your context — no need to Read it back)

# Psyche context & memory model

> Draft design doc (2026-05-31). Resolves PRD §17.3 (Psyche live/project authoring directives) and the memformat redesign. Governs how the Psyche authors, structures, syncs, and surfaces an agent's context. On-disk layout: `docs/STORAGE.md`. Several patterns are adapted from `BigscreenVR/pi-agent-memory` (credited inline).

## Two-tier context

- **live context** (per-agent, project-independent — the agent's *mind*): identity/persona, relationship + rapport with the user, the user's cross-project preferences & working style, cross-project life threads, **learnings from work that may apply to other projects**, a thin **project roster** (one line per project the agent works on), the agent's self-model. Synced to **all** instances of the endpoint.
- **project context** (per-agent-per-project — what the agent is doing *here*): codebase/architecture, decisions made here, **current focus / next steps / todos**, project conventions, build/test commands, blockers. Synced **only** to same-project instances.

(This split independently matches pi-agent-memory's Project Store vs Global Store and its discriminator dialogue — convergent validation.)

### The discriminator (applied per *topic*, not per fragment)

> **"Would this still be true and relevant if the user opened a *different* project tomorrow?"** Yes → live. No → project. Equivalently: **WHO the agent is** (live) vs **WHAT it's doing here** (project).

### Anti-leak rules

The observed failure is project detail leaking *into* live context (which then wrongly syncs everywhere). Countermeasures:

1. **Default-to-project.** A fragment goes to project context unless it *passes the cross-project test* to be promoted to live. Promotion needs justification; demotion is free. The bias points against the leak.
2. **`current focus / next steps` is project-scoped** — the single biggest leak category. Route it to project unless the focus genuinely spans projects.
3. **Live holds a thin project roster, never project depth** — cross-project *awareness* travels; depth stays per-project.
4. **Commune-source asymmetry** (the structural fix): the live signal is strong only from **Self-authored communes**; **echo communes are project-centric** (they synthesize project-heavy session logs). So **echo communes are project-context-primary and live-context-conservative** — they make few/no live edits. This kills the leak at its source. Provenance is recorded on each block (`Source:`).

### Ownership

The live/project authoring directives are **core-owned** — fed to every Psyche by spt-core (part of `$psyche_prompt` / a core system-prompt fragment), so the split is consistent across all adapters (inconsistent rules → sync chaos). Adapters add only harness mechanics (how to write the drop file).

## memformat (deeply integrated, self-evolving, the split scaffold)

memformat goes from a decorative, ignored seed to the **load-bearing schema that defines the two-tier context**. It makes the live/project decision **per-topic and rare** instead of per-fragment and constant — which is what makes the split stable.

- **Tier-tagged topics.** Each topic carries a tier: `live` or `project`. The tier is decided **once, when the topic is created** (apply the discriminator one time), not per fragment. Communes then just *fill* topics; the tier routes content to the right context file + sync tier. The two-slice envelope (`<live-context>`/`<project-context>`) becomes the *rendering* of tier-tagged topics.
- **Self-evolving.** The Psyche adds/removes/re-tiers topics as it learns Self's realm of work (memformat's original intent). **Re-tiering migrates** the topic's accumulated content between tiers (project→live or vice versa), not forward-only.
- **The schema is live** (per-agent, travels everywhere); **project-tagged topic content** is instantiated per-project.
- Example tiers: `identity / relationship / user-preferences / cross-project-learnings / project-roster` = **live**; `focus / decisions / architecture / todos / blockers` = **project**.

### Structured blocks (adapted from pi-agent-memory)

A memformat topic is a tier-tagged **container of structured blocks**, not free-form prose. Each block:

```
## <stable-block-id>
Concepts: <comma list>          # retrieval keys
Aliases: <comma list>           # retrieval synonyms (optional)
Applies to: <glob paths>        # sub-project scoping (optional)
Source: self-commune | echo-commune | user   # provenance (drives the live-conservative rule)
Created: <iso>
Updated: <iso>
Hash: sha256:<canonical-content-hash>         # for block-level OCC merge (deferred, see below)
Pinned: true|false              # hot (dense-injected) vs indexed (sparse, on-demand)

<body>
```

`Concepts`/`Aliases` make context **retrievable**; `Applies to` enables fine-grained scoping + query-routing; `Source` carries provenance; `Pinned` drives start-injection density (below).

## Retrieval Map (generated INDEX)

A generated, lightweight **INDEX** over topics — summaries + concepts + aliases + paths — **not** a vector DB, **not** a full mirror (adapted from pi-agent-memory's Retrieval Map). One INDEX per **project** (aggregating all agents' project context → realizes cross-agent synthesis + query-routing: "which endpoint owns this task") and per **agent**. Regenerated on context change; the merge driver + INDEX regeneration keep it current.

## Start-injection: dense working set, sparse tail

The balance between immediate productivity and bounded context cost — tuned for SPT's **per-agent, work-fast** priority (vs pi's multi-agent-per-project digging, which is sparse-by-default):

- **DENSE — the working set, injected in full at start:** all **live context** + the **current project's hot blocks** (`Pinned` + recently-updated focus/decisions/todos). The agent is immediately productive, no digging.
- **SPARSE — the long tail, pointer + INDEX, retrieved on demand:** other projects, **cross-agent/cross-project synthesis**, cold/archived blocks. (This is where pi's bounded "topic-names + pointer, never inline bodies" awareness pattern applies — it scales regardless of store size.)
- **Hot/cold curated by the Psyche** via `Pinned` + recency; keeping the hot set tight is part of maintaining memformat.
- **Tunable** via a working-set **density knob** (spt-core global setting + per-endpoint override).

## Deferred

- **Rebalance / Memory-Health pass** (later): the Psyche periodically (a) demotes leaked live→project content, and (b) — the deeper, pi-credited insight — verifies context is still *true vs the current code* (Memory Health), which requires investigating the codebase, so it's an autonomous-agent job, distinct from mechanical staleness (hash/INDEX mismatch).
- **Block-level OCC merge** (v1.x): the per-block `Hash:` (canonical content hash, excluding `Updated`/`Hash`) enables block-level, stale-aware cross-node merge — finer than the v1 whole-file newest-wins driver (`docs/STORAGE.md`). Recorded now (the block format carries `Hash:`), applied later.

## Not adopted

pi-agent-memory's **human-in-the-loop approval** (extract → review → approve, max-5-candidates) and **bash-mutation guardrails** fit its "human curates" philosophy. SPT's Psyche is **trusted to author its own mind autonomously** — so we adopt pi's *structure* (blocks, INDEX, bounded awareness, provenance, OCC) but **not** its approval gate. Controlled mutation is already covered by the drop-file single-writer rule (KNOWN-HAZARDS 6.4).


Edit

Caveat / dependency: the live-vs-project split exists conceptually today but the **authoring directives need refining** — the Psyche has been putting more project-specific detail into live context than into project context. The sync split only behaves well once those directives are sharpened so each tier holds the right material. The precedence/freshness guard (KNOWN-HAZARDS 6.5) — with node identity in the marker — keeps a dormant instance's stale context from clobbering the active authoritative one.

The precedence/freshness guard (KNOWN-HAZARDS 6.5) — with node identity in the marker — keeps a dormant instance's stale context from clobbering the active authoritative one.

**Authoring directives + memformat** (full design: `docs/CONTEXT-MEMORY.md`): the live/project split is made stable by (1) a per-*topic* cross-project discriminator with default-to-project bias, (2) **commune-source asymmetry** — echo communes are project-primary and live-conservative (the leak fix), and (3) **deep memformat integration** — memformat becomes the load-bearing, self-evolving, **tier-tagged** schema (each topic tagged `live`/`project`, decided once at creation, re-tiering migrates content), whose topics are containers of **structured blocks** (id + Concepts/Aliases/Applies-to + Source + timestamps + Hash + Pinned). A generated **Retrieval Map (INDEX)** per project + per agent realizes cross-agent synthesis + query-routing. **Start-injection** is dense on the working set (live + current-project hot/`Pinned` blocks → immediate productivity) and sparse on the tail (other projects, cross-agent synthesis, cold blocks → pointer + INDEX, on-demand), tunable via a density knob. Several patterns adapted from `BigscreenVR/pi-agent-memory`.


The file C:\Users\decid\Documents\projects\spt-core\CONTEXT.md has been updated successfully. (file state is current in your context — no need to Read it back)

The precedence/freshness guard (KNOWN-HAZARDS 6.5) — with node identity in the marker — keeps a dormant instance's stale context from clobbering the active authoritative one.

**Authoring directives + memformat** (full design: `docs/CONTEXT-MEMORY.md`): the live/project split is made stable by (1) a per-*topic* cross-project discriminator with default-to-project bias, (2) **commune-source asymmetry** — echo communes are project-primary and live-conservative (the leak fix), and (3) **deep memformat integration** — memformat becomes the load-bearing, self-evolving, **tier-tagged** schema (each topic tagged `live`/`project`, decided once at creation, re-tiering migrates content), whose topics are containers of **structured blocks** (id + Concepts/Aliases/Applies-to + Source + timestamps + Hash + Pinned). A generated **Retrieval Map (INDEX)** per project + per agent realizes cross-agent synthesis + query-routing. **Start-injection** is dense on the working set (live + current-project hot/`Pinned` blocks → immediate productivity) and sparse on the tail (other projects, cross-agent synthesis, cold blocks → pointer + INDEX, on-demand), tunable via a density knob. Several patterns adapted from `BigscreenVR/pi-agent-memory`.

So all `ling` instances catch up on live context; only same-project `ling` instances catch up on project context. This resolves the "divergent simultaneous work" concern (two instances on different projects don't clobber each other's project detail) without losing the "mind follows everywhere" property.

-Caveat / dependency: the live-vs-project split exists conceptually today but the **authoring directives need refining** — the Psyche has been putting more project-specific detail into live context than into project context. The sync split only behaves well once those directives are sharpened so each tier holds the right material. The precedence/freshness guard (KNOWN-HAZARDS 6.5) — with node identity in the marker — keeps a dormant instance's stale context from clobbering the active authoritative one.

+The precedence/freshness guard (KNOWN-HAZARDS 6.5) — with node identity in the marker — keeps a dormant instance's stale context from clobbering the active authoritative one.

+**Authoring directives + memformat** (full design: `docs/CONTEXT-MEMORY.md`): the live/project split is made stable by (1) a per-*topic* cross-project discriminator with default-to-project bias, (2) **commune-source asymmetry** — echo communes are project-primary and live-conservative (the leak fix), and (3) **deep memformat integration** — memformat becomes the load-bearing, self-evolving, **tier-tagged** schema (each topic tagged `live`/`project`, decided once at creation, re-tiering migrates content), whose topics are containers of **structured blocks** (id + Concepts/Aliases/Applies-to + Source + timestamps + Hash + Pinned). A generated **Retrieval Map (INDEX)** per project + per agent realizes cross-agent synthesis + query-routing. **Start-injection** is dense on the working set (live + current-project hot/`Pinned` blocks → immediate productivity) and sparse on the tail (other projects, cross-agent synthesis, cold blocks → pointer + INDEX, on-demand), tunable via a density knob. Several patterns adapted from `BigscreenVR/pi-agent-memory`.

When the user is driving an instance from off-node, the agent should be able to:


- Refining Psyche authoring directives so the live/project context split holds cleanly (R-INST-5).

- ~~Refining Psyche authoring directives so the live/project context split holds cleanly~~ — **resolved**, see [`docs/CONTEXT-MEMORY.md`](./docs/CONTEXT-MEMORY.md) (per-topic discriminator + default-to-project; commune-source asymmetry; deep tier-tagged memformat with structured blocks; generated Retrieval Map; dense-working-set / sparse-tail injection; patterns from pi-agent-memory).


The file C:\Users\decid\Documents\projects\spt-core\PRD.md has been updated successfully. (file state is current in your context — no need to Read it back)

- ~~Exact `$SPT_HOME` on-disk layout~~ — **resolved**, see [`docs/STORAGE.md`](./docs/STORAGE.md) (single relocatable root; adapter-agnostic ID keying; separate `tracked/` git repo vs node-local `perches/`; one path resolver).

- ~~Manifest schema concretes~~ — **resolved**, see [`docs/MANIFEST.md`](./docs/MANIFEST.md) (opaque command templates; per-hook `can_inject`; node-wide cross-adapter fallback; global+override config knobs; worked `claude-spt` example). Capability-declaration shape beyond `hostable_types` remains open.

-- Refining Psyche authoring directives so the live/project context split holds cleanly (R-INST-5).

+- ~~Refining Psyche authoring directives so the live/project context split holds cleanly~~ — **resolved**, see [`docs/CONTEXT-MEMORY.md`](./docs/CONTEXT-MEMORY.md) (per-topic discriminator + default-to-project; commune-source asymmetry; deep tier-tagged memformat with structured blocks; generated Retrieval Map; dense-working-set / sparse-tail injection; patterns from pi-agent-memory).

- Warm-vs-cold dormancy default policy details and the suspend-from-anywhere command surface.

- Consent/security-gate UX (shared by instantiate-anywhere + remote-exec) when those land.

- ~~Remote-control: formal Shell or bare session-surface?~~ — **resolved**: opposite directions. A **Shell is agent→surface** (agent drives embodiment, receives sensory); **remote-control is user→agent** (a viewport onto a running instance). Remote-control is the bare network session-surface, *not* a Shell. (Shell model now fully specified — CONTEXT §Shell model, `docs/MANIFEST.md` `kind="shell"`, `docs/STORAGE.md`.)


- [`docs/DOCS-STRATEGY.md`](./docs/DOCS-STRATEGY.md) — dual-audience (human + AI-agent) documentation strategy.

- [`docs/DOCS-STRATEGY.md`](./docs/DOCS-STRATEGY.md) — dual-audience (human + AI-agent) documentation strategy.
- [`docs/CONTEXT-MEMORY.md`](./docs/CONTEXT-MEMORY.md) — Psyche context & memory model (live/project, memformat, retrieval, injection).


- [`docs/DOCS-STRATEGY.md`](./docs/DOCS-STRATEGY.md) — dual-audience (human + AI-agent) documentation strategy.
- [`docs/CONTEXT-MEMORY.md`](./docs/CONTEXT-MEMORY.md) — Psyche context & memory model (live/project, memformat, retrieval, injection).

- [`docs/STORAGE.md`](./docs/STORAGE.md) — `$SPT_HOME` on-disk layout.

- [`docs/MANIFEST.md`](./docs/MANIFEST.md) — runtime manifest schema + worked `claude-spt` example.

+- [`docs/CONTEXT-MEMORY.md`](./docs/CONTEXT-MEMORY.md) — Psyche context & memory model (live/project, memformat, retrieval, injection).

- Sister project networking research: `claude_skill_owl/docs/research/SPT Networked Messaging Research Brief.md`.


Bash

cd "C:/Users/decid/Documents/projects/spt-core" && git add -A && git status --short && git commit -q -F - <<'EOF'
docs: psyche context & memory model (17.3 + memformat redesign)

CONTEXT-MEMORY.md — resolves the live/project authoring-directive problem
and deeply integrates memformat.

- Per-topic cross-project discriminator + default-to-project bias; current
  focus/next-steps routed to project (the main leak)
- Commune-source asymmetry: echo communes project-primary, live-conservative
  (the structural leak fix); provenance recorded per block (Source:)
- memformat as load-bearing, self-evolving, tier-tagged schema; tier decided
  once at topic creation; re-tiering migrates content; topics contain
  structured blocks (id/Concepts/Aliases/Applies-to/Source/timestamps/Hash/
  Pinned) — adapted from BigscreenVR/pi-agent-memory
- Generated Retrieval Map (INDEX) per project + per agent (cross-agent
  synthesis + query-routing)
- Start-injection: dense working set (live + current-project hot) / sparse
  tail (other projects, cross-agent synthesis, cold) + density knob
- Deferred: rebalance/Memory-Health pass; block-level OCC merge (Hash carried
  now, applied v1.x). Not adopted: pi's human-approval gate (psyche is
  autonomous)
- Core-owned directives for cross-adapter consistency

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EOF
git push -q origin main && echo pushed && git log --oneline -1

Commit and push context-memory model


warning: in the working copy of 'CONTEXT.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'PRD.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/CONTEXT-MEMORY.md', LF will be replaced by CRLF the next time Git touches it
M  CONTEXT.md
M  PRD.md
A  docs/CONTEXT-MEMORY.md
pushed
a71c145 docs: psyche context & memory model (17.3 + memformat redesign)


cd "C:/Users/decid/Documents/projects/spt-core" && git log --oneline -1 && git status --short && git rev-parse --abbrev-ref HEAD && git rev-parse HEAD@{upstream} 2>/dev/null && echo "ahead/behind:" && git rev-list --left-right --count origin/main...HEAD 2>/dev/null

Verify commit and push state


a71c145 docs: psyche context & memory model (17.3 + memformat redesign)
main
a71c14559c221b0841f53d6180d410e119e6c2c9
ahead/behind:
0	0


17.3 recorded, committed (`a71c145`), pushed — working tree clean, `origin/main` synced (0 ahead/behind).

**17.3 resolved** → `docs/CONTEXT-MEMORY.md`. The leak fix is structural (commune-source asymmetry + per-topic tier decisions via memformat, not per-fragment judgment), and pi-agent-memory contributed real upgrades: structured blocks with provenance/retrieval metadata, a generated Retrieval Map, bounded awareness — reconciled to SPT's per-agent-work-fast priority as **dense working set / sparse tail**. Block-level OCC merge + Memory-Health pass recorded as deferred.

**Where things stand — PRD §17:**
- ✓ 17.1 storage layout · ✓ 17.2 manifest schema · ✓ 17.3 psyche context/memory · ✓ 17.6 remote-control-vs-Shell
- **Open: 17.4** (dormancy default policy + suspend command surface) · **17.5** (consent/security-gate UX — now also touched by shell `can_shutdown` + `shell_wake_spawn_anywhere`)

Repo: 7 commits on `SaberMage/spt-core` (private). Want to take 17.4 or 17.5 next?


A. agree B. agree C. agree, though in their case they're tailoring memory for projects where multiple agents may be working on different tasks within the same project. SPT focuses largely on the per-a…


17.4 next


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


**17.4 — dormancy default policy + suspend surface.** We locked the *states* (active / dormant-warm / suspended-cold / offline) and that warm is the default. What's open: the **transition triggers/thresholds** and the **command surface**. Here's the model I'd propose.

**State transitions:**
- **active** — a driver is attached (a frontend/PTY attached, or the harness session is foreground) *or* there's been interaction within a short **idle-grace window**.
- **active → dormant** — the driver detaches, attention shifts to another instance of the same ID, or the idle-grace window passes with no interaction. Session stays **warm** (process alive, instant re-activation).
- **dormant → suspended** — manual (`spt suspend` / `spt shutdown` / shell `api owner-shutdown`), *or* auto-suspend after a longer **auto-suspend threshold** (opt-in only).
- **wake (→ active)** — `spt wake`, a driver attaches, or a shell wake-watcher fires.
- **offline** — node unreachable (orthogonal; not a chosen transition).

**Default policy (confirming + extending "default warm"):**
- **Warm is the default** — instances go dormant (not suspended) when undriven; resources stay warm for instant switch. ✓ (locked)
- **Auto-suspend is opt-in, default OFF globally** — but **node-overridable** (a handheld / Pi / resource-constrained node sensibly defaults it ON) and **per-endpoint overridable**. This matters for *billable* harnesses too — a warm dormant `claude` session may hold a cost footprint, so those endpoints may want auto-suspend on.
- **Thresholds are config knobs** (global default → node override → endpoint override, per our config-knob model): `idle-grace` (active→dormant) and `auto-suspend-after` (dormant→suspended).

**Command surface** — all user/agent-initiated lifecycle ops, **unprefixed** (not `api`):
- `spt suspend <id[@node]>` — suspend any of your instances, from any node (rides registry + messaging cross-node).
- `spt wake <id[@node]>` — explicitly wake a dormant/suspended instance.
- `spt shutdown` — an agent suspends *its own* endpoint (already locked: graceful, fires transition echo-commune, cascades shells offline).
- (machinery, already locked) shell `api owner-shutdown` — a shell suspends its owner.

Three decisions to confirm:
1. **Idle-grace for active→dormant** — do you want a soft idle timer (active→dormant after no interaction even while a session is technically open), or **purely attachment-based** (dormant only when the driver actually detaches, no timer)? I lean **attachment-primary + a soft idle-grace** so harness-hosted sessions (terminal left open but unattended) eventually go dormant rather than counting as forever-active. Your call.
2. **Auto-suspend default = OFF global, node + endpoint override** (handhelds/billable can flip it on) — agree?
3. **Command names** `spt suspend` / `spt wake` / `spt shutdown` (+ `id@node` targeting) — good, or different verbs?


-active-->dormant "idle-grace window passes with no interaction"--nix this. session should be considered active so long as it was the most recently-interacted instance for that ID + has a driver attached. -the opt-in auto-suspend threshold would only start counting from the moment a session goes dormant -confirmations: 1. answered above 2. agree 3. verbs are good. --- tangential note: i want to change echo communes for specific boundaries: `/clear`, `/compress`, and `spt suspend`. all 3 should `--resume` the Self session, so we get meaningful updates to live-context on a consistent basis. the first two should do commune file-drop. and suspend should do signoff file-drop. --- one other thing before i forget: agents behind an spt-hosted endpoint should get a working `spt refresh` lever. it 1. triggers a `/clear`, 2. triggers an echo commune, and 3. guarantees that a resume signal will hit their harness post-clear so their turn starts and they can resume their work, starting with immediate next-steps.


-active-->dormant "idle-grace window passes with no interaction"--nix this. session should be considered active so long as it was the most recently-interacted instance for that ID + has a driver attac…


one other note: shell attached should be considered equivalent to driver attached, as far as dormancy goes


Dormancy refinements locked. Capturing — plus the boundary-commune change and `spt refresh`, which are meaty additions.


Edit

Resting is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit stop is needed to switch. A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).

**State transitions (no idle timer):**
- **active** = the instance is the **most-recently-interacted instance for that ID** *and* **has a driver attached**. Both conditions. There is **no idle-grace timer** — a session stays active as long as it holds those two properties.
- **active → dormant** — the driver detaches, *or* another instance of the same ID becomes the most-recently-interacted one (attention shifts). No explicit stop needed to switch; driving `ling@laptop` makes `ling@desktop` dormant.
- **dormant → suspended** — manual (`spt suspend` / `spt shutdown` / shell `api owner-shutdown`), *or* opt-in auto-suspend after an `auto-suspend-after` threshold **counted from the moment the instance went dormant**.
- **wake (→ active)** — `spt wake`, a driver attaches, or a shell wake-watcher fires.

A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).

**Default policy:** warm (dormant) is the default when undriven. **Auto-suspend is opt-in, default OFF globally**, but **node-overridable** (a handheld / Pi / resource-constrained node may default it ON) and **per-endpoint overridable** (e.g. a billable harness whose warm session holds a cost footprint). Thresholds (`auto-suspend-after`) are config knobs: global default → node override → endpoint override.

**Commands** (user/agent-initiated lifecycle, unprefixed): `spt suspend <id[@node]>` (suspend any of your instances from any node), `spt wake <id[@node]>` (explicitly wake a dormant/suspended instance), `spt shutdown` (an agent suspends its *own* endpoint — graceful, fires the suspend boundary commune, cascades shells offline).


**State transitions (no idle timer):**
- **active** = the instance is the **most-recently-interacted instance for that ID** *and* **has a driver attached**. Both conditions. There is **no idle-grace timer** — a session stays active as long as it holds those two properties.
- **active → dormant** — the driver detaches, *or* another instance of the same ID becomes the most-recently-interacted one (attention shifts). No explicit stop needed to switch; driving `ling@laptop` makes `ling@desktop` dormant.
- **dormant → suspended** — manual (`spt suspend` / `spt shutdown` / shell `api owner-shutdown`), *or* opt-in auto-suspend after an `auto-suspend-after` threshold **counted from the moment the instance went dormant**.
- **wake (→ active)** — `spt wake`, a driver attaches, or a shell wake-watcher fires.

A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).

**Default policy:** warm (dormant) is the default when undriven. **Auto-suspend is opt-in, default OFF globally**, but **node-overridable** (a handheld / Pi / resource-constrained node may default it ON) and **per-endpoint overridable** (e.g. a billable harness whose warm session holds a cost footprint). Thresholds (`auto-suspend-after`) are config knobs: global default → node override → endpoint override.

**Commands** (user/agent-initiated lifecycle, unprefixed): `spt suspend <id[@node]>` (suspend any of your instances from any node), `spt wake <id[@node]>` (explicitly wake a dormant/suspended instance), `spt shutdown` (an agent suspends its *own* endpoint — graceful, fires the suspend boundary commune, cascades shells offline).

- **dormant** (default, *warm*): a live, state-preserved seat whose harness session stays running → instant re-activation. The default resting state.

- **suspended** (opt-in, *cold*): the harness session is closed and resumed-on-wake → frees RAM/compute (and, for billable harnesses, footprint), at the cost of a resume on wake. Reached via per-endpoint opt-in *auto-suspend*, or on demand: a subcommand can **suspend an endpoint from anywhere** (any node).

-Resting is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit stop is needed to switch. A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).

+- **active** = the instance is the **most-recently-interacted instance for that ID** *and* **has a driver attached**. Both conditions. There is **no idle-grace timer** — a session stays active as long as it holds those two properties.

+- **active → dormant** — the driver detaches, *or* another instance of the same ID becomes the most-recently-interacted one (attention shifts). No explicit stop needed to switch; driving `ling@laptop` makes `ling@desktop` dormant.

+- **dormant → suspended** — manual (`spt suspend` / `spt shutdown` / shell `api owner-shutdown`), *or* opt-in auto-suspend after an `auto-suspend-after` threshold **counted from the moment the instance went dormant**.

+- **wake (→ active)** — `spt wake`, a driver attaches, or a shell wake-watcher fires.

+A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).

+**Default policy:** warm (dormant) is the default when undriven. **Auto-suspend is opt-in, default OFF globally**, but **node-overridable** (a handheld / Pi / resource-constrained node may default it ON) and **per-endpoint overridable** (e.g. a billable harness whose warm session holds a cost footprint). Thresholds (`auto-suspend-after`) are config knobs: global default → node override → endpoint override.

+**Commands** (user/agent-initiated lifecycle, unprefixed): `spt suspend <id[@node]>` (suspend any of your instances from any node), `spt wake <id[@node]>` (explicitly wake a dormant/suspended instance), `spt shutdown` (an agent suspends its *own* endpoint — graceful, fires the suspend boundary commune, cascades shells offline).

**transition echo commune:** any active → (dormant | suspended) transition fires an **echo commune** capturing the outgoing instance's final context delta; that commune syncs to whichever instance becomes active next. This is the finer mechanism behind catch-up-on-activation (and mirrors the sister project's "echo commune before signoff" pattern — KNOWN-HAZARDS 3.3).

**deferred-message gate:** deferred (spool-only, hook-consumer) messages are **not** delivered to a dormant or suspended instance — they hold until it is active again. (Extends the deferred-row semantics, KNOWN-HAZARDS 1.4/4.4, with an instance-state gate.)


Prompt: one other note: shell attached should be considered equivalent to driver attached, as far as dormancy goes


Good — folding that in: a linked shell counts as a driver for dormancy.