@ddtcorex/dsh-maestro-memory 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +21 -0
  3. package/README.md +448 -0
  4. package/cordis.patch.yml +13 -0
  5. package/lib/client.js +756 -0
  6. package/lib/index.d.ts +10 -0
  7. package/lib/index.d.ts.map +1 -0
  8. package/lib/index.js +627 -0
  9. package/lib/index.js.map +1 -0
  10. package/lib/memory/batch.d.ts +44 -0
  11. package/lib/memory/batch.d.ts.map +1 -0
  12. package/lib/memory/batch.js +75 -0
  13. package/lib/memory/batch.js.map +1 -0
  14. package/lib/memory/feedback.d.ts +18 -0
  15. package/lib/memory/feedback.d.ts.map +1 -0
  16. package/lib/memory/feedback.js +29 -0
  17. package/lib/memory/feedback.js.map +1 -0
  18. package/lib/memory/store.d.ts +154 -0
  19. package/lib/memory/store.d.ts.map +1 -0
  20. package/lib/memory/store.js +606 -0
  21. package/lib/memory/store.js.map +1 -0
  22. package/lib/migration/cli.d.ts +18 -0
  23. package/lib/migration/cli.d.ts.map +1 -0
  24. package/lib/migration/cli.js +143 -0
  25. package/lib/migration/cli.js.map +1 -0
  26. package/lib/migration/fixture.d.ts +45 -0
  27. package/lib/migration/fixture.d.ts.map +1 -0
  28. package/lib/migration/fixture.js +177 -0
  29. package/lib/migration/fixture.js.map +1 -0
  30. package/lib/migration/service.d.ts +75 -0
  31. package/lib/migration/service.d.ts.map +1 -0
  32. package/lib/migration/service.js +713 -0
  33. package/lib/migration/service.js.map +1 -0
  34. package/lib/prompt/snapshot.d.ts +26 -0
  35. package/lib/prompt/snapshot.d.ts.map +1 -0
  36. package/lib/prompt/snapshot.js +74 -0
  37. package/lib/prompt/snapshot.js.map +1 -0
  38. package/lib/review/queue.d.ts +72 -0
  39. package/lib/review/queue.d.ts.map +1 -0
  40. package/lib/review/queue.js +306 -0
  41. package/lib/review/queue.js.map +1 -0
  42. package/lib/skills-browser.d.ts +67 -0
  43. package/lib/skills-browser.d.ts.map +1 -0
  44. package/lib/skills-browser.js +185 -0
  45. package/lib/skills-browser.js.map +1 -0
  46. package/lib/storage/atomic-store.d.ts +211 -0
  47. package/lib/storage/atomic-store.d.ts.map +1 -0
  48. package/lib/storage/atomic-store.js +582 -0
  49. package/lib/storage/atomic-store.js.map +1 -0
  50. package/lib/storage/layout.d.ts +81 -0
  51. package/lib/storage/layout.d.ts.map +1 -0
  52. package/lib/storage/layout.js +162 -0
  53. package/lib/storage/layout.js.map +1 -0
  54. package/lib/storage/legacy-format.d.ts +104 -0
  55. package/lib/storage/legacy-format.d.ts.map +1 -0
  56. package/lib/storage/legacy-format.js +262 -0
  57. package/lib/storage/legacy-format.js.map +1 -0
  58. package/lib/sync/config.d.ts +22 -0
  59. package/lib/sync/config.d.ts.map +1 -0
  60. package/lib/sync/config.js +57 -0
  61. package/lib/sync/config.js.map +1 -0
  62. package/lib/sync/git.d.ts +97 -0
  63. package/lib/sync/git.d.ts.map +1 -0
  64. package/lib/sync/git.js +282 -0
  65. package/lib/sync/git.js.map +1 -0
  66. package/lib/sync/index.d.ts +6 -0
  67. package/lib/sync/index.d.ts.map +1 -0
  68. package/lib/sync/index.js +6 -0
  69. package/lib/sync/index.js.map +1 -0
  70. package/lib/sync/layout.d.ts +15 -0
  71. package/lib/sync/layout.d.ts.map +1 -0
  72. package/lib/sync/layout.js +33 -0
  73. package/lib/sync/layout.js.map +1 -0
  74. package/lib/sync/merge.d.ts +42 -0
  75. package/lib/sync/merge.d.ts.map +1 -0
  76. package/lib/sync/merge.js +159 -0
  77. package/lib/sync/merge.js.map +1 -0
  78. package/lib/sync/service.d.ts +70 -0
  79. package/lib/sync/service.d.ts.map +1 -0
  80. package/lib/sync/service.js +508 -0
  81. package/lib/sync/service.js.map +1 -0
  82. package/lib/todo/store.d.ts +142 -0
  83. package/lib/todo/store.d.ts.map +1 -0
  84. package/lib/todo/store.js +452 -0
  85. package/lib/todo/store.js.map +1 -0
  86. package/lib/types/client/index.d.ts +7 -0
  87. package/lib/types/client/index.d.ts.map +1 -0
  88. package/package.json +63 -0
  89. package/src/client/index.tsx +994 -0
  90. package/src/host/index.ts +591 -0
  91. package/src/host/memory/batch.ts +107 -0
  92. package/src/host/memory/feedback.ts +36 -0
  93. package/src/host/memory/store.ts +650 -0
  94. package/src/host/migration/cli.ts +139 -0
  95. package/src/host/migration/fixture.ts +218 -0
  96. package/src/host/migration/service.ts +786 -0
  97. package/src/host/prompt/snapshot.ts +95 -0
  98. package/src/host/review/queue.ts +325 -0
  99. package/src/host/skills-browser.ts +191 -0
  100. package/src/host/storage/atomic-store.ts +605 -0
  101. package/src/host/storage/layout.ts +191 -0
  102. package/src/host/storage/legacy-format.ts +308 -0
  103. package/src/host/sync/config.ts +67 -0
  104. package/src/host/sync/git.ts +280 -0
  105. package/src/host/sync/index.ts +5 -0
  106. package/src/host/sync/layout.ts +39 -0
  107. package/src/host/sync/merge.ts +188 -0
  108. package/src/host/sync/service.ts +494 -0
  109. package/src/host/todo/store.ts +480 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,96 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project uses
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.0.1] - 2026-08-25
8
+
9
+ Fix a live gap in the `memory` tool: the `daily` track only honored an explicit
10
+ `date` for `add`/`list`, so an entry on an older day could never be removed or
11
+ edited through the tool (it always targeted today's file). Verified on a real
12
+ session after restart.
13
+
14
+ ### Fixed
15
+
16
+ - **`date` now applies to `remove`/`replace` on the `daily` track.** The store
17
+ methods accept an optional `{ date }` and thread it through
18
+ `fileFor(target, cwd, date)`, and the host tool `execute` + `memory.mutate`
19
+ RPC pass it through. A non-`YYYY-MM-DD` value returns an error via the
20
+ existing `dailyPath` → `assertDate` guard.
21
+ - Updated the `date` schema description to cover add/list/replace/remove.
22
+
23
+ ### Notes
24
+
25
+ - TDD regression tests added in `tests/memory-m2.spec.ts`; full suite 202 pass,
26
+ `pnpm run build` / `pnpm run verify` green.
27
+
28
+ ## [1.0.0] - 2026-08-24
29
+
30
+ Initial release of `@ddtcorex/dsh-maestro-memory`, a from-scratch TypeScript
31
+ rebrand of `dsh-memory-evolve` that runs as a DeepSeek Harness plugin and
32
+ preserves the existing `~/.dsh/memories` files in place.
33
+
34
+ ### Added
35
+
36
+ - **Memory tool (`memory`)** across five durable tracks: global `memory`,
37
+ `user`, per-project `key` / `project`, and date-stamped `daily`. Entries are
38
+ stored verbatim in the same `§`-delimited layout as the legacy files; archived
39
+ entries split to `*-archive.md`.
40
+ - **Todos tool (`dtodo`)** across `life` / `work` / `project` / `daily` with a
41
+ bounded 8-item smart view, category and quadrant fields, and a per-day
42
+ `YYYY-MM-DD.todo.md` file.
43
+ - **`memory_suggest` tool** (confirmation-gated): proposals are queued in
44
+ `SUGGESTIONS.jsonl` and only written to memory after explicit user approval.
45
+ - **`memory:snapshot` system-prompt context** at configurable `snapshotOrder`
46
+ (default 500): injects session-id header, global + current-project `KEY`,
47
+ the end-of-turn daily/project write discipline, and the todos reminder.
48
+ - **In-place adoption migration** (`scripts/migrate.mjs`, RPC
49
+ `migration.*`): read-only `inspect` → `--apply` backs up a SHA-256 manifest
50
+ and adopts `schema.json`; `--verify` compares digests and blocks writes on
51
+ drift via `write-block.json`; `rollback(root, runId)` restores byte-identical
52
+ files.
53
+ - **Confirmation-gated review queue** (`queue.*`): review decisions are queued
54
+ and applied via an explicit, user-facing decision flow; the review tool is
55
+ registered dynamically and only when the runtime switch is on.
56
+ - **Read-first skills browser** (`skills.list`, host `skills-browser`):
57
+ metadata/origin-only listing of the default maestro-skills checkout, no
58
+ mutation and no body content.
59
+ - **Web UI** in a single `conversation.view` slot (`id: maestro-memory`,
60
+ order 40) with Memory / Review / Todos / Skills tabs, DSH-themed controls, and
61
+ package-private RPC over `/dsh-maestro-memory`.
62
+ - **M5 Git sync** (opt-in): project-scoped `sync.enable/disable/status/fetch/
63
+ push/pull/resolve/listConflicts`. Disabled means zero network activity; pushes
64
+ are explicit and a conflict never silently drops either version.
65
+
66
+ ### Fixed
67
+
68
+ - Hard-coded light-then-dark theme styles replaced with `--dsw-alias-*` tokens
69
+ (the active-tab highlight uses `--dsw-alias-interactive-bg-active`, which
70
+ flips correctly on both themes).
71
+ - Track/navigation controls made visually distinct and height-consistent so
72
+ switching between memory tracks never reflows the toolbar.
73
+ - The memory tool's `date` parameter is now honored for the `daily` track
74
+ (previously declared but unused) with an invalid-date guard.
75
+ - Key-track entries always carry an id even without a summary, so
76
+ `expand(id)` targets any key entry.
77
+ - Daily memory stamps the local calendar date (matching the todos store) instead
78
+ of UTC, so daily logs land on the same day.
79
+ - `skills.list` no longer reads an arbitrary client-supplied directory; it is
80
+ constrained to the resolved default maestro-skills checkout.
81
+
82
+ ### Removed
83
+
84
+ - All legacy `dsh-memory-evolve` features not carried into the rebrand: COI /
85
+ broadcast, advisor, notify/`de_channel_send`, search, prompt library, model
86
+ registry, bookmarks/mermaid/canvas, and the old `/memory-evolve` HTTP server.
87
+
88
+ ### Notes
89
+
90
+ - The package is consumed as a DSH plugin via `cordis.patch.yml`
91
+ (`id: maestro-memory`) and is installed with a `link:` dependency or the
92
+ `github:ddtcorex/dsh-maestro-memory#<sha>` form. Live `lib/` is committed so a
93
+ rebuild is only needed after editing `src/`.
94
+
95
+ [1.0.0]: https://github.com/ddtcorex/dsh-maestro-memory/releases/tag/v1.0.0
96
+ [1.0.1]: https://github.com/ddtcorex/dsh-maestro-memory/releases/tag/v1.0.1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-external
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,448 @@
1
+ # dsh-maestro-memory
2
+
3
+ ## Purpose
4
+
5
+ Durable, user-governed memory and todos for DeepSeek Harness (DSH) that preserves your existing `~/.dsh/memories` files in place.
6
+
7
+ > **One sentence:** Give the AI in DSH cross-session durable memory and todos — the more you use it, the more it understands you, and switching sessions never loses context.
8
+
9
+ - **Package:** `@ddtcorex/dsh-maestro-memory` (`cordis.patch.yml` id `maestro-memory`)
10
+ - **Changelog:** `CHANGELOG.md`
11
+ - **Version:** `1.0.1`
12
+
13
+ ---
14
+
15
+ ## Requirements
16
+
17
+ - Node.js 22+, pnpm 11+
18
+ - DSH `deepseek-harness` master (for `cordis`, `dsh-client-*` peers)
19
+ - Existing `~/.dsh/memories` directory (created lazily if absent)
20
+
21
+ ---
22
+
23
+ ## Install
24
+
25
+ From the checked-out repo:
26
+
27
+ ```sh
28
+ pnpm install # install deps (frozen lockfile in CI)
29
+ pnpm run build # tsc host + tsc client + build-client.mjs -> lib/
30
+ pnpm run verify # tsc --noEmit host + client (typecheck)
31
+ pnpm test # full Vitest suite (13 files, 199 tests)
32
+ ```
33
+
34
+ Manual verification of the client bundle:
35
+
36
+ ```sh
37
+ test -f lib/client.js && head -n 2 lib/client.js | grep -q "ModuleLoader" && echo "bundle ok"
38
+ ls -lh lib/client.js lib/index.js
39
+ ```
40
+
41
+ ### From a DSH profile (operator)
42
+
43
+ The package is consumed as a DSH plugin via `cordis.patch.yml`. Two install paths:
44
+
45
+ **Local link (development / recommended for cutover rehearsal):**
46
+
47
+ ```sh
48
+ # inside the profile that will own the plugin:
49
+ dsh plugin --profile web add link:<workspace-root>/packages/dsh-maestro-memory
50
+ # or manually in ~/.dsh/profiles/web/package.json:
51
+ # "@ddtcorex/dsh-maestro-memory": "link:<workspace-root>/packages/dsh-maestro-memory"
52
+ ```
53
+
54
+ **Git / registry (production after release):**
55
+
56
+ ```sh
57
+ dsh plugin --profile web add github:ddtcorex/dsh-maestro-memory#<tag-or-sha>
58
+ # pin to an exact commit SHA; branch names reuse stale tarballs (pnpm cache pitfall)
59
+ ```
60
+
61
+ After install, rebuild is not needed inside the profile — the host loads `lib/index.js` and the client loads `lib/client.js` via the `dsh.client` manifest. If you edited `src/client/`, rebuild at the checkout first (`pnpm run build`).
62
+
63
+ ---
64
+
65
+ ## Profile Patch
66
+
67
+ `cordis.patch.yml` is **owned by the package** and applied automatically by `dsh plugin add`. Do not duplicate it in the profile.
68
+
69
+ ```yaml
70
+ # dsh-maestro-memory/cordis.patch.yml (shipped with the package)
71
+ - insert:
72
+ - id: maestro-memory
73
+ name: '@ddtcorex/dsh-maestro-memory'
74
+ config:
75
+ memoryDir: null # null -> ~/.dsh/memories
76
+ snapshotOrder: 500 # systemPrompt.context order
77
+ ```
78
+
79
+ Profile `~/.dsh/profiles/web/package.json` after a correct install:
80
+
81
+ ```json
82
+ {
83
+ "dsh": { "profile": { "bundles": ["@ddtcorex/dsh-maestro-memory"] } },
84
+ "dependencies": {
85
+ "@ddtcorex/dsh-maestro-memory": "link:<workspace-root>/packages/dsh-maestro-memory"
86
+ }
87
+ }
88
+ ```
89
+
90
+ **Rules:**
91
+
92
+ - `dependencies` value **must be `link:`**, not a semver. CI and `assertSingleOwner` reject non-link owners.
93
+ - `bundles` must list exactly one owner for each compat tool (see below). Do not keep `dsh-memory-evolve` and `dsh-maestro-memory` in the same profile — they compete for `memory`/`dtodo` and for file ownership. The loader crashes on duplicate `id: maestro-memory` if you copy the patch row into the profile manually.
94
+ - `memoryDir: null` resolves to `~/.dsh/memories` (`resolveMemoryRoot(null)`). Override only for tests / rehearsal (`--root /tmp/...`).
95
+
96
+ Verify the profile in a rehearsal (see `src/host/migration/fixture.ts`):
97
+
98
+ ```ts
99
+ import { createFixtureProfile, assertSingleOwner } from '@ddtcorex/dsh-maestro-memory/migration/fixture'
100
+ await createFixtureProfile({ profileDir: '/tmp/profile', packageDir: '/path/to/dsh-maestro-memory' })
101
+ const res = await assertSingleOwner('/tmp/profile')
102
+ console.assert(res.ok && res.owners['memory'] === '@ddtcorex/dsh-maestro-memory')
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Supported Tools
108
+
109
+ All tools are registered via `ctx.tools.register` inside `ctx.effect(..., 'label')` so they dispose cleanly on unload. No HTTP.
110
+
111
+ | Tool | Purpose | When visible |
112
+ |------|---------|--------------|
113
+ | `memory` | CRUD + query for five tracks (`memory`/`user`/`project`/`key`/`daily`) + archive/expand. See `src/host/memory/store.ts`. | Always |
114
+ | `dtodo` | Four-track todos (`life`/`work`/`project`/`daily`) with stable 8-hex ids, status/due/quadrant, smart view (max 8), historical daily lookup. | Always |
115
+ | `memory_suggest` | **Gated** — model proposes `memory`/`user`/`key`/`todo-*` into `SUGGESTIONS.jsonl`; never writes directly. Requires human approve/edit/reject via Review UI or `queue.decide` RPC. | Always |
116
+ | `memory_review_status` | Read-only queue depth / write-block status (used by prompt hint / UI badge). | Always |
117
+ | `skill_manage` | Browse / mutate `~/.agents/skills` (optional module). Disabled by default; enable only if the optional skills module is explicitly configured. | Opt-in |
118
+
119
+ ### `memory` — actions and targets
120
+
121
+ ```ts
122
+ memory({
123
+ action: 'add'|'list'|'replace'|'remove'|'archive'|'expand',
124
+ target: 'memory'|'user'|'project'|'key'|'daily', // memory=global, key=per-cwd long-term
125
+ content?: string, // add: entry body; replace: new body
126
+ match?: string, // replace/remove/archive: unique substring of existing entry
127
+ filter?: string, // list: content substring filter
128
+ since?: string, until?: string, // list: YYYY-MM-DD
129
+ limit?: number, recent?: boolean, branch?: string, archived?: boolean,
130
+ branches?: string, // add key: csv "main,dev" (empty=all), branch scope
131
+ summary?: string, // add key: one-line summary for progressive disclosure
132
+ id?: string, // expand: [mem-xxxx] id
133
+ cwd?: string, // project/key track working directory (defaults to session cwd)
134
+ date?: string, // daily track YYYY-MM-DD
135
+ })
136
+ ```
137
+
138
+ - **Progressive disclosure:** `key` entries are stored with an optional `[summary]` line; `list` without `expand` returns summaries; `expand` with `id` returns full text.
139
+ - **Branch scope:** `key` entries may carry `[branch:main]` tags; `list` with `branch` filters to that branch + entries with no branch tag.
140
+
141
+ ### `dtodo` — actions
142
+
143
+ ```ts
144
+ dtodo({
145
+ action: 'add'|'list'|'done'|'update'|'remove',
146
+ target?: 'life'|'work'|'project'|'daily', // add/list filter; add defaults to cwd?project:work
147
+ content?: string,
148
+ id?: string, // done/update/remove
149
+ due?: string, // YYYY-MM-DD
150
+ quadrant?: 'q1'|'q2'|'q3'|'q4', // or important/urgent booleans -> quadrant
151
+ cat?: string, status?: 'pending'|'doing'|'done'|'blocked'|'cancelled',
152
+ all?: boolean, past?: boolean, expired?: boolean, // list: smart-view controls
153
+ cwd?: string, date?: string,
154
+ })
155
+ ```
156
+
157
+ - **Smart view (default):** when `all !== true` and no filter, `list` returns at most 8 items ordered `overdue -> due today -> current project -> q1/q2 -> rest`. Uses local date, not UTC.
158
+ - **History:** `past=true` alone shows only completed history; `past=true AND expired=true` includes expired unfinished daily todos (daily todos expire same day).
159
+
160
+ ### `memory_suggest` (gated)
161
+
162
+ ```ts
163
+ memory_suggest({ target: 'memory'|'user'|'key'|'todo-life'|'todo-work'|'todo-project'|'todo-daily', content: string, reason: string })
164
+ ```
165
+
166
+ Dedupes by `(target, content)` within the queue (bumps `hits`), appends to `SUGGESTIONS.jsonl`. The model must never write `key`/`user` directly — queue + human click is the only activation path.
167
+
168
+ ---
169
+
170
+ ## System Prompt Snapshot
171
+
172
+ Registered as `ctx.systemPrompt.context({ name: 'memory:snapshot', order: 500, text: (ctx) => renderSnapshot(cwd, branch) })`.
173
+
174
+ Injected text is **bounded** and deterministic: `USER + global MEMORY + current-project KEY` (branch-filtered if `session.header.branch` is present), plus a header with `sessionId`/`sessionName` and an end-of-turn discipline note (rendered verbatim as `---` + newline + sentence):
175
+
176
+ > End of every turn you must: 1. Write daily+project via memory entries (daily+project in one call) 2. Check dtodo list (bounded, max 8)
177
+
178
+ `daily` and `project log` (`projects/<hash>/MEMORY.md`) are queryable via `memory` but **not injected**, to keep prompt cost predictable. New `prompt/snapshot.ts` must reproduce this contract or agents silently stop writing logs.
179
+
180
+ Each injected section also enforces a **per-track byte cap** — defaults `SNAPSHOT_SECTION_CAPS = { memory: 2048, user: 4096, key: 6144 }`, overridable per call via `renderSnapshot(store, ctx, { caps })`. Entries are kept newest-first; the oldest overflow is dropped. The newest entry is always kept: if it alone exceeds the cap **and** carries an `[summary:…]` header tag (parsed by `ENTRY_HEAD_RE`), it renders compacted to `head + [summary:…]`; untagged oversize entries stay whole rather than vanishing.
181
+
182
+ ---
183
+
184
+ ## UI & RPC
185
+
186
+ - **UI:** exactly one `conversation.view` slot `{ name:'conversation.view', id:'maestro-memory', order:40, label:()=>'Memory' }` with internal tabs **Memory / Review queue / Todos**. Uses package-private RPC, no HTTP, no DOM hacks. Client injects `['slots','locale','conversation','sessions','connection']`.
187
+ - **RPC channel:** `/dsh-maestro-memory` (`ctx.connection.rpc.handle` host, `ctx.connection.rpc.call` client). Endpoints: `queue.list`, `queue.decide` (`approve`/`reject`/`archive` with optional `edits`/`targets` + `cwd`), `memory.list`, `todo.list`, `todo.mutate`, `migration.inspect`/`dryRun`/`run`/`verify`, `status` (`{ queue, blocked }`). `migration.run` via RPC requires `payload.apply === true`.
188
+
189
+ ---
190
+
191
+ ## Cutover
192
+
193
+ **Principle:** staged single-owner replacement — never run `dsh-memory-evolve` and `dsh-maestro-memory` in the same profile. The new internals, services, RPC methods, and slot ids use a Maestro namespace; compatibility is limited to agent-facing tool names and legacy file grammar.
194
+
195
+ **Operator steps (production):**
196
+
197
+ 1. **Preflight** on a copy, not live home (see Migration). Keep the live profile untouched until verification passes.
198
+ 2. **Backup** the live `~/.dsh/memories` via `node scripts/migrate.mjs --root ~/.dsh/memories --apply` — this is the only write; it creates `manifest.json` + byte-identical `files/` under `.maestro-memory/backups/<utc-run-id>/` + `schema.json` + `journal`.
199
+ 3. **Verify** (`--verify`) — must be `ok=true`, `mismatches=[]`. If not, writes are blocked (`write-block.json`) — resolve before continuing.
200
+ 4. **Profile swap:** remove `dsh-memory-evolve` from `bundles`/`dependencies`, add `@ddtcorex/dsh-maestro-memory` as `link:` (or pinned git SHA). Ensure exactly one owner per compat tool (`memory`, `dtodo`).
201
+ 5. **Reload profile:** restart `dsh web` at a user-approved window (ask first — do not kill the live `dsh web` process mid-session; it holds both :3000 and :3080). After restart, live-read every track (`memory` list for each target, `dtodo` list) before first mutation.
202
+ 6. **One write** against live data, then `verify` again.
203
+
204
+ **Before any writes, rollback is just a profile change** (remove Maestro, restore old bundle). After writes, restore files from the manifest.
205
+
206
+ For a disposable rehearsal, use `src/host/migration/fixture.ts` (`createFixtureProfile`, `createCopiedMemoryRoot`, `assertSingleOwner`) — see `tests/m4-rehearsal.spec.ts` and the `Migration rehearsal` CI job. Never touch `~/.dsh/memories` in tests.
207
+
208
+ ---
209
+
210
+ ## Migration
211
+
212
+ CLI: `node scripts/migrate.mjs --root <path> [--inspect|--dry-run|--verify|--apply] [--run-id <id>]`
213
+
214
+ Default is **read-only**. The only write is `--apply`.
215
+
216
+ | Command | Effect | Side effects |
217
+ |---------|--------|--------------|
218
+ | `--inspect` (default) | Inventory, parse, byte count, SHA-256, warnings for malformed JSONL / locks / non-canonical files | None |
219
+ | `--dry-run` | Same as inspect, explicitly read-only | None |
220
+ | `--apply` | **Backup + adopt:** byte-preserving copy of every file (excluding `.maestro-memory`) into `backups/<utc-run-id>/files/` + `manifest.json` (`path, bytes, sha256, inventory`) + `schema.json` + `migration-journal.jsonl` entry. Only after all required data parses; source content is never reformatted. | Writes `manifest`, `files/`, `schema.json`, `journal` |
221
+ | `--verify` | Reopen with new stores, compare digest (`bytes`, `sha256`) + inventory (`memoryEntries`, `todoIds`, `queueValid`) against manifest. On mismatch, writes `.maestro-memory/write-block.json` and blocks mutations; on success clears the block. | Writes `write-block.json` on failure; clears on success |
222
+
223
+ **Disk layout:**
224
+
225
+ ```
226
+ ~/.dsh/memories/
227
+ MEMORY.md USER.md # may be absent until first global write
228
+ MEMORY-archive.md USER-archive.md
229
+ SUGGESTIONS.jsonl
230
+ TODOS-life.md TODOS-work.md
231
+ daily/YYYY-MM-DD.md daily/YYYY-MM-DD.todo.md
232
+ projects/<sha1(cwd)[:12]>/
233
+ MEMORY.md KEY.md
234
+ KEY-archive.md TODOS.md
235
+ .maestro-memory/
236
+ schema.json
237
+ migration-journal.jsonl
238
+ write-block.json # present only when verify failed
239
+ backups/<utc-run-id>/
240
+ manifest.json # { files:[{path,relative,bytes,sha256,kind,...}], inventory, runId, at }
241
+ files/... # byte-identical copies
242
+ ```
243
+
244
+ **Warnings (non-fatal, reported in `inspect`/`dryRun`/`verify`):**
245
+
246
+ - `non-canonical` — file does not round-trip through `§` parse/serialize (drift); mutation is refused until canonicalized.
247
+ - `malformed todo` — entry missing timestamp/id in a todo file.
248
+ - `malformed queue` — JSONL line in `SUGGESTIONS.jsonl` that does not parse as `{target, content}`.
249
+
250
+ **Write-block:** `migration/service.ts:isWriteBlocked(root)` checks `.maestro-memory/write-block.json`. When blocked, `memory`/`dtodo` mutations return an error until `verify` passes or `rollback` clears it.
251
+
252
+ **Examples:**
253
+
254
+ ```sh
255
+ node scripts/migrate.mjs --root ~/.dsh/memories # inspect (read-only)
256
+ node scripts/migrate.mjs --root /tmp/mem --dry-run # dry-run
257
+ node scripts/migrate.mjs --root /tmp/mem --apply # backup + adopt
258
+ node scripts/migrate.mjs --root /tmp/mem --verify # verify (latest manifest)
259
+ node scripts/migrate.mjs --root /tmp/mem --verify --run-id 20260824T151230.425Z
260
+ ```
261
+
262
+ ---
263
+
264
+ ## Verification
265
+
266
+ 1. After `inspect`/`dryRun`, confirm `ok=true`, expected `memoryEntries`/`todoIdsCount`/`queueValid`, and review `warnings`.
267
+ 2. After `--apply`, confirm `manifest.json` exists, each `files/<relative>` copy is byte-identical (`sha256` matches), and `~/.dsh/memories` files are unchanged (no reformatting).
268
+ 3. After `--verify`, confirm `ok=true`, `mismatches=[]`. If `ok=false`, check `mismatches` (`digest mismatch`, `byte count mismatch`, `todo ID set mismatch`, `inventory mismatch`) and `.maestro-memory/write-block.json`. No mutation should proceed while blocked.
269
+ 4. After profile reload, live-read via tools/RPC (`memory` list for `memory`/`user`/`key`/`daily`/`project`, `dtodo` list for `life`/`work`/`project`/`daily`) and compare to pre-cutover inventory.
270
+
271
+ The rehearsal suite (`tests/m4-rehearsal.spec.ts`) exercises the full sequence against a copied schema: fixture profile (`link:`) → one-owner proof → dry-run (no `.maestro-memory`) → backup (byte-preserving) → verify → profile reload (`apply`/`ctx.effect`) → live reads → one write → second verify (fails) → rollback (byte-identical) → verify (passes) → live home untouched.
272
+
273
+ ---
274
+
275
+ ## Rollback
276
+
277
+ Rollback restores files **byte-identical** from a backup manifest. It is exercised and tested in `tests/m4-rehearsal.spec.ts`.
278
+
279
+ **When to rollback:**
280
+
281
+ - Before any writes: no rollback needed — just revert the profile change (remove Maestro bundle, restore old plugin).
282
+ - After a failed `verify` or a bad write: restore from the backup that `verify` reports.
283
+
284
+ **How (CLI / service API):**
285
+
286
+ ```ts
287
+ import { rollback } from './src/host/migration/service.ts'
288
+ // restore latest (schema.json runId or newest backup)
289
+ await rollback('/tmp/memories')
290
+ // or specific run
291
+ await rollback('/tmp/memories', '20260824T151230.425Z')
292
+ ```
293
+
294
+ Or via the `migration` RPC (host) if exposed. The service:
295
+
296
+ - Copies each `manifest.files[].relative` from `backups/<runId>/files/` to its original `path`, verifying `sha256` after copy.
297
+ - If a file was absent at backup time (`exists:false` in manifest) but appeared later, it is removed.
298
+ - Clears `write-block.json` on completion and appends a `rollback` entry to `migration-journal.jsonl`.
299
+ - Returns `{ ok, runId, manifestPath, restored, errors }` (`restored` = count of files restored/removed).
300
+
301
+ **After rollback:**
302
+
303
+ - `verify` must pass (`ok=true`, no mismatches).
304
+ - A new write must succeed (the write-block is cleared).
305
+
306
+ **Retention:** keep `~/.dsh/memories/.maestro-memory/backups/` for at least 90 days after cutover (per plan). Do not delete the manifest for the adopted run.
307
+
308
+ ---
309
+
310
+ ## Removed Features (intentionally not in v1)
311
+
312
+ Source-grounded inventory. Propose any as a separate plugin later.
313
+
314
+ | Source subsystem | Verdict | Rationale |
315
+ |------------------|---------|-----------|
316
+ | **Cross-device Git memory sync** (`lib/sync/*`) | **KEEP as optional module (M5)** | Large conflict-resolution product; must not delay local-data reliability. Disabled = zero network/Git activity. |
317
+ | **Skills management/browser** (`lib/skills.js`, `lib/skills-manager.js`) | **KEEP as optional module (M6)** | Mutates `~/.agents/skills`; Maestro already owns skills in `maestro-skills`. Core stays read-only. |
318
+ | **COI / external CLI dispatch, scheduler, broadcast, ws coordinator, stats, attachments, session orchestration** (`lib/coi/*`, `lib/session-orch.js`) | **DROP** | Independent orchestration platform; profile already has DSH Codex/Claude subagent bundles. |
319
+ | **Advisor** (`lib/advisor/*`) | **REDESIGN** (separate opt-in plugin) | Full reviewer runtime with model calls and panel — not memory. |
320
+ | **Notify / channel send / session images** (`lib/notify.js`) | **DROP** | IM/web delivery via global integration registry (`de_channel_send`, `de_notify`); unrelated to durable memory. |
321
+ | **Local / docs / Codex search** (`lib/search-docs.js`, `lib/search/*`) | **DROP** | Host file scans / shell-out expands authority without being needed for memory. |
322
+ | **Prompt injection library** (`lib/prompts.js`) | **DROP** | Reusable guidance belongs in `maestro-skills`. |
323
+ | **Model registry / settings** (`lib/models.js`) | **DROP** | Overlays DSH model config; `lib/index.js` already injects DSH settings/llm. |
324
+ | **Bookmarks, Mermaid, canvas, UI settings, aliases, update checker, i18n** (`lib/{bookmarks,mermaid,canvas,ui-settings,aliases,update,i18n}.js`) | **REDESIGN** | Independent features; propose individually if needed. |
325
+ | **Client/WebUI broad tabs** (`src/client/index.ts` family) | **REDESIGN** | Legacy mounts many tabs/actions + HTTP API; v1 has one slot + package-private RPC. |
326
+ | **HTTP API** (`lib/api.js` `/memory-evolve` prefix, `ctx.webServer`) | **DROP** | No `webServer` registration; only `ctx.connection.rpc.handle('/dsh-maestro-memory', …)`. |
327
+
328
+ **Preserved in backup but not imported:** `advisor/`, `pending-skills/`, `plugin-state.json`, `search-docs-index.json`, `coi/` (copied byte-identical into `files/` and listed in `manifest.json`, never parsed as memory state).
329
+
330
+ ---
331
+
332
+ ## Workflow: Superpowers skills are mandatory
333
+
334
+ Every change to this repository MUST follow the Superpowers skill workflow, in order:
335
+
336
+ 1. **brainstorming** — explore intent, requirements, and design before any code; record the outcome in `docs/superpowers/specs/` (`YYYY-MM-DD-<topic>-design.md`).
337
+ 2. **writing-plans** — turn an approved spec into a task-by-task plan with exact test and implementation sketches in `docs/superpowers/plans/` (`YYYY-MM-DD-<topic>.md`).
338
+ 3. **executing-plans** — implement task by task with strict TDD: write the failing test first, verify RED, implement, verify GREEN, then commit that task as its own commit before starting the next.
339
+
340
+ Do not skip ahead to implementation, batch multiple tasks into one commit, or commit while a task's tests are red. Trivial mechanical fixes may go straight to a commit but still need tests when behavior changes.
341
+
342
+ ---
343
+
344
+ ## Git Workflow
345
+
346
+ - Never commit to `master` directly; batch related work on a feature branch (`feat/...`, `fix/...`). One TDD task = one commit while executing a plan.
347
+ - Conventional commit subjects, imperative mood.
348
+ - Push the branch and open an MR when the batch is green; rebase instead of merging master into the branch when the base moves.
349
+ - `origin` is `git@github.com:ddtcorex/dsh-maestro-memory.git` (private, default branch `master`).
350
+
351
+ ---
352
+
353
+ ## Layout
354
+
355
+ - `src/host/` — Cordis host plugin (`storage/legacy-format.ts`, `storage/layout.ts`, `storage/atomic-store.ts`, `memory/store.ts`, `todo/store.ts`, `review/queue.ts`, `migration/service.ts`, `prompt/snapshot.ts`, `index.ts`).
356
+ - `src/client/` — DSH client bundle (`src/client/index.tsx` → `lib/client.js` via `scripts/build-client.mjs`, `conversation.view` id `maestro-memory` order 40, internal Memory/Review/Todos tabs, RPC `/dsh-maestro-memory`).
357
+ - `tests/` — Vitest specs (`legacy-format.spec.ts`, `storage.spec.ts`, `atomic-store.spec.ts`, `memory-m2.spec.ts`, `todo-m3.spec.ts`, `migration.spec.ts`, `m4-rehearsal.spec.ts`, etc.).
358
+ - `scripts/migrate.mjs` — migration CLI (`--root`, `--apply`/`--dry-run`/`--verify`).
359
+
360
+ Build outputs in `lib/` (`lib/client.js` via `scripts/build-client.mjs`) are generated — never edit by hand.
361
+
362
+ ---
363
+
364
+ ## Development
365
+
366
+ ```sh
367
+ pnpm install
368
+ pnpm test # full suite (13 files, 199 tests)
369
+ pnpm run verify # tsc --noEmit host + client (typecheck)
370
+ pnpm run build # tsc host + tsc client + bundle client.js
371
+ pnpm exec vitest run tests/legacy-format.spec.ts tests/storage.spec.ts tests/atomic-store.spec.ts # unit
372
+ pnpm exec vitest run tests/memory-m2.spec.ts tests/todo-m3.spec.ts tests/suggestion-queue.spec.ts tests/rpc-queue.spec.ts # integration
373
+ pnpm exec vitest run tests/migration.spec.ts # migration
374
+ pnpm exec vitest run tests/m4-rehearsal.spec.ts # rehearsal (fixture profile + rollback)
375
+ node scripts/migrate.mjs --root ~/.dsh/memories # inspect (read-only)
376
+ node scripts/migrate.mjs --root /tmp/mem --apply # backup + adopt
377
+ node scripts/migrate.mjs --root /tmp/mem --verify # verify
378
+ ```
379
+
380
+ Run `pnpm test` after host changes, `pnpm run verify` after TypeScript changes, `pnpm run build` after editing `src/client/`. Do not kill/restart the live `dsh web` process serving a session — ask for a convenient restart window.
381
+
382
+ ### CI
383
+
384
+ `.github/workflows/ci.yml` runs on `push` to `master`/`main` and on every PR:
385
+
386
+ | Job step | Command | What it proves |
387
+ |----------|---------|----------------|
388
+ | Install | `pnpm install --frozen-lockfile` | Reproducible deps |
389
+ | Build | `pnpm run build` | Host + client compile |
390
+ | Typecheck | `pnpm run verify` | `tsc --noEmit` host + client |
391
+ | Unit | `vitest run tests/legacy-format.spec.ts tests/storage.spec.ts tests/atomic-store.spec.ts` | Pure delimiter / path / atomic-store |
392
+ | Integration | `vitest run tests/memory-m2.spec.ts tests/todo-m3.spec.ts tests/suggestion-queue.spec.ts tests/rpc-queue.spec.ts tests/smoke.spec.ts` | Stores + queue + RPC |
393
+ | Migration | `vitest run tests/migration.spec.ts` | inspect/dryRun/run/verify, backup manifest, write-block |
394
+ | Rehearsal | `vitest run tests/m4-rehearsal.spec.ts` | Fixture `link:` profile, one-owner proof, copied-schema cutover + rollback, live home untouched |
395
+ | Client | `test -f lib/client.js && grep ModuleLoader lib/client.js` | Bundle exists + header |
396
+ | Full | `pnpm test` | Entire suite (13 files, 199 tests) |
397
+
398
+ ---
399
+
400
+ ## Security
401
+
402
+ - Never print, commit, or add fixture values for secrets. Use obviously synthetic values in tests and docs.
403
+ - No cloud service, database rewrite, telemetry, or sync in v1; M5 sync (Git-backed) is opt-in and disabled means zero network activity.
404
+ - Secrets must never be echoed to the client; new secret-bearing fields need masking + constant-time comparison.
405
+ - File permissions: memory files inherit host umask; `SUGGESTIONS.jsonl` is append-only; `write-block.json` is local-only.
406
+
407
+ ---
408
+
409
+ ## Troubleshooting
410
+
411
+ | Symptom | Cause | Fix |
412
+ |---------|-------|-----|
413
+ | `tool "memory" has multiple owners` in `assertSingleOwner` | Both `dsh-memory-evolve` and `dsh-maestro-memory` in `bundles` | Remove the old plugin from the profile; keep exactly one owner per compat tool |
414
+ | `profile cordis.patch.yml must not duplicate id maestro-memory` | Profile patch duplicates the package's `cordis.patch.yml` row | Delete the row from the profile patch; the package provides it |
415
+ | `verify failed: digest mismatch` / `write-block.json` exists | File changed after backup (hand-edit, concurrent writer) | Inspect `write-block.json` mismatches, then `rollback(root, runId)` or resolve drift and `verify` again |
416
+ | `non-canonical` warning | File contains non-`§`-canonical content (hand-edit) | Back up manually, then normalize via a single `replace` edit through the tool (creates canonical serialization) |
417
+ | `mismatched todo IDs` after verify | Todo file edited outside the store | Roll back or re-run `inspect` and compare inventories |
418
+ | DSH Web UI shows no Memory tab | `lib/client.js` not built or bundle header missing | `pnpm run build` at the checkout, then restart `dsh web` (user-approved window) and hard-refresh the browser |
419
+
420
+ ---
421
+
422
+ ## Ghi chú vận hành (Tiếng Việt — ngắn gọn)
423
+
424
+ > English là ngôn ngữ chính của tài liệu và mã nguồn. Phần này chỉ tóm tắt thao tác cho người vận hành tiếng Việt.
425
+
426
+ - **Cài đặt:** `pnpm install` → `pnpm run build` → `pnpm run verify` → `pnpm test`. Thêm plugin vào profile bằng `dsh plugin --profile web add link:/path/to/dsh-maestro-memory` (không copy `cordis.patch.yml` vào profile).
427
+ - **Công cụ hỗ trợ:** `memory` (5 track), `dtodo` (4 track, smart view 8), `memory_suggest` (đề xuất phải được duyệt), `memory_review_status`. `skill_manage` chỉ khi bật module tùy chọn.
428
+ - **Cutover (chuyển đổi):** chỉ chạy một plugin sở hữu `memory`/`dtodo`. Không chạy đồng thời `dsh-memory-evolve` và `dsh-maestro-memory`. Thực hiện trên bản copy trước, sau đó mới áp dụng lên `~/.dsh/memories`.
429
+ - **Migration:** `node scripts/migrate.mjs --root <path>` (mặc định chỉ đọc). `--apply` mới ghi backup + `schema.json`. `--verify` kiểm tra `sha256` + tồn kho; nếu lệch sẽ ghi `write-block.json` và chặn ghi.
430
+ - **Rollback:** `await rollback(root, runId)` khôi phục byte-identical từ `backups/<runId>/files/`; xóa `write-block.json` khi xong. Trước khi có ghi mới, rollback chỉ là đổi profile.
431
+ - **Tính năng đã loại bỏ:** COI/broadcast, advisor, notify/`de_channel_send`, search, prompt library, model registry, bookmarks/mermaid/canvas. Chi tiết xem bảng "Removed Features".
432
+ - **CI:** `build` + `typecheck` + `unit` + `integration` + `migration` + `rehearsal` + `client` + `full` — tất cả phải xanh trước khi merge.
433
+
434
+ ---
435
+
436
+ ## Documentation
437
+
438
+ Keep current behavior in `README.md`. Do not add plans, transient investigation logs, or duplicate specifications under `docs/`; capture only durable operator and architecture knowledge. The one exception is Superpowers artifacts: specs under `docs/superpowers/specs/` and plans under `docs/superpowers/plans/` are kept as the design record for each change batch.
439
+
440
+ ---
441
+
442
+ ## References
443
+
444
+ - `src/host/migration/service.ts` — inspect/dryRun/run/verify/rollback implementation
445
+ - `src/host/migration/fixture.ts` — fixture profile + copied-schema helpers for rehearsal
446
+ - `tests/m4-rehearsal.spec.ts` — end-to-end rehearsal (profile → backup → verify → reload → write → rollback)
447
+ - `scripts/migrate.mjs` — operations CLI (read-only by default, `--apply` to write)
448
+ - `.github/workflows/ci.yml` — build / typecheck / unit / integration / migration / client / full
@@ -0,0 +1,13 @@
1
+ # dsh-maestro-memory bundle patch: inserts the host row into the profile's
2
+ # plugin roster. The browser half rides the package's `dsh.client` manifest
3
+ # (the client registry serves lib/client.js), and this row materializes the
4
+ # Maestro memory / todos host plugin on boot.
5
+ # Install with `dsh plugin --profile <name> add <path-or-git-url>`; the bundle
6
+ # patch is applied automatically, so do NOT insert this row again in the
7
+ # profile patch (duplicate ids crash the loader).
8
+ - insert:
9
+ - id: maestro-memory
10
+ name: '@ddtcorex/dsh-maestro-memory'
11
+ config:
12
+ memoryDir: null
13
+ snapshotOrder: 500