memhouse 0.18.12 → 0.20.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 (169) hide show
  1. package/AGENTS.md +22 -151
  2. package/CHANGELOG.md +263 -29
  3. package/LICENSE +202 -22
  4. package/LICENSES/agentlytics-MIT.txt +21 -0
  5. package/LICENSES/memhouse-MIT-until-0.18.11.txt +22 -0
  6. package/LICENSES/models.dev-MIT.txt +22 -0
  7. package/NOTICE +28 -0
  8. package/README.md +42 -20
  9. package/SECURITY.md +21 -8
  10. package/bin/memhouse.js +2223 -311
  11. package/docs/README.md +11 -9
  12. package/docs/guides/admin-password.md +37 -10
  13. package/docs/guides/configuration.md +13 -6
  14. package/docs/guides/daemons.md +64 -3
  15. package/docs/guides/import-claude-ai.md +204 -0
  16. package/docs/guides/install.md +11 -6
  17. package/docs/guides/invite.md +21 -7
  18. package/docs/guides/operate.md +114 -7
  19. package/docs/guides/search-and-resume.md +72 -3
  20. package/docs/guides/share.md +91 -52
  21. package/docs/guides/storage.md +44 -4
  22. package/docs/guides/troubleshooting.md +121 -14
  23. package/docs/guides/uninstall.md +4 -3
  24. package/docs/guides/update.md +16 -0
  25. package/docs/guides/upgrade-to-0.20.md +188 -0
  26. package/docs/releases/README.md +2 -1
  27. package/docs/releases/v0.18.10.md +5 -5
  28. package/docs/releases/v0.18.11.md +3 -3
  29. package/docs/releases/v0.20.0.md +627 -0
  30. package/docs/releases/v0.20.1.md +114 -0
  31. package/docs/tutorials/01-first-house.md +10 -7
  32. package/docs/tutorials/02-join-a-house.md +24 -15
  33. package/docs/tutorials/03-share-your-memory.md +40 -31
  34. package/editors/antigravity.js +1 -80
  35. package/editors/base.js +463 -9
  36. package/editors/bridge.js +119 -0
  37. package/editors/claude.js +273 -105
  38. package/editors/codex.js +278 -52
  39. package/editors/copilot.js +1 -70
  40. package/editors/cursor.js +1 -75
  41. package/editors/derive-fingerprints.json +18 -0
  42. package/editors/harness-paths.js +69 -0
  43. package/editors/index.js +1 -20
  44. package/editors/normalise/agy.js +910 -0
  45. package/editors/normalise/copilot-cli.js +321 -0
  46. package/editors/normalise/cursor-agent.js +852 -0
  47. package/editors/normalise/cursor.js +474 -0
  48. package/editors/normalise/devin.js +575 -0
  49. package/editors/normalise/gemini-cli.js +602 -0
  50. package/editors/normalise/goose.js +673 -0
  51. package/editors/normalise/opencode.js +633 -0
  52. package/editors/normalise/vscode.js +549 -0
  53. package/editors/normalise/zed.js +473 -0
  54. package/editors/opencode-v4.js +119 -0
  55. package/editors/scope.js +14 -3
  56. package/editors/sessions-log.js +349 -0
  57. package/editors/sqlite.js +28 -1
  58. package/editors/vscode.js +1 -72
  59. package/editors/windsurf.js +1 -143
  60. package/examples/01-local-one-machine/README.md +24 -19
  61. package/examples/01-local-one-machine/run.sh +1 -1
  62. package/examples/02-existing-clickhouse/README.md +30 -22
  63. package/examples/02-existing-clickhouse/run.sh +5 -4
  64. package/examples/03-team-house/README.md +44 -37
  65. package/examples/03-team-house/run.sh +35 -18
  66. package/examples/04-self-hosted-team/README.md +29 -14
  67. package/examples/04-self-hosted-team/compose.yml +5 -2
  68. package/examples/04-self-hosted-team/smoke.sh +26 -12
  69. package/examples/README.md +8 -4
  70. package/examples/fixtures/README.md +4 -1
  71. package/examples/fixtures/make-sessions.js +8 -5
  72. package/memhouse/admin-secret.js +89 -24
  73. package/memhouse/argv-secrets.js +64 -0
  74. package/memhouse/channel.js +49 -9
  75. package/memhouse/codex-plugin.js +194 -0
  76. package/memhouse/delivery/AGENT-INSTALL-PROMPT.md +2 -1
  77. package/memhouse/delivery/AGENT-INSTALL.md +22 -11
  78. package/memhouse/delivery/INVITATION.md.tpl +44 -45
  79. package/memhouse/delivery/PROMPT.md +53 -5
  80. package/memhouse/delivery/UPGRADE.md.tpl +1 -1
  81. package/memhouse/delivery/codex/AGENTS-BLOCK.md +11 -0
  82. package/memhouse/delivery/codex/SKILL.md +50 -0
  83. package/memhouse/delivery/kernel-install.md +17 -9
  84. package/memhouse/delivery/plugin/.claude-plugin/plugin.json +2 -2
  85. package/memhouse/delivery/plugin/reference/HOUSE.md +168 -41
  86. package/memhouse/delivery/plugin/skills/access/SKILL.md +13 -5
  87. package/memhouse/delivery/plugin/skills/admin/SKILL.md +14 -6
  88. package/memhouse/delivery/plugin/skills/house/SKILL.md +25 -8
  89. package/memhouse/delivery/plugin/skills/recall/SKILL.md +66 -8
  90. package/memhouse/delivery/plugin/skills/sql/SKILL.md +66 -7
  91. package/memhouse/deploy.js +1 -1
  92. package/memhouse/flags.js +35 -18
  93. package/memhouse/help.js +37 -0
  94. package/memhouse/host.js +82 -2
  95. package/memhouse/house/HOUSE.md +2 -2
  96. package/memhouse/house/convert-profile.js +638 -0
  97. package/memhouse/house/convert-v3.js +742 -0
  98. package/memhouse/house/doctor-v3.js +324 -0
  99. package/memhouse/house/host-alias.js +85 -0
  100. package/memhouse/house/house.js +3 -3
  101. package/memhouse/house/migrate.js +2 -2
  102. package/memhouse/house/profile-dedupe.js +183 -0
  103. package/memhouse/house/profile-edges.js +554 -0
  104. package/memhouse/house/profile-house.js +430 -0
  105. package/memhouse/house/profile-rollup.js +190 -0
  106. package/memhouse/house/q-cli.js +863 -0
  107. package/memhouse/house/queries-v3.js +1013 -0
  108. package/memhouse/house/reconcile-v3.js +192 -0
  109. package/memhouse/house/records-to-profile.js +174 -0
  110. package/memhouse/house/records-to-rows.js +169 -0
  111. package/memhouse/house/relocate.js +2 -1
  112. package/memhouse/house/schema-choice.js +121 -0
  113. package/memhouse/house/schema-profile.js +290 -0
  114. package/memhouse/house/schema-profile.sql.tpl +398 -0
  115. package/memhouse/house/schema-v3.js +267 -0
  116. package/memhouse/house/schema-v3.sql.tpl +213 -0
  117. package/memhouse/import/claude-ai.js +420 -0
  118. package/memhouse/import/import.js +372 -0
  119. package/memhouse/import/pyjson.js +206 -0
  120. package/memhouse/import/records.js +64 -0
  121. package/memhouse/import/writer-profile.js +192 -0
  122. package/memhouse/import/writer-v3.js +240 -0
  123. package/memhouse/import/zip.js +112 -0
  124. package/memhouse/invitation.js +102 -0
  125. package/memhouse/invitefile.js +5 -2
  126. package/memhouse/plugin-copies.js +176 -0
  127. package/memhouse/provision.js +7 -2
  128. package/memhouse/release-hint.js +24 -0
  129. package/memhouse/resume.js +4 -4
  130. package/memhouse/server/api-v3.js +632 -0
  131. package/memhouse/server/dashboard-auth.js +181 -0
  132. package/memhouse/server/dashboard-port.js +91 -0
  133. package/memhouse/server/queries.js +30 -0
  134. package/memhouse/server/server.js +118 -20
  135. package/memhouse/service.js +1 -1
  136. package/memhouse/share.js +687 -1
  137. package/memhouse/shipper/adapter-capture.js +756 -0
  138. package/memhouse/shipper/capture.js +2571 -0
  139. package/memhouse/shipper/dedupe-profile.js +237 -0
  140. package/memhouse/shipper/dedupe-v3.js +1027 -0
  141. package/memhouse/shipper/forks.js +233 -0
  142. package/memhouse/shipper/log-adapter.js +219 -0
  143. package/memhouse/shipper/maintenance.js +49 -0
  144. package/memhouse/shipper/normalise-run.js +148 -0
  145. package/memhouse/shipper/passlog.js +68 -5
  146. package/memhouse/shipper/profile-capture.js +903 -0
  147. package/memhouse/shipper/ship.js +91 -8
  148. package/memhouse/shipper/snapshots.js +193 -0
  149. package/memhouse/shipper/source-roots.js +111 -0
  150. package/memhouse/version-notice.js +66 -0
  151. package/package.json +14 -7
  152. package/public/THIRD_PARTY_NOTICES.txt +2 -2
  153. package/public/assets/index-CcNnhfM3.js +78 -0
  154. package/public/index.html +1 -1
  155. package/ui/package-lock.json +56 -37
  156. package/ui/package.json +1 -0
  157. package/ui/src/App.jsx +29 -14
  158. package/ui/src/components/ChatSidebar.jsx +69 -12
  159. package/ui/src/lib/api.js +76 -0
  160. package/ui/src/lib/dashboard-token.js +41 -0
  161. package/ui/src/main.jsx +22 -5
  162. package/ui/src/pages/Dashboard.jsx +22 -36
  163. package/ui/third-party-notices.js +119 -0
  164. package/ui/vite.config.js +5 -1
  165. package/docs/releases/v0.18.12.md +0 -59
  166. package/memhouse/COMPETITION.md +0 -221
  167. package/memhouse/DESIGN.md +0 -222
  168. package/memhouse/RESUME-AND-UPDATE.md +0 -182
  169. package/public/assets/index-CnWzrjF3.js +0 -78
