You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/MEMORY.md
+54-2Lines changed: 54 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -51,7 +51,7 @@ Enum selecting which file to write to:
51
51
52
52
### `WriteMode`
53
53
54
-
-`Append` — append content, inserting a `\n` separator if the file does not end with one
54
+
-`Append` — append content, inserting a `\n` separator if the file does not end with one. For `long_term` only, appended lines are deduplicated (see [Long-term append deduplication](#long-term-append-deduplication))
55
55
-`Overwrite` — replace the entire file
56
56
57
57
### `Mem`
@@ -120,7 +120,7 @@ Rules:
120
120
121
121
## Rig Tools
122
122
123
-
Three tools are registered when the `memory` feature is enabled:
123
+
Four tools are registered when the `memory` feature is enabled:
124
124
125
125
### `memory_write`
126
126
@@ -140,6 +140,38 @@ Three tools are registered when the `memory` feature is enabled:
140
140
141
141
`source=list` enumerates all `.md` files in the store (global MEMORY.md + current project's notes + daily logs).
142
142
143
+
### `memory_edit`
144
+
145
+
| Parameter | Type | Description |
146
+
|---|---|---|
147
+
|`target`| string |`long_term`, `scratchpad`, `daily`, or `note`|
148
+
|`name`| string (opt) | Note stem (required for `note`); or a `YYYY-MM-DD` date for `daily` to edit an earlier day (defaults to today) |
149
+
|`old_str`| string (opt) | Substring to replace; must occur exactly once. Omit to delete a whole note |
Replaces a unique substring in a memory file in place. `old_str` is matched literally (no fuzzy matching) and must occur exactly once in the target file; zero or multiple matches fail without writing. `new_str` replaces the match verbatim with no newline cleanup, so an empty `new_str` deletes exactly the matched text, and including the trailing newline in `old_str` deletes a whole line.
153
+
154
+
Omitting `old_str` deletes an entire note file (`notes/<name>.md`) from disk; this requires `target=note` with a `name`. Omitting `old_str` for `long_term`, `scratchpad`, or `daily` is rejected and changes nothing. Deleting a note that does not exist is an error.
155
+
156
+
---
157
+
158
+
## Backups
159
+
160
+
Content-destroying mutations first copy the current file to a sibling `.bak` (single version, `MEMORY.md` becomes `MEMORY.bak`), so the pre-mutation content stays recoverable. There is exactly one `.bak` per file: each qualifying mutation overwrites the previous `.bak` rather than keeping a history. The `.bak` extension keeps these files out of `memory_read source=list` and `memory_search`, which both filter to `.md` only, so backups never leak into the model's context.
161
+
162
+
A backup is taken only before these operations (and only when the target file already exists: a first-ever overwrite of a not-yet-created file has nothing to back up and skips silently):
Appends are non-destructive by construction, so they never back up. `daily` and `note` content edits are targeted unique-match replacements (low-risk and already reversible via a re-edit), so they are deliberately left un-backed-up to avoid churn.
172
+
173
+
If the backup copy itself fails (for example the `.bak` path is not writable), the mutation still proceeds (the primary operation is what was asked for), but the tool response is suffixed with a `warning: backup failed, no .bak written` note so the caller knows there is no undo for that change. The failure is also logged.
174
+
143
175
### `memory_search`
144
176
145
177
| Parameter | Type | Description |
@@ -148,6 +180,26 @@ Three tools are registered when the `memory` feature is enabled:
148
180
149
181
---
150
182
183
+
## Long-term append deduplication
184
+
185
+
`MEMORY.md` is curated one fact per line, so `memory_write target=long_term mode=append` deduplicates its lines. This applies to `long_term` appends **only**: `scratchpad`, `daily`, and `note` appends are never deduplicated (repeats are preserved), and no target dedups on `overwrite`.
186
+
187
+
Comparison is whitespace-insensitive: each line is normalized by trimming and collapsing every run of Unicode whitespace (ASCII spaces/tabs and the full-width `U+3000` space) to a single ASCII space, preserving case. Two lines that differ only in whitespace width are duplicates.
188
+
189
+
For a `long_term` append batch (the incoming content split on `\n`):
190
+
191
+
1. Batch-internal duplicates are dropped, keeping the first occurrence.
192
+
2. Lines whose normalized form already exists anywhere in `MEMORY.md` are dropped.
193
+
3. Blank / whitespace-only lines normalize to empty; they are never a dedup key (they carry no fact) and are kept verbatim.
194
+
4. If nothing meaningful survives, the write is skipped entirely and the file is left byte-for-byte unchanged.
195
+
196
+
The response message reflects the outcome:
197
+
- No duplicates: `Wrote N bytes to <path>` (unchanged).
198
+
- Partial dedup: `Wrote N bytes to <path> (skipped M duplicate line(s))`.
199
+
- Whole batch dropped: `Nothing written to <path>: all M line(s) were duplicates`.
200
+
201
+
---
202
+
151
203
## Search Algorithm
152
204
153
205
`Mem::search(query)` implements a case-insensitive, multi-term keyword search:
0 commit comments