@@ -0,0 +1,627 @@
1
+ # memhouse 0.20.0 — 2026-10-29
2
+
3
+ 0.20 changes how memhouse stores what it captures. It is the first release since 0.18.11;
4
+ there is no 0.19.
5
+
6
+ - **Schema 3.** A session is keyed by its store: the machine, the editor, the editor's data
7
+ folder (for OpenCode, its database file) and the session's own id. Each member gets six
8
+ conversation tables, the harness-sessions-sql profile: `<m>_session_parts`, `<m>_session_usage`,
9
+ `<m>_session_links`, `<m>_session_lifecycle`, `<m>_calls` and `<m>_sessions`. Each member also
10
+ gets a capture archive in `<db>_archive` (`<m>_session_units` and `<m>_session_sources`) that
11
+ keeps every transcript byte as captured.
12
+ - **Nothing is cut or lost.** Tool output is kept in full. A message an editor reverts,
13
+ rewinds or deletes stays in the house as a removed version: never counted, and listed by
14
+ `memhouse q removed`.
15
+ - **A fork is billed once.** Where a session copies another and the copy can be told, the copied
16
+ prefix counts only in the original: by the link agy and Copilot CLI state, or by a matching first
17
+ reply in OpenCode, VS Code, cursor-agent, Cursor and Goose. Other editors' copies, Claude
18
+ Code's and Codex's included, are not covered.
19
+ - **Ten editors read as they store their sessions.** Each has a normaliser that reads its
20
+ store directly: agy, Copilot CLI, Cursor, cursor-agent, Devin, Gemini CLI, Goose,
21
+ OpenCode, VS Code and Zed. Claude Code and Codex are read natively, and the remaining
22
+ editors through their 0.18 readers.
23
+ - **Moving a 0.18 house is a deliberate admin step, and `memhouse update` never does it.**
24
+ A 0.18 house keeps working on 0.20, unchanged, until its admin converts it.
25
+ - **memhouse is Apache-2.0 from this release.** Releases up to 0.18.11 remain MIT.
26
+
27
+ ## Read this before upgrading
28
+
29
+ ### `memhouse update` installs 0.20 and does not convert the house
30
+
31
+ A machine on 0.20 keeps shipping a 0.18 house's rooms until two things happen: the admin
32
+ converts the member, then the machine is switched with `memhouse migrate --v3-switch`.
33
+ Every machine can be upgraded first, days before the conversion.
34
+
35
+ Behind a registry mirror, 0.18's own `update` asks registry.npmjs.org and cannot see 0.20.
36
+ There, run `npm install -g memhouse@<version>`, then `memhouse update --no-install`.
37
+
38
+ `update` still does what every update does to the rooms it ships into: it creates a missing
39
+ room or column for the new code (`ship --ensure-schema`), and can run a pending 0.18
40
+ migration (it asks first; `--migrate` or `--yes` say yes). It never converts to schema 3.
41
+
42
+ ### Converting a house is the admin's procedure
43
+
44
+ [Upgrade to 0.20](../guides/upgrade-to-0.20.md) is the whole procedure. Run each step with
45
+ `--dry-run` first; each refuses before changing anything when the house is not in the
46
+ state it expects.
47
+
48
+ 1. `memhouse migrate --v3-prepare --member <m>` fences the 0.18 writers off and renames the
49
+ member's rooms into `<db>_archive` as `*_pre_v3`. Nothing is deleted.
50
+ 2. `memhouse migrate --v3-convert --member <m>` converts the rooms into schema 3. It takes
51
+ the cached tokens out of 0.18's Gemini CLI input (see Fixed), and `--dry-run` says how
52
+ many rows and tokens that is.
53
+ 3. `memhouse migrate --v3-reconcile --member <m>` compares the legacy and converted rows,
54
+ session by session.
55
+ 4. `memhouse migrate --v3-shares --member <m>` moves the member's recorded shares.
56
+ 5. `memhouse migrate --v3-switch`, on every machine, followed by a restart.
57
+
58
+ The upgrade guide also gives the rollback for before any machine has switched.
59
+
60
+ ### A house in daily use is converted on its own schedule
61
+
62
+ **Converting a house people depend on is not part of `npm update` or of this release.** It
63
+ rewrites that house's data: the conversion, the host re-key to sessions-model/host/v0, and the
64
+ OpenCode key. Plan it first, as its admin decides: the steps, the rollback copy, the disk it
65
+ needs, and the downtime. Until then the house stays on the 0.18 rooms, and its machines can run
66
+ 0.20 against it unchanged.
67
+
68
+ ### New houses
69
+
70
+ A house created by 0.20 (`deploy --local`, `install --admin-user`, `invite` into an empty
71
+ server) is schema 3. Joining an existing house follows the layout the house already has,
72
+ and a house with members on both layouts is refused until `--schema` says which.
73
+
74
+ ### Scripts that pass `--admin-password`
75
+
76
+ `--admin-password <value>` is refused in 0.20 (it was deprecated in 0.18.10). A script that
77
+ passes it stops with exit 2 and nothing done. Give the password in `MEMHOUSE_ADMIN_PASSWORD`
78
+ or with `--admin-password-file` (a file at mode 600, or `-` for stdin) instead. See
79
+ [the admin password guide](../guides/admin-password.md).
80
+
81
+ ## Security note for 0.18 houses: member names that collide
82
+
83
+ **What it allows.** A member is granted its tables through one prefix wildcard: `bob` holds
84
+ `mem.bob_*`. That prefix also matches the tables of any member whose name is `bob` plus an
85
+ underscore and more, such as `bob_smith`. So `bob` can **read and write** `bob_smith`'s
86
+ sessions. Mostly the longer name's tables are exposed, to the shorter name. When the longer
87
+ name's extra part starts one of the shorter name's table names, the reverse holds too:
88
+ `mem.bob_session_*` matches `bob`'s `bob_session_parts`. Checked on ClickHouse 26.7
89
+ (2026-10-22). Every memhouse release up to 0.18.11 created such members without a warning, so
90
+ an existing house may have a pair.
91
+
92
+ **Check your house, as its admin:**
93
+
94
+ ```sql
95
+ SELECT name FROM system.users ORDER BY name
96
+ ```
97
+
98
+ Look for one name that is another's plus `_`. On a schema-3 install, `memhouse doctor` run as
99
+ the shorter-named member names the pair too.
100
+
101
+ **What 0.20 does.** It refuses to create a member whose name collides (install and invite),
102
+ `memhouse doctor` names an existing pair, and `memhouse migrate --v3-shares` refuses to move
103
+ shares in such a house. memhouse cannot untangle an existing pair yet. If you find one, the
104
+ choices are in [Troubleshooting](../guides/troubleshooting.md#two-member-names-that-collide).
105
+ Both of them touch another person's data, so decide before acting.
106
+
107
+ ## What changes for you
108
+
109
+ ### Capture
110
+
111
+ - **Every row carries its store.** `host` is now sessions-model/host/v0: the first 32 hex
112
+ of a hash of the OS machine id, the same for every reader on the machine. Where no machine
113
+ id can be read, it is a random id kept on disk (`r-…`). `memhouse doctor` lists the 0.18
114
+ id under "host identity" as an alias of this machine.
115
+ - **The capture archive.** `<db>_archive.<m>_session_units` holds every captured byte;
116
+ `<m>_session_sources` holds the cursor of each source: its generation and how far it has been
117
+ read. Each read writes a manifest beside its units, so an emptied or shrunk source replays correctly
118
+ from the archive alone. A session file the editor deletes stays in the house, recorded
119
+ once as gone.
120
+ - **A session the editor deletes is recorded as gone only on the editor's own word.** On the
121
+ profile layout, a session or source the editor's store no longer lists is read once more, and
122
+ is recorded as gone (its records kept) only when the editor's reader answers that it is not
123
+ there. A session the list leaves out that still reads is captured as usual. In 0.20 this
124
+ covers the editors read through the harness-session-adapters package, Claude Code and Codex; the
125
+ others are read from their normalised logs, which are never swept (see Known issues), and
126
+ join when they move to the package. The Claude Code and Codex readers look a session up in
127
+ the store again, so a session whose folder is missing both when the store is listed and when
128
+ it is read is taken as gone; when it comes back it is read again in full, in a new
129
+ generation. Only a store whose list names none of the sessions the house holds of it (an
130
+ empty or unreadable store) is refused whole.
131
+ - **The shipper log says so.** After the pass line, a `vanish refused: <editor> <store>: …` line
132
+ names each refused store (and a session the list left out that still reads, or could not be
133
+ read), and `memhouse doctor` fails a line naming them until a later pass is clean. A
134
+ `vanish warning: …` line names a listed session's source the list left out that still reads;
135
+ doctor shows it and does not fail.
136
+ - **An emptied store needs one step.** A store whose every session was deleted lists nothing,
137
+ which is what an unreadable store looks like, so it is refused on every pass. If its
138
+ sessions were deleted, run the command the refusal names:
139
+ `memhouse doctor --confirm-empty-store <editor>:<store>` (the store path quoted when it has
140
+ spaces). It confirms the sessions the last pass refused (those recorded as gone already
141
+ replay without it). The next pass that finds the store there, its list still empty and no
142
+ other session held takes each as gone where the editor's reader says it is; a list that names
143
+ a session again, or a session held that it does not name, cancels the confirmation.
144
+ - At most 100 sessions a store are taken per pass; the rest follow on the next passes.
145
+ - **Normalisers.** The ten editors above write a normalised session log under
146
+ `$MEMHOUSE_HOME/session-logs/`, and the engine captures from it. Sub-agents are child
147
+ records, so a log grows by appending. Reading is bounded to 30 s a pass, and a store that
148
+ is busy or locked fails the pass rather than reading as fewer messages.
149
+ - **OpenCode is keyed by its database file** (`opencode.db`, `opencode-<channel>.db`, or
150
+ `OPENCODE_DB`), not its data folder (sessions-model §1).
151
+ - **Converted 0.18 rows dedupe against capture.** After the switch, each captured session
152
+ absorbs the converted rows of the same session, so nothing is counted twice. Rows whose
153
+ transcripts are gone stay, counted once.
154
+ - **A fork is billed once.** A fork that names its origin (agy, Copilot CLI) is checked against
155
+ it message by message, on content. Its usage that copies the origin's counts only in the
156
+ origin; a message the fork ran again is billed in the fork. `memhouse doctor` counts the forks
157
+ billed once and the links it could not verify. A fork that keeps no pointer to its origin
158
+ (OpenCode, VS Code, cursor-agent, Cursor, Goose) is found by its first reply: a session whose
159
+ first answer (at least 40 characters) is an earlier session's is checked against it the same
160
+ way. Two unrelated sessions whose first exchanges match word for word and token for token
161
+ cannot be told from a fork.
162
+ - **An adapter upgrade re-derives each session from its archive, and its first pass takes
163
+ longer.** The first pass after an adapter states a new version replays each of its sessions'
164
+ archived passes one by one under that version (harness-sessions-sql §8):
165
+ - every record it makes, current or a removed version, is written again at the new version;
166
+ - every key it no longer makes is deleted, a removed version included, except a key the new
167
+ version still holds back, which stays as last written.
168
+
169
+ A session with a single archived pass is refolded instead, which gives the same rows. The
170
+ upgrade pass takes longer the more passes a session's archive holds. The replay reads archived
171
+ units at most 32 MiB and 1,000 units a response, and a pass under an unchanged version is not
172
+ affected. On a lab ClickHouse reached through an SSH tunnel, with a synthetic store of
173
+ 50 sessions, the upgrade pass took:
174
+
175
+ | archived passes a session | without the re-derive (a 0.20 dev build) | 0.20 | 0.20, per 1,000 sessions |
176
+ |---|---|---|---|
177
+ | 4 | 0.6 s a session | 0.55 s | about 9 min |
178
+ | 12 | 0.5 s | 0.8 s | about 13 min |
179
+ | 24 | 0.5 s | 1.0 s | about 17 min |
180
+
181
+ The second column is a 0.20 development build's upgrade, which refolded only each session's
182
+ last state; at 4 passes the two are within noise. The last column scales the 50-session runs.
183
+ These are single runs, and round trips to the house dominate them, so read them as an order of
184
+ magnitude for planning, not a measurement of any real house.
185
+
186
+ ### Reading
187
+
188
+ - `memhouse q <name>` runs named queries over schema 3: `recent`, `session`, `window`,
189
+ `search`, `spend`, `models`, `tools`, `activity`, `hosts`, `coverage`, `removed` and more
190
+ (`memhouse q --list`). A session's ref names its store (`<harness>:<id>~…`); a bare id
191
+ still resolves when it is unique.
192
+ - `q window <ref>` reads around a search hit (`--at <message_id>`) or a moment (`--ts`); with
193
+ neither it opens at the session's first turn, so a ref from `q recent` reads as it is.
194
+ - `memhouse status`, `doctor`, `search`, `stats` and `resume`, the dashboard and the `/mem`
195
+ skills read schema 3 once an install is switched.
196
+ - The `/mem` skills install into Claude Code only. In Codex or another editor, an agent reads
197
+ the house with `memhouse q` and `memhouse room-sql`; `--help` and the install guide say so.
198
+ - `MEMHOUSE_URL` is the house, its port included; `MEMHOUSE_PORT` is this machine's dashboard
199
+ port. `--help`, the env file and the configuration guide say so.
200
+
201
+ ### Importing a claude.ai export
202
+
203
+ `memhouse import claude-ai <export>` stores a claude.ai data export in the house: the export's
204
+ part zips or their folder, an older single `.zip`, an unpacked export, or an archive folder. Each
205
+ conversation with any text becomes a `claude-ai` session with origin `import`, in one batch per
206
+ run.
207
+
208
+ - It is a dry run unless `--write`. A later export adds only the conversations the house lacks;
209
+ `--update` also replaces the ones that changed.
210
+ - `--list` shows the batches and `--undo <batch> --write` tombstones one.
211
+ - On schema 3 a conversation is a session in the service's own store, not a machine's.
212
+ - A run that stops after all its rows went in is finished by the next `--write`. One whose
213
+ insert stopped early leaves its batch `applying`, and the next run writes those sessions in its
214
+ own batch.
215
+
216
+ [Import a claude.ai export](../guides/import-claude-ai.md) has the details.
217
+
218
+ ### Sharing is narrower on schema 3
219
+
220
+ A share, full or scoped, is `SELECT` on exactly the member's six conversation tables
221
+ (`<m>_session_parts`, `<m>_session_usage`, `<m>_session_links`, `<m>_session_lifecycle`,
222
+ `<m>_calls`, `<m>_sessions`), never the archive.
223
+
224
+ - A scope is a row policy on each of the six. A `project=` or `folder=` scope lets no usage,
225
+ link or lifecycle row through (the `sessions` row carries the totals); a time scope lets no
226
+ link row through.
227
+ - A full share no longer reaches `meta`, `events` or tables added later. On the 0.18 rooms
228
+ it was `SELECT ON <m>_*`.
229
+ - A scope's row policies are written before any grant.
230
+ - `source=` is `harness=` on schema 3; `source=` still works.
231
+
232
+ `--v3-shares` moves the shares memhouse recorded. It refuses, changing nothing, when the
233
+ record and the grants ClickHouse holds disagree. [Share and revoke](../guides/share.md) has
234
+ the details.
235
+
236
+ ## Security
237
+
238
+ - **Reading an editor's store never writes into it.** On 0.18, the SQLite readers (Cursor,
239
+ Goose, OpenCode, Zed, Antigravity) could create `-wal` and `-shm` files in the editor's
240
+ folder: a WAL database opened read-only creates them, and they stay after close. A WAL
241
+ store with no `-wal` is now read immutable, and a rollback-journal store (Zed's) with a
242
+ plain read under its lock. 0.18 machines that ran those readers may have such files left
243
+ in the editors' folders. They are SQLite's own, and the editor uses or replaces them.
244
+ - **No reader reads a credential.** The usage path that read an editor's credentials is
245
+ gone. The cursor-agent normaliser never decodes credential-bearing steps or untyped
246
+ arguments, and redacts them from the stored model context too.
247
+ - **A share cannot reach the archive.** The capture archive is granted to its member alone
248
+ (`SELECT, INSERT`, without grant option), so no share can hand over the raw transcripts.
249
+ - **The share commands fail closed.** Every name that reaches a grant statement is checked
250
+ and quoted. `migrate --v3-shares` matches a member's tables and filters by exact name,
251
+ never by prefix, and stops on any read it cannot make. A scoped share needs the server to
252
+ prove that a row policy hides nothing from other readers. A 0.18 wildcard grant
253
+ (`<m>_*`) is taken back only from a reader who holds it: by `migrate --v3-shares`, or by a
254
+ member's revoke when no other member's name starts with theirs. A new share refuses while
255
+ the reader holds it, or while that cannot be read.
256
+ - **Members no longer receive SHOW ROW POLICIES.** On a shared house that privilege let a
257
+ member list other members' scoped shares, including their filters. Admins of existing houses
258
+ run `memhouse migrate --v3-shares` (or the statement below) to remove it from existing
259
+ members.
260
+
261
+ ```sql
262
+ -- for each member <m> of the house <db>; a house still on the 0.18 rooms too
263
+ REVOKE SHOW ROW POLICIES ON <db>.<m>_* FROM <m>;
264
+ ```
265
+
266
+ Sharing does not need it: `memhouse share --list` shows a share's scope from memhouse's own
267
+ record. Where `--v3-shares` refuses to move a member's shares (two names that collide, a grant
268
+ it did not record), it prints this statement instead.
269
+ - **Two member names that collide are refused.** See [the security note for 0.18
270
+ houses](#security-note-for-018-houses-member-names-that-collide) above.
271
+ - **The `/mem` skills pre-approve reads only.** Each skill pre-approves only the read
272
+ commands it teaches, drawn from `memhouse q`, `search`, `resume`, `room-sql`,
273
+ `sessions-query`, `status`, `whoami`, `stats`, `instance`, `doctor` and `share --list`.
274
+ Every other command asks first, including one chained to a read.
275
+ - **Joining always changes the invited password.** `install --env` changes it to a new one
276
+ only the member's machine knows, also when it runs without a terminal (as an agent runs
277
+ it). In a terminal it asks first. Only an explicit no, or `--keep-password`, keeps the
278
+ invited password, and the output then says the inviter still knows it. The invite file
279
+ and the invitation page now say the same, and both tell an agent not to open the file.
280
+ - **An admin password is never taken from the command line.** `--admin-password <value>`,
281
+ deprecated in 0.18.10, is refused: the command stops before doing anything, exits 2, and
282
+ names `MEMHOUSE_ADMIN_PASSWORD` and `--admin-password-file` instead, without printing the
283
+ value. `--admin-password-file` refuses a file that group or other users can read or
284
+ write (`chmod 600` fixes it); a container's secret mounts are the exception.
285
+ - **`setup` stores an administrator credential only on request.** Given a credential that
286
+ administers the server, `memhouse setup` writes nothing unless `--keep-admin` says so (a
287
+ terminal is asked, default no; `--yes` does not count). Such a password given as
288
+ `--password` is refused outright. Re-saving the credential already configured does not ask.
289
+ - **The dashboard requires a token.** The dashboard now requires a per-user token; a loopback
290
+ connection alone is no longer enough. Open it with `memhouse dashboard`.
291
+ - **The dashboard's port.** When it is in use, possibly by another account on this machine, the
292
+ dashboard takes the next free port up to 10 above and says which (`memhouse status` and
293
+ `memhouse dashboard` use it); a dashboard that cannot start is a warning from
294
+ `memhouse start`, not a failure.
295
+ - **Report a security issue to security@memhouse.io.** It is the public contact while the
296
+ repository is private (`SECURITY.md`), and npm shows it as the package's `bugs` address.
297
+
298
+ ## Fixed
299
+
300
+ - **One number for "sessions".** `memhouse instance` printed each table's row count beside its
301
+ name, which read as a second, different count of sessions: a table holds every version of a
302
+ session that is not merged yet. It now says rows, and that `status` and `stats` count the
303
+ sessions. `status`, `stats`, `q hosts` and `q machines` count the same live sessions.
304
+ - **`memhouse update` reads the registry npm installs from.** It asked registry.npmjs.org
305
+ whatever npm was configured with, so behind a mirror it reported "the registry did not
306
+ answer" and could not update. It now reads `npm config get registry` (or
307
+ `npm_config_registry`), prints which registry it read and why one could not be read, and no
308
+ longer suggests `--channel` when the registry itself is the problem. Mirror users: a
309
+ registry that needs a login is still read without one; install by hand there, then
310
+ `memhouse update --no-install`.
311
+ - **The package ships what a user needs, not the repository's rules.** `AGENTS.md` is now the
312
+ install and deploy guide for agents only. The conventions for working on memhouse (release
313
+ lines, the instruments, what the codebase learned the hard way) moved to `CONTRIBUTING.md`,
314
+ which stays in the repository. `memhouse/DESIGN.md`, `COMPETITION.md` and
315
+ `RESUME-AND-UPDATE.md` no longer ship. An agent reading files in an installed package found
316
+ repository rules there and took them for the user's.
317
+ - **One machine is one writer after the move.** Converted rows keep a machine's 0.18 id and
318
+ captured ones carry the new one; the machine now records the pair in its member's meta (at
319
+ `--v3-switch` and each capture pass), and status, `q machines`, `q hosts` and doctor read the
320
+ 0.18 id's rows as that machine. No row is rewritten.
321
+ - **The `/mem` skills follow the memhouse that runs them.** Only `memhouse update` refreshed
322
+ them, so an upgrade with `npm install -g` (or one behind a registry mirror, where `update`
323
+ cannot see the registry) left the 0.18 skills teaching the 0.18 rooms on a schema-3 house.
324
+ The first command after an upgrade, or the shipper's restart, now replaces every copy bound
325
+ to that install with its own version's skills, never one bound to another install and never
326
+ with an older version. `memhouse doctor` names any it could not refresh.
327
+ - **The `/mem` skills' pre-approved reads asked anyway.** The skills wrote each command with an
328
+ expansion in front of the verb, which no `allowed-tools` rule can match, so every read
329
+ prompted. They now write plain `memhouse …`, and those reads run without a prompt. They also
330
+ sent you to `SHOW DATABASES` for a share you received, which does not show it: a share is the
331
+ housemate's tables in your own database. It is found with `SHOW TABLES` (through
332
+ `memhouse room-sql`) and read with `memhouse q … --member <them>`.
333
+ - `memhouse room-sql "DESCRIBE sessions"` told a member it lacked the privilege to read its own
334
+ columns: only a name after `FROM`/`JOIN` was resolved, so the server was asked about a table
335
+ that is not the member's. The table a `DESCRIBE`, `DESC`, `SHOW CREATE` or `EXISTS` names is
336
+ now resolved too. Another member's tables stay closed.
337
+ - `status`, `doctor` and `stats` no longer call a house that answers "not connected" when
338
+ your tables are not where this install looks, and no longer print ClickHouse's
339
+ `UNKNOWN_TABLE` and the SQL behind it. They name the reason and the next step: the house
340
+ was moved to schema 3 (`memhouse migrate --v3-switch`), it was not moved yet, this install
341
+ holds an administrator credential with no rooms of its own, or there are no tables for you
342
+ in the configured house. `status --json` adds `reachable` and `rooms_problem`; `connected`
343
+ still means the house answered and its rooms could be read.
344
+ - A shipper keeps what it read when it started. After `memhouse migrate --v3-switch` or an
345
+ upgrade under a running shipper, `status` showed its failing passes as a warning and exited 0.
346
+ It now calls that shipper stale, says why (the env file changed, or the package was installed,
347
+ after it started), repeats `memhouse stop && memhouse start`, and exits 1 while its passes
348
+ fail. A shipper that re-read the env file itself (it adopts a moved house and a newer
349
+ credential) is not called stale. `status --json` adds `shipper.stale`.
350
+ - `memhouse instance` printed "schema 2" on a machine left on the 0.18 rooms after its house was
351
+ converted, as if all was well. It now asks the house what `status` asks and says the install
352
+ is stranded, with the fix. `memhouse q schema` with no house configured says `none` instead of
353
+ `2`.
354
+ - `memhouse invite` from an admin home with no install of its own minted 0.18 rooms in a
355
+ schema-3 house. It now detects the house, as install does.
356
+ - The invitation page `invite` writes beside the env file (`MEMHOUSE-INVITATION.md`) listed
357
+ the 0.18 rooms for every invite, and found the env file with `ls invite-*.env`, which matches
358
+ nothing written with `--out <dir>/<other-name>.env`. It now lists the tables the invite
359
+ created, by the house's layout, and names the env file the invite wrote.
360
+ - `memhouse migrate`'s admin steps (`--v3-prepare`, `--v3-convert`, `--v3-reconcile`,
361
+ `--v3-shares`) now take `MEMHOUSE_ADMIN_USER` and `MEMHOUSE_ADMIN_PASSWORD` (or `--admin-user`
362
+ and `--admin-password-file`), like the other admin commands. From an admin home with no install
363
+ of its own they acted as the default `memhouse_root` with no password, and reported "could not
364
+ list mem's tables". `MEMHOUSE_USER` and `MEMHOUSE_PASSWORD` still work for them in this
365
+ release, with a note (see Deprecated); with no admin credential at all a step refuses before it
366
+ connects and names the missing variables. An authentication failure is named as one, in
367
+ `migrate`, `doctor` and `--v3-switch`: `could not authenticate as '<user>' at <url>`.
368
+ - `memhouse deploy --local` stopped at the install on 0.20: only an admin may create the
369
+ member's archive tables. The superuser it has at hand now builds them.
370
+ - The self-hosted example's nightly backup (`examples/04-self-hosted-team`) now includes
371
+ `<db>_archive`.
372
+ - On schema 3, a machine with no Claude Code or Codex directory stopped every pass before the
373
+ other editors were read. It now ships whatever the normalisers and the bridge find.
374
+ - Gemini CLI tokens were stored as Gemini reports them. Gemini counts cached tokens inside the
375
+ input and thought tokens outside the output, so the house charged cached tokens twice (in
376
+ `input_tokens` and again as `cache_read_tokens`) and never charged thoughts. Gemini CLI rows
377
+ now use the house split: input without the cached tokens, output with the thoughts. Usage
378
+ records carry the two flags of sessions-model §2.4 (`input_includes_cache`,
379
+ `output_includes_reasoning`). A machine that ran a 0.20 pre-release re-derives its Gemini CLI
380
+ sessions on its first pass after the update.
381
+ - 0.18 stored Gemini CLI input with the cached tokens counted inside it, and stored the same
382
+ tokens again as cache reads, so they were charged twice. `--v3-convert` takes them out of
383
+ input (floor 0), and `--v3-reconcile` compares on the same rule. 0.18 never stored Gemini's
384
+ thought tokens, so converted Gemini CLI rows still leave them out: they cannot be recovered.
385
+ - **Zed tokens counted only each turn's last request.** The Zed normaliser took each turn's
386
+ `request_token_usage`, which holds only the turn's last request, and ignored the thread's own
387
+ total, `cumulative_token_usage`; in the test stores that was about a tenth of the total. A Zed
388
+ session's usage is now that total, as one per-session series (sessions-model §2.4 "No
389
+ overlap"), and no message carries usage. The log keeps the total as increments, so it still
390
+ only appends as a thread grows. Each increment is dated by the thread's save that first showed
391
+ it (Zed keeps no time per message or per request), so a thread memhouse reads late, or a
392
+ pre-release's thread re-derived after the update, has its whole total so far on the day of its
393
+ last save, not on the day of the read. A thread whose total is empty records no usage, even
394
+ when per-turn values exist: they are last-request snapshots, not a total.
395
+ - **Copilot CLI dropped usage that had no reply message.** A reply cut off by the output cap
396
+ leaves a `usage_record` and no `assistant.message`, and a compaction's own model call records
397
+ its tokens only on the compaction. Both are now usage rows of their own (keyed by the call's
398
+ `apiCallId`, or a short hash of it when it is VS Code's long opaque id, and
399
+ `compaction:<id>#1`), and so is a `usage_record` that has no `apiCallId` (keyed by its own
400
+ event id). A fork does not bill the ones it copied from its origin.
401
+ - **A Zed message this reader does not know no longer shifts the ids after it.** Zed's replies
402
+ have no ids of their own: each is the user message's id plus its position. An unknown entry
403
+ was skipped, so the replies after it took its position. It is now kept in its place as a
404
+ notice naming its kind (never its content), and an unknown part of a message is a notice too.
405
+ - **A Copilot CLI fork no longer claims its sibling.** A fork copies its origin's events,
406
+ including the origin's earlier "Forked this session into …" marker, which the fork read as a
407
+ fork of its own. Markers older than the session's own start are now the origin's history.
408
+ - A machine that ran a 0.20 pre-release re-derives its Zed and Copilot CLI sessions on its first
409
+ passes after the update: both normalisers' `VERSION` rose, so every session the editor still
410
+ holds is read again, and a changed log is re-derived at a higher rev. A session the editor has
411
+ since deleted keeps the rows it had: the archive holds the normalised log, not the editor's
412
+ store.
413
+ - `memhouse doctor` failed (exit 1) on an unpriced model alone, and told the reader to run
414
+ `memhouse update` even on the latest release, where update changes nothing and the gap stays.
415
+ An unpriced model is now a warning: doctor names `memhouse update` (and the release it would
416
+ install) only when a newer one exists, and otherwise says the cost stays unknown until a release
417
+ adds a price.
418
+
419
+ ## Known issues
420
+
421
+ - **A session file deleted from disk before the move leaves no removed mark.** 0.18 recorded no
422
+ deletions and the conversion reads only rows, so such a session keeps its converted rows and
423
+ reads as current. A deletion after the switch is recorded when a capture sees it. See
424
+ [Upgrade to 0.20](../guides/upgrade-to-0.20.md#sessions-deleted-before-the-move).
425
+ - **A capture pass that stops before it records its read, and finds the source changed when it
426
+ runs again, leaves the stopped read in the archive.** The archive keeps every read, and nothing
427
+ marks a read as finished, so the stopped read's units stay in their own generation. The
428
+ current state is right, and so is a refold. Only a replay from the archive alone, pass by pass,
429
+ which rebuilds the removed versions, shows the stopped read's records as removed versions that
430
+ the live passes never wrote. It waits for harness-sessions-sql to define a commit mark for a read (its
431
+ spec question 20).
432
+ - **A session whose capture state is lost after its log was rewritten repeats its time-less
433
+ records.** memhouse dates a record that has no time of its own (every Zed message, for one)
434
+ when it first captures it, and keeps that date: it is part of the record's key in `turns`. If
435
+ the house loses the session's `files` row in `<db>_archive` and the machine loses its local
436
+ capture cache as well, memhouse reads the log again from the start and looks those dates up in
437
+ the archive by position. When the log was rewritten after it was first captured (a change to
438
+ its history, a rename), the positions have moved: the records get new dates and appear twice in
439
+ `turns`, once under each key. The session's own totals and counts are not affected, and Zed's
440
+ token increments, which carry the thread's own time, keep their keys.
441
+ - **An emptied store keeps doctor failing until it is confirmed.** On the profile layout, a store
442
+ whose every session the editor deleted is refused on every pass (see Capture), and doctor names
443
+ it, until `memhouse doctor --confirm-empty-store <editor>:<store>` is run. Its records are not
444
+ touched meanwhile. A replay from the archive alone into a house whose store lists nothing
445
+ rebuilds only the sessions whose sources were all recorded gone before; the others wait for
446
+ that confirmation too.
447
+ - **Deleted sessions of the normalised editors are not recorded as gone on the profile layout.**
448
+ The normaliser never deletes a session log, and a log missing from the list cannot be told from
449
+ one that cannot be read, so those stores are never swept: such a session's records stay current
450
+ and its archive replays its last read, which is what recording it gone (policy kept) would
451
+ give, without the event in the archive.
452
+ - **A house replaced under the same name is swept late.** A machine keeps, per store, what the
453
+ last pass listed, and asks the house what it holds only when a session goes missing from the
454
+ list or the last sweep left something pending. A house restored from its archive under the same
455
+ name, with the machine's capture state kept, has its vanished sessions recorded only once a
456
+ listed session next goes missing. Removing the folder `$MEMHOUSE_HOME/state/derive-profile/stores/`
457
+ (this machine's per-store sweep state) makes the next pass ask. It also holds any empty-store
458
+ confirmation not yet used (`*.confirm.json`, `*.confirm.claimed`): removing it drops those too,
459
+ and an emptied store has to be confirmed again. Do not remove its parent, which also holds each
460
+ session's capture state and would make every session be read again in full.
461
+ - **A replay takes at most 100 sessions a store per pass.** A replay from the archive alone of a
462
+ store with more sessions than its list names (sessions deleted since) rebuilds them over
463
+ several passes.
464
+ - **A vanished session's edges can wait.** When a session leaves its store and the pass's
465
+ budget defers its edge recompute, no later pass revisits it, because it is no longer listed:
466
+ its edges are recomputed when its origin's rows next change. This only happens when an
467
+ inferred-fork harness runs out of nomination budget in that pass.
468
+ - **Run one `memhouse import claude-ai` (import or undo) per member at a time.** They take no
469
+ lock. Two at once can:
470
+ - split a new conversation's rows between the two batches, since both write it at the same
471
+ `rev`. The read-back then disagrees, and undoing one batch removes part of the
472
+ conversation. Undo both batches and import again;
473
+ - record a batch's status wrong. An undo can be recorded `applied`: re-running it puts the
474
+ status right while any of its rows are still live, with partial counts; otherwise the batch
475
+ reads `applied` with no live sessions. An import still filling its sessions can be recorded
476
+ `applied` before it finishes: if it then stops, the next `--write` fills its rows.
477
+ - **Profile rows written before the schema step retyped their table keep their old values.**
478
+ The pre-release draft of harness-sessions-sql v1 changed three column types. The schema step
479
+ (`memhouse ship --ensure-schema`, which `memhouse update` runs) changes the types of an
480
+ existing profile table. It casts the stored values to the new types; it does not work them out
481
+ again:
482
+ - `adapter_ver` keeps the number that encodes the version, now as text (`1000002001` for
483
+ `0.2.1`). The capture compares it as that version; a query returns `1000002001`.
484
+ - `child_stored` stays `true` where the link did not say, until the link is written again.
485
+ - `orig_sha256` keeps the old empty value (64 zero bytes) where it would now be null.
486
+
487
+ Rows written afterwards carry the version string, and a null `child_stored` where no statement
488
+ says. The two `<db>_archive` tables are the exception: a member cannot change their types, so
489
+ the capture keeps writing them in the old types, and `--ensure-schema` says so, until an admin
490
+ runs, for member `<m>`:
491
+
492
+ ```sql
493
+ ALTER TABLE <db>_archive.<m>_session_units MODIFY COLUMN adapter_ver LowCardinality(String), MODIFY COLUMN orig_sha256 Nullable(FixedString(64));
494
+ ALTER TABLE <db>_archive.<m>_session_units MODIFY COLUMN orig_sha256 REMOVE DEFAULT;
495
+ ALTER TABLE <db>_archive.<m>_session_sources MODIFY COLUMN adapter_ver LowCardinality(String);
496
+ ```
497
+
498
+ The first pass under an adapter's next version re-derives each of its sessions (see Capture),
499
+ which writes the conversation rows again with the version string and a null `child_stored`
500
+ where no statement says. The archived units are never rewritten, and the cursor rows it writes
501
+ again keep the old number until an admin runs the statements above.
502
+ - **A session that moved to another adapter is not re-derived on a later upgrade.** A harness
503
+ whose sessions moved from one adapter to another (the log adapter's bridge rows, then a session
504
+ adapter's) leaves sessions that hold rows of both. On the new adapter's next version, such a
505
+ session is only refolded: its current records are written at the new version, and its removed
506
+ versions stay at the version that wrote them.
507
+
508
+ Differences between the in-repo normalisers and the harness-session-adapters package (sessions-model
509
+ v1.2), found by running both over the same stores. Each is retired when memhouse reads these
510
+ editors through that package, after 0.20.
511
+
512
+ - **Devin token totals exclude abandoned branches in 0.20.** The messages of a branch the user
513
+ reverted, and their usage, are not stored, so their tokens are not counted. The package keeps
514
+ them, flagged `on_active_branch: false`, and will count them.
515
+ - Copilot CLI: reasoning effort is never read; assistant messages carry an empty `effort`,
516
+ though the call's usage record has `reasoningEffort`.
517
+ - Copilot CLI: a tool result whose call is not in the session file is dropped. Gemini CLI keeps
518
+ such a result.
519
+ - Copilot CLI: encrypted reasoning (`encryptedContent`, written by VS Code's Copilot engine) is
520
+ not kept as an opaque reasoning part.
521
+ - VS Code: a tool call with no id of its own gets one built from the response's base id rather
522
+ than from the message that holds the call (sessions-model §1 rule 6). Only the call's key
523
+ differs.
524
+ - OpenCode: a URL cut to `scheme://host` carries no `<redacted len=N>` marker (sessions-model
525
+ §4.2). Nothing leaks either way.
526
+ - OpenCode v2: usage is read with basis `net`, where sessions-model §2.4 leaves both usage flags
527
+ unknown. No number changes while the reasoning count is zero.
528
+ - Optional fields left empty: the session `mode` (Devin, VS Code, Copilot CLI); the call that
529
+ started a sub-agent, on the parent's side of the link (agy, cursor-agent, Gemini CLI, Zed); and
530
+ `source: injected` on the file a Zed mention expands into.
531
+
532
+ ## Deprecated
533
+
534
+ - **Passwords on the command line: `--password`, `--member-password`, `--to-password`.** They
535
+ still work, with a warning on every run, and are refused from the next minor release. Use
536
+ `MEMHOUSE_PASSWORD`, `MEMHOUSE_MEMBER_PASSWORD` or `MEMHOUSE_TO_PASSWORD`, or the new
537
+ `--password-file`, `--member-password-file` and `--to-password-file` (a file at mode 600,
538
+ or `-` for stdin). See [the admin password guide](../guides/admin-password.md).
539
+ - **`MEMHOUSE_USER` / `MEMHOUSE_PASSWORD` as the admin for `migrate --v3-*`.** They still work
540
+ when no admin variable is set, with a note on every run, and are not read for the admin steps
541
+ from the next minor release. Use `MEMHOUSE_ADMIN_USER` and `MEMHOUSE_ADMIN_PASSWORD`.
542
+ - **The 0.18 rooms layout.** 0.20 still writes it, for houses not yet converted. Its
543
+ removal version is not decided yet. New features are schema 3 only.
544
+ - **The 0.18 line:** 0.18 users who are not moving to 0.20 yet, upgrade to 0.18.12 or
545
+ later.
546
+
547
+ ## Deferred
548
+
549
+ - `memhouse reset` and `relocate` on schema 3 (both refuse there).
550
+ - Moving the editor adapters to the shared harness-skeleton package. That comes after 0.20;
551
+ until then the in-repo readers take bug fixes only.
552
+ - A gentar suite for the 0.20 upgrade and the schema-3 install, and a phase-2 run on a
553
+ release candidate.
554
+ - A full fresh-stack restore of example 04 on 0.20. Only the archive's part was checked.
555
+ - Known follow-ups:
556
+ - an adapter upgrade re-emits rows at the same rev and relies on insertion order to win;
557
+ - `text_len` counts UTF-16 units, not code points;
558
+ - normaliser catch-alls for filesystem errors;
559
+ - Cursor sub-agent composer ids are still read from the session's first line.
560
+
561
+ ## How this release was tested
562
+
563
+ - `npm test`: unit, v3 read and CLI, capture, dedupe, bridge, layout 3.2, the ten
564
+ normaliser suites, maintenance, no-credentials, and the sessions-model known-answer
565
+ vectors. CI runs it on ClickHouse 25.11 and 26.7, with the invite and
566
+ origin matrices and a headless e2e.
567
+ - `misc/v3-share-matrix.sh` (repository; in CI): what full, scoped, narrowed and revoked shares reach,
568
+ read with each person's own credential.
569
+ - `misc/upgrade-drill-0.20.sh` (repository), the upgrade drill (to be re-run on the release candidate; its
570
+ last full run predates the profile tables). It covers a 0.18.11
571
+ house with shares, and 0.20 install, join, fresh house and invites. It also covers grants the
572
+ move must refuse or leave alone: a schema-3 member's share, grants made by hand, a revoked
573
+ share that still holds a grant, a whole-database grant, and a colliding member name. It then
574
+ runs prepare, convert (including 0.18 OpenCode rows), reconcile, shares, switch and capture.
575
+ It ends with what each reader can see, doctor, search, `q removed`, and the known-answer
576
+ vectors on Linux.
577
+ - `misc/profile-lab/run-all.js` (repository) on a lab ClickHouse 26.7.4.58: twelve suites on the profile
578
+ capture engine, among them crashes and re-runs, resets, holds, forks billed once, the retype
579
+ of an older table to harness-sessions-sql v1's types, and an adapter upgrade's re-derive
580
+ against a twin captured by the new version.
581
+ - The harness-sessions-sql conformance kit (`misc/conformance/`, repository), kit v1 at harness-sessions-sql `96dcb3f`, on
582
+ ClickHouse: 22 pass, 2 not applicable to memhouse, and 1 fail, the crash-safe case of a
583
+ stopped read whose source changed before the re-run (Known issues).
584
+ - The four examples, run on a lab host against 0.20.0-dev.15. Each README quotes that run.
585
+ - The dev house has run the 0.20 dev builds since dev.1, capturing from
586
+ a maintainer's main machine.
587
+ - **Agent drill: owed.** The 0.18.11 notes promised a drill before the next minor
588
+ (`drills/DRILLS.md`, repository). It has not run for 0.20 yet. It must run,
589
+ or be explicitly waived, before this release is tagged.
590
+
591
+ ## Not live-tested
592
+
593
+ What 0.20 has not yet done on a real house or machine:
594
+
595
+ - **Converting a real house end to end.**
596
+ - `--v3-prepare`, `--v3-shares` and `--v3-switch` have run only in the upgrade drill, on a
597
+ made-up house on a lab ClickHouse.
598
+ - `--v3-convert` has also run, during development, on a copy of a real member's data restored
599
+ from a backup.
600
+ - No production house has been converted yet (see [A house in daily use](#a-house-in-daily-use-is-converted-on-its-own-schedule)).
601
+ - **Moving real shares.** `--v3-shares` has met only the drill's made-up readers and the CI
602
+ matrix's. No house with real readers has been moved.
603
+ - **Upgrading a busy machine in place.** The drill upgrades a machine with two made-up Claude
604
+ Code sessions. A 0.18.11 machine shipping many editors has not been upgraded and switched.
605
+ - **The normalisers on real stores outside one Mac.** They read live editor stores only on a
606
+ maintainer's main machine (macOS), into the dev house. On Linux they ran against fixtures, and
607
+ Windows is untested: CI runs on Ubuntu only.
608
+ - **OpenCode channel databases** (`opencode-<channel>.db`, `OPENCODE_DB`): fixtures and the
609
+ conversion drill only. The machine that ran 0.20 has the default database alone.
610
+ - **ClickHouse Cloud.** The examples ran on a lab ClickHouse 26.7 and the self-hosted compose
611
+ stack.
612
+ - **Example 04's full restore** onto a fresh stack. Only the archive's part was checked on 0.20.
613
+ - **The colliding-names guard on every path.** It runs in install (as admin) and invite.
614
+ `--print-sql` prints the statements without checking, and `deploy --local` makes its single
615
+ member in a new house.
616
+ - **An agent drill** (`drills/DRILLS.md`, repository): owed, as said above.
617
+ - **gentar**: no 0.20 suite yet, and no bench run on a release candidate.
618
+
619
+ ## Upgrade
620
+
621
+ ```bash
622
+ memhouse update # 0.20; the house stays on the 0.18 rooms
623
+ memhouse status # every line a check mark
624
+ ```
625
+
626
+ Then, once the admin has converted your member: `memhouse migrate --v3-switch`, `memhouse
627
+ stop && memhouse start`, and `memhouse doctor`.