@treeseed/dsh-chapters 0.1.24 → 0.2.0

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 (103) hide show
  1. package/README.md +2 -1
  2. package/cordis.patch.yml +49 -1
  3. package/docs/architecture.md +1 -496
  4. package/docs/backlog.md +234 -4
  5. package/docs/contract.md +127 -1
  6. package/docs/development.md +248 -12
  7. package/docs/host-compaction-seam.md +108 -0
  8. package/docs/knowledge-repo.md +532 -16
  9. package/docs/project-state.md +323 -0
  10. package/docs/verify.md +217 -0
  11. package/lib/archive.d.ts +49 -1
  12. package/lib/archive.js +56 -2
  13. package/lib/archive.js.map +1 -1
  14. package/lib/arrival.js +24 -10
  15. package/lib/arrival.js.map +1 -1
  16. package/lib/attach.d.ts +46 -0
  17. package/lib/attach.js +120 -0
  18. package/lib/attach.js.map +1 -0
  19. package/lib/auto-save.d.ts +74 -0
  20. package/lib/auto-save.js +162 -26
  21. package/lib/auto-save.js.map +1 -1
  22. package/lib/client.js +413 -11
  23. package/lib/client.js.map +1 -1
  24. package/lib/commands.d.ts +8 -1
  25. package/lib/commands.js +135 -7
  26. package/lib/commands.js.map +1 -1
  27. package/lib/compose.js +25 -6
  28. package/lib/compose.js.map +1 -1
  29. package/lib/continue-core.d.ts +24 -1
  30. package/lib/continue-core.js +41 -9
  31. package/lib/continue-core.js.map +1 -1
  32. package/lib/diag.d.ts +47 -0
  33. package/lib/diag.js +141 -0
  34. package/lib/diag.js.map +1 -0
  35. package/lib/engine-core.d.ts +51 -1
  36. package/lib/engine-core.js +63 -7
  37. package/lib/engine-core.js.map +1 -1
  38. package/lib/engine.d.ts +93 -0
  39. package/lib/engine.js +477 -36
  40. package/lib/engine.js.map +1 -1
  41. package/lib/enrich-queue.js +28 -2
  42. package/lib/enrich-queue.js.map +1 -1
  43. package/lib/enrich-wire.d.ts +92 -2
  44. package/lib/enrich-wire.js +135 -17
  45. package/lib/enrich-wire.js.map +1 -1
  46. package/lib/enrich.d.ts +27 -2
  47. package/lib/enrich.js +114 -18
  48. package/lib/enrich.js.map +1 -1
  49. package/lib/index.d.ts +51 -0
  50. package/lib/index.js +180 -2
  51. package/lib/index.js.map +1 -1
  52. package/lib/indexing.d.ts +7 -0
  53. package/lib/indexing.js.map +1 -1
  54. package/lib/injections.d.ts +1 -0
  55. package/lib/injections.js +38 -0
  56. package/lib/injections.js.map +1 -1
  57. package/lib/last-resort.d.ts +24 -0
  58. package/lib/last-resort.js +27 -0
  59. package/lib/last-resort.js.map +1 -0
  60. package/lib/prompt.d.ts +130 -0
  61. package/lib/prompt.js +255 -0
  62. package/lib/prompt.js.map +1 -0
  63. package/lib/registry.d.ts +39 -1
  64. package/lib/registry.js +76 -9
  65. package/lib/registry.js.map +1 -1
  66. package/lib/render.d.ts +31 -3
  67. package/lib/render.js +75 -3
  68. package/lib/render.js.map +1 -1
  69. package/lib/repair-run.d.ts +49 -0
  70. package/lib/repair-run.js +423 -0
  71. package/lib/repair-run.js.map +1 -0
  72. package/lib/repair.d.ts +200 -0
  73. package/lib/repair.js +354 -0
  74. package/lib/repair.js.map +1 -0
  75. package/lib/rules-commands.d.ts +22 -0
  76. package/lib/rules-commands.js +177 -20
  77. package/lib/rules-commands.js.map +1 -1
  78. package/lib/rules.d.ts +184 -7
  79. package/lib/rules.js +312 -27
  80. package/lib/rules.js.map +1 -1
  81. package/lib/save-core.d.ts +31 -1
  82. package/lib/save-core.js +153 -17
  83. package/lib/save-core.js.map +1 -1
  84. package/lib/session-rules.d.ts +222 -0
  85. package/lib/session-rules.js +454 -0
  86. package/lib/session-rules.js.map +1 -0
  87. package/lib/store.d.ts +22 -1
  88. package/lib/store.js +94 -17
  89. package/lib/store.js.map +1 -1
  90. package/lib/sync.js +13 -1
  91. package/lib/sync.js.map +1 -1
  92. package/lib/tools.d.ts +14 -0
  93. package/lib/tools.js +311 -32
  94. package/lib/tools.js.map +1 -1
  95. package/lib/types.d.ts +12 -1
  96. package/lib/types.js.map +1 -1
  97. package/lib/version.d.ts +9 -0
  98. package/lib/version.js +10 -0
  99. package/lib/version.js.map +1 -0
  100. package/lib/warnings.d.ts +10 -0
  101. package/lib/warnings.js +18 -0
  102. package/lib/warnings.js.map +1 -0
  103. package/package.json +9 -3
@@ -0,0 +1,454 @@
1
+ /**
2
+ * #50 §7.5 — the session tier's capture path.
3
+ *
4
+ * Two speeds, deliberately (this is §6.2's single-slot constraint applied to
5
+ * rules, not a new policy):
6
+ *
7
+ * - **marker**: `ADD RULE: <text>` on a line of a HUMAN message becomes an
8
+ * active session rule at the existing `turn/end` boundary — deterministic,
9
+ * zero model tokens (§1 invariant 1), the body is the human's own words.
10
+ * - **enrichment** (src/enrich-wire.ts): idle-batched, default off, and only
11
+ * ever activates behind a verbatim quote of human bytes.
12
+ *
13
+ * Session rules are **files, not registry rows**: their activation derives from
14
+ * the ancestor path (§7.5), so they need no record to resolve status, no linked
15
+ * project (an unlinked checkout still gets its rules — the whole point of the
16
+ * tier), and no fact in a pool-synced curation file. The registry keeps holding
17
+ * the shared tier's pre-sync records exactly as before.
18
+ *
19
+ * Idempotent by body: repeated `turn/end`s on a continuable child, and the
20
+ * after-loss re-derivation walk over the durable log, both FILL GAPS and never
21
+ * duplicate — the same posture `rehydrateStoreFromMirror` takes.
22
+ *
23
+ * Pure module: events and paths in, files out. The engine listener mounts it.
24
+ */
25
+ import fs from 'node:fs';
26
+ import path from 'node:path';
27
+ import { isHumanAuthored, stripReminderSpans } from "./injections.js";
28
+ import { turnSpanOf } from "./signature.js";
29
+ import { buildRulesSection, collectStoreRules, parseRuleFile, parseRuleMarkers, renderRuleFile, ruleSlug, } from "./rules.js";
30
+ import { redactText } from "./redact.js";
31
+ const messageText = (data) => {
32
+ const message = (data.message ?? data);
33
+ const content = Array.isArray(message.content) ? message.content : [];
34
+ return content.filter((b) => b.type === 'text').map((b) => b.text ?? '').join('\n');
35
+ };
36
+ /**
37
+ * The markers a turn's HUMAN messages contain. Two screens, both load-bearing:
38
+ *
39
+ * - `isHumanAuthored` — the same shared predicate signatures use (r28: injected
40
+ * context moves chapter boundaries and would here too). A host-injected
41
+ * instruction file that happens to contain a line starting with the marker is
42
+ * NOT a rule request.
43
+ * - `stripReminderSpans` — `<system-reminder>` spans ride INSIDE human
44
+ * messages. The human's own lines survive; the injected span does not (§15
45
+ * amendment, "host-injected context is not conversation").
46
+ */
47
+ export function humanRuleMarkers(events, prefix) {
48
+ const out = [];
49
+ const seen = new Set();
50
+ for (const ev of events) {
51
+ if (ev.type !== 'user/message')
52
+ continue;
53
+ const data = (ev.data ?? {});
54
+ if (!isHumanAuthored(data))
55
+ continue;
56
+ const text = stripReminderSpans(messageText(data)).text;
57
+ for (const body of parseRuleMarkers(text, prefix)) {
58
+ if (seen.has(body))
59
+ continue;
60
+ seen.add(body);
61
+ out.push({ body, seq: ev.seq });
62
+ }
63
+ }
64
+ return out;
65
+ }
66
+ /**
67
+ * The next free number in the SHARED per-machine sequence. Both tiers author
68
+ * into `rules/<harness>/`, and ids are `<harness>/<NNN>`, so a session rule must
69
+ * not take a number a project rule already holds — neither the registry's (pre-
70
+ * sync) records' nor the disk's (a killed write leaves a file without a record;
71
+ * the r39 walk-past rule). Numbers are allocated, never scanned-for-content.
72
+ */
73
+ export function nextRuleNumber(opts) {
74
+ const used = new Set();
75
+ const tail = new RegExp(`^${opts.harnessId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/(\\d{3})$`);
76
+ for (const [id] of opts.records) {
77
+ const m = tail.exec(id);
78
+ if (m !== null)
79
+ used.add(Number(m[1]));
80
+ }
81
+ if (fs.existsSync(opts.rulesDir)) {
82
+ for (const f of fs.readdirSync(opts.rulesDir)) {
83
+ const m = /^(\d{3})-/.exec(f);
84
+ if (m !== null)
85
+ used.add(Number(m[1]));
86
+ }
87
+ }
88
+ let n = 1;
89
+ while (used.has(n))
90
+ n += 1;
91
+ return n;
92
+ }
93
+ const norm = (s) => s.toLowerCase().replace(/\s+/g, ' ').trim();
94
+ /**
95
+ * Capture this turn's markers. Returns the rule ids written (empty when nothing
96
+ * matched), and never throws into the session — the caller contains failures the
97
+ * way the signature listener does.
98
+ */
99
+ export function captureSessionRules(deps) {
100
+ const markers = humanRuleMarkers(deps.events, deps.prefix);
101
+ if (markers.length === 0)
102
+ return { written: [], skipped: 0 };
103
+ const rulesDir = path.join(deps.storeDir, 'rules', deps.harnessId);
104
+ // dedupe against what is ALREADY on disk for this machine (§7.5 idempotence):
105
+ // a body captured last turn is not captured twice, and a re-derivation pass
106
+ // over the log fills only the gaps.
107
+ const existing = new Set();
108
+ if (fs.existsSync(rulesDir)) {
109
+ for (const f of fs.readdirSync(rulesDir)) {
110
+ if (!f.endsWith('.md'))
111
+ continue;
112
+ // the SAME parser the collectors use — one definition of a rule body,
113
+ // never a second hand-rolled frontmatter split that can drift from it.
114
+ const parsed = parseRuleFile(fs.readFileSync(path.join(rulesDir, f), 'utf8'));
115
+ if (parsed !== null)
116
+ existing.add(norm(parsed.body));
117
+ }
118
+ }
119
+ let written = 0;
120
+ const ids = [];
121
+ for (const m of markers) {
122
+ if (existing.has(norm(m.body)))
123
+ continue;
124
+ // §9 applies to rule bodies like any other archived bytes: a captured rule
125
+ // can carry a credential, and this tier is local but still a file on disk.
126
+ const body = redactText(m.body).text;
127
+ const number = nextRuleNumber({ records: deps.records, harnessId: deps.harnessId, rulesDir });
128
+ const title = body.split(/[.!?\n]/)[0].trim().slice(0, 60);
129
+ const name = `${String(number).padStart(3, '0')}-${ruleSlug('general', title)}.md`;
130
+ fs.mkdirSync(rulesDir, { recursive: true });
131
+ const file = path.join(rulesDir, name);
132
+ // write-once: the file is created complete, never edited in place (§1.3)
133
+ fs.writeFileSync(file, renderRuleFile({
134
+ number,
135
+ category: 'general',
136
+ title,
137
+ sourceSession: deps.sessionId,
138
+ at: (deps.now !== undefined ? deps.now() : new Date().toISOString()),
139
+ body: `${body}\n`,
140
+ scope: 'session',
141
+ session: deps.sessionId,
142
+ derivedFrom: 'marker',
143
+ }), { flag: 'wx' });
144
+ ids.push(`${deps.harnessId}/${String(number).padStart(3, '0')}`);
145
+ existing.add(norm(body));
146
+ written += 1;
147
+ }
148
+ return { written: ids, skipped: markers.length - written };
149
+ }
150
+ /**
151
+ * The current session's ancestor path, root-first — the async mirror of
152
+ * `registry.ancestorPath`, which takes a synchronous getter the realm does not
153
+ * have. §7.5 activation is "this rule's session is on MY path", so every render
154
+ * site needs this walk; a missing parent stops the walk instead of throwing,
155
+ * because a rules render must never break a compaction (the same containment
156
+ * the notice path uses).
157
+ */
158
+ export async function ancestorSessionIds(get, sessionId, maxDepth = 1000) {
159
+ const out = [];
160
+ const seen = new Set();
161
+ let cursor = sessionId;
162
+ while (cursor !== undefined && !seen.has(cursor) && seen.size < maxDepth) {
163
+ seen.add(cursor);
164
+ out.push(cursor);
165
+ const state = await get(cursor);
166
+ if (state === undefined)
167
+ break;
168
+ cursor = state.parentSession ?? undefined;
169
+ }
170
+ return out.reverse();
171
+ }
172
+ /** One mined rule candidate, as the enrichment pass proposes it (§7.5). */
173
+ /**
174
+ * Rule ancestry crosses BOTH lineage edges (#52, user decision 2026-10-21).
175
+ *
176
+ * Our registry is the sole authority on CONTINUATION lineage and deliberately
177
+ * ignores the kernel header's `parentSession` — right for chapter numbering and
178
+ * the TOC, and it left a whole session shape uncovered: a delegated subagent is an
179
+ * unrelated root by that view, so it inherited no activated rules from ANY door.
180
+ * The rule tier now walks both edges, READ-ONLY, writing nothing:
181
+ *
182
+ * registry parentSession (ours) ∪ header parentSession (the host's)
183
+ *
184
+ * transitively, so a subagent of a continuation child of a delegating parent sees
185
+ * the whole chain — and still walks UP only, which keeps the direction one-way: a
186
+ * child's inferences never govern the session that delegated them. Two bounds,
187
+ * because this runs inside a head rebuild: an expansion cap, and a reader that
188
+ * throws contributes no edge — a rule walk must never break a turn.
189
+ */
190
+ export const RULE_LINEAGE_MAX_EXPANSIONS = 64;
191
+ export async function ruleAncestorSet(opts) {
192
+ const cap = opts.maxExpansions ?? RULE_LINEAGE_MAX_EXPANSIONS;
193
+ const ancestors = new Set();
194
+ for (const s of opts.seeds)
195
+ if (s !== '')
196
+ ancestors.add(s);
197
+ const queue = [...ancestors];
198
+ let expansions = 0;
199
+ let truncated = false;
200
+ while (queue.length > 0) {
201
+ if (expansions >= cap) {
202
+ truncated = true;
203
+ break;
204
+ }
205
+ const id = queue.shift();
206
+ expansions += 1;
207
+ const edges = [];
208
+ if (opts.parentOf !== undefined) {
209
+ try {
210
+ const p = await opts.parentOf(id);
211
+ if (typeof p === 'string' && p !== '')
212
+ edges.push(p);
213
+ }
214
+ catch { /* unreadable header ⇒ not an ancestor */ }
215
+ }
216
+ if (opts.registryAncestors !== undefined) {
217
+ try {
218
+ edges.push(...(await opts.registryAncestors(id)));
219
+ }
220
+ catch { /* shrink, never fail */ }
221
+ }
222
+ for (const e of edges)
223
+ if (e !== '' && !ancestors.has(e)) {
224
+ ancestors.add(e);
225
+ queue.push(e);
226
+ }
227
+ }
228
+ return { ancestors, truncated };
229
+ }
230
+ /**
231
+ * A `parentOf` from whatever session views this plane can reach: a live agent
232
+ * handle first, then `sessionQuery`'s durable headers. undefined when NEITHER
233
+ * exists — which degrades the walk to registry edges only.
234
+ *
235
+ * Cordis `ctx.get` THROWS for an unregistered service and a plugin plane may have
236
+ * neither, so every lookup is guarded (the #43 lesson: a strict host makes a
237
+ * plausible service name throw).
238
+ */
239
+ export function makeHeaderParentReader(get) {
240
+ if (get === undefined)
241
+ return undefined;
242
+ // `agents` is a SERVICE: ctx.get('agents').get(id) hands back the live handle —
243
+ // the same two-step auto-save uses, and the only way to read a session other
244
+ // than the one currently rendering.
245
+ const liveParent = (id) => {
246
+ try {
247
+ const svc = get('agents');
248
+ const handle = svc?.get?.(id);
249
+ const session = handle?.agent?.session ?? handle?.session;
250
+ const p = session?.header?.parentSession;
251
+ return typeof p === 'string' && p !== '' ? p : null;
252
+ }
253
+ catch {
254
+ return null;
255
+ }
256
+ };
257
+ let durable = null;
258
+ const durableParent = async (id) => {
259
+ if (durable === null) {
260
+ try {
261
+ const sq = get('sessionQuery');
262
+ if (sq?.listSessions === undefined)
263
+ return null;
264
+ const m = new Map();
265
+ for (const r of await sq.listSessions() ?? []) {
266
+ const sid = r?.header?.id;
267
+ if (typeof sid === 'string' && sid !== '') {
268
+ const p = r.header?.parentSession;
269
+ m.set(sid, typeof p === 'string' && p !== '' ? p : null);
270
+ }
271
+ }
272
+ durable = m;
273
+ }
274
+ catch {
275
+ return null;
276
+ }
277
+ }
278
+ return durable.get(id) ?? null;
279
+ };
280
+ return async (id) => {
281
+ // live handle first: it is the current session's own edge, and the freshest
282
+ // answer for any session still open in this process
283
+ const live = liveParent(id);
284
+ if (live !== null)
285
+ return live;
286
+ return durableParent(id);
287
+ };
288
+ }
289
+ const normQuote = (t) => t.toLowerCase().replace(/\s+/g, ' ').replace(/[\u2018\u2019]/g, "'").replace(/[\u201c\u201d]/g, '"').trim();
290
+ /**
291
+ * THE verbatim-quote gate (§7.5). A mined rule activates only when its quote is
292
+ * the human's own words, present verbatim (whitespace-normalized) in the
293
+ * chapter's USER text. Assistant prose is deliberately NOT admissible evidence:
294
+ * the model restating an instruction it was given is the amplification path, and
295
+ * a quote it invented is a guess wearing a citation.
296
+ */
297
+ export function quoteIsHuman(humanText, quote) {
298
+ const q = normQuote(quote);
299
+ if (q.length < 3)
300
+ return false;
301
+ return normQuote(humanText).includes(q);
302
+ }
303
+ /**
304
+ * Write the mined candidates. Each records its warrant (`stated` / `inferred`) and
305
+ * the chapter it came from; whether it governs on arrival is the `autoActivate`
306
+ * POLICY, not an evidence test (§7.5, amended 2026-10-21 — the tier's premise is
307
+ * that the human may not have known to say the rule). Deduped by normalized body
308
+ * against what the machine already holds, so re-running the ladder (or a
309
+ * chapter annotated by a newer model) fills gaps and never duplicates.
310
+ */
311
+ export function captureMinedRules(deps) {
312
+ const rulesDir = path.join(deps.storeDir, 'rules', deps.harnessId);
313
+ const existing = new Set();
314
+ for (const r of collectStoreRules(deps.storeDir, deps.harnessId))
315
+ existing.add(norm(r.body));
316
+ const written = [];
317
+ let stated = 0;
318
+ let inferred = 0;
319
+ let pending = 0;
320
+ const cap = Math.max(0, deps.maxPerChapter);
321
+ for (const m of deps.rules) {
322
+ // the cap counts rules WRITTEN, not candidates examined: a duplicate or an
323
+ // over-short candidate must not eat a chapter's budget
324
+ if (written.length >= cap)
325
+ break;
326
+ const body = redactText(m.text.trim().replace(/\s+/g, ' ')).text;
327
+ if (body.length < 10 || existing.has(norm(body)))
328
+ continue;
329
+ // The quote check CLASSIFIES, it does not authorize (§7.5, amended
330
+ // 2026-10-21): `stated` = a human sentence is behind this, `inferred` = the
331
+ // model judged it useful and nobody said it. Both govern unless the plugin is
332
+ // configured to hold mined rules for review, and the difference is SHOWN in
333
+ // the rendered rule line — which is where a judgement call belongs.
334
+ const basis = quoteIsHuman(deps.humanText, m.quote) ? 'stated' : 'inferred';
335
+ const reviewOnly = !deps.autoActivate;
336
+ const number = nextRuleNumber({ records: deps.records, harnessId: deps.harnessId, rulesDir });
337
+ const category = (m.category || 'general').toLowerCase().replace(/[^a-z0-9-]/g, '').slice(0, 24) || 'general';
338
+ const title = body.split(/[.!?]/)[0].trim().slice(0, 60);
339
+ const name = `${String(number).padStart(3, '0')}-${ruleSlug(category, title)}.md`;
340
+ fs.mkdirSync(rulesDir, { recursive: true });
341
+ fs.writeFileSync(path.join(rulesDir, name), renderRuleFile({
342
+ number, category, title, sourceSession: deps.sessionId,
343
+ at: (deps.now !== undefined ? deps.now() : new Date().toISOString()),
344
+ body: `${body}\n`,
345
+ scope: 'session', session: deps.sessionId, derivedFrom: 'model',
346
+ reviewOnly, basis, chapter: deps.chapterNumber,
347
+ }), { flag: 'wx' });
348
+ written.push(`${deps.harnessId}/${String(number).padStart(3, '0')}`);
349
+ existing.add(norm(body));
350
+ if (basis === 'stated')
351
+ stated += 1;
352
+ else
353
+ inferred += 1;
354
+ if (reviewOnly)
355
+ pending += 1;
356
+ }
357
+ return { ids: written, stated, inferred, pending };
358
+ }
359
+ /** Turn a task description into the relevance term set §7.4 selects on — the same
360
+ * normalization the rule-term index uses, so a query and a rule body meet in one
361
+ * vocabulary instead of two. */
362
+ export function queryTopics(query) {
363
+ const out = [];
364
+ for (const tok of query.toLowerCase().split(/[^a-z0-9]+/)) {
365
+ if (tok.length < 3 || tok.length > 28 || out.includes(tok))
366
+ continue;
367
+ out.push(tok);
368
+ }
369
+ return out.slice(0, 40);
370
+ }
371
+ /**
372
+ * THE one rules renderer for a rebuilt head. Both doors (continue / fork) and the
373
+ * compaction checkpoint call this and nothing else, so the same session can never
374
+ * see two different rule sets depending on which path rebuilt its head — the same
375
+ * one-builder discipline as `search.rankEntries` and `deterministicCommand`.
376
+ *
377
+ * Relevance topics come from the session’s OWN stored turn signatures:
378
+ * deterministic, already on disk, zero model tokens (§1 invariant 1).
379
+ */
380
+ export async function renderRulesBlock(opts) {
381
+ const walk = opts.includeSession
382
+ ? await ruleAncestorSet({
383
+ seeds: [opts.sessionId],
384
+ registryAncestors: async (id) => await ancestorSessionIds(async (s) => await opts.store.get(s), id),
385
+ ...(opts.parentOf !== undefined ? { parentOf: opts.parentOf } : {}),
386
+ })
387
+ : { ancestors: new Set(), truncated: false };
388
+ const ancestors = walk.ancestors;
389
+ const state = opts.relevance ? await opts.store.get(opts.sessionId) : undefined;
390
+ const topics = opts.topicsOverride !== undefined && opts.topicsOverride.length > 0
391
+ ? opts.topicsOverride
392
+ : opts.relevance && state !== undefined
393
+ ? state.collections.slice(-6).flatMap((c) => c.terms).slice(0, 40)
394
+ : undefined;
395
+ return buildRulesSection(opts.mirrorDir, {
396
+ projectKey: opts.projectKey,
397
+ harnessId: opts.harnessId,
398
+ budgetTokens: opts.budgetTokens,
399
+ projectCap: opts.projectCap,
400
+ sessionCap: opts.sessionCap,
401
+ ...(opts.includeSession ? { storeDir: opts.storeDir, ancestorSessions: ancestors } : {}),
402
+ ...(topics !== undefined ? { relevanceTopics: topics } : {}),
403
+ ...(walk.truncated ? { lineageTruncated: true } : {}),
404
+ });
405
+ }
406
+ /**
407
+ * The `turn/end` listener (the same boundary the topic signatures collect on —
408
+ * §7.5: this extends the existing door, it is not a sixth archive moment).
409
+ *
410
+ * Never-break containment: every failure lands in `warn`, because a rule that
411
+ * could not be written must not cost a user their turn.
412
+ */
413
+ export function makeRuleCaptureListener(deps) {
414
+ const chains = new Map();
415
+ return (session, event) => {
416
+ try {
417
+ if (!deps.enabled())
418
+ return;
419
+ if (event?.type !== 'turn/end')
420
+ return;
421
+ const id = session?.id;
422
+ const events = session?.snapshotEvents?.();
423
+ const cwd = session?.header?.cwd;
424
+ const endSeq = event.seq;
425
+ if (id === undefined || events === undefined || cwd === undefined || endSeq === undefined)
426
+ return;
427
+ const span = turnSpanOf(events, endSeq);
428
+ if (span.length === 0)
429
+ return;
430
+ // cheap pre-screen OUTSIDE the async chain: no marker in this turn is the
431
+ // overwhelmingly common case, and it must cost one regex over one message.
432
+ if (humanRuleMarkers(span, deps.prefix()).length === 0)
433
+ return;
434
+ const job = () => deps.store()
435
+ .then(({ store }) => captureSessionRules({
436
+ storeDir: deps.storeRoot(cwd),
437
+ harnessId: deps.harnessId(),
438
+ sessionId: id,
439
+ records: store.rules(),
440
+ prefix: deps.prefix(),
441
+ events: span,
442
+ }))
443
+ .then((res) => { if (res.written.length > 0)
444
+ deps.onCaptured?.(id, res.written); })
445
+ .catch(deps.warn);
446
+ const prev = chains.get(id);
447
+ void (prev === undefined ? job() : prev.then(job)).finally(() => chains.delete(id));
448
+ }
449
+ catch (error) {
450
+ deps.warn(error);
451
+ }
452
+ };
453
+ }
454
+ //# sourceMappingURL=session-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-rules.js","sourceRoot":"","sources":["../src/session-rules.ts"],"names":[],"mappings":"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"}
package/lib/store.d.ts CHANGED
@@ -50,6 +50,12 @@ export interface DomainLike {
50
50
  put(key: string, value: {
51
51
  value: string;
52
52
  }): Promise<void>;
53
+ /** Row removal (host table verb — dsh-storage-domain `delete(key)`). #71: a
54
+ * `model clear` must RESTORE the config fallback, which a stored value cannot
55
+ * express — `''` is the deliberate OFF state, deletion is the ABSENT state.
56
+ * Optional only so pre-delete fakes keep compiling; the store refuses loudly
57
+ * at the call site when the host table lacks it (every supported host has it). */
58
+ delete?(key: string): Promise<boolean>;
53
59
  entries(): IterableIterator<[string, {
54
60
  value: string;
55
61
  }]>;
@@ -67,7 +73,19 @@ export interface DomainLike {
67
73
  export type ProjectRecord = import('./sync.ts').ProjectRecord;
68
74
  export interface RegistryStore {
69
75
  get(sessionId: string): Promise<SessionState>;
70
- put(sessionId: string, state: SessionState): Promise<void>;
76
+ /**
77
+ * A whole-row write, guarded. `expectedRev` is the `rev` of the row the caller READ; a mismatch means
78
+ * another door committed since, and the write is REFUSED with numbers rather than clobbering it.
79
+ * Omit it only where a blind put is intended (the boot witness probe, a rollback to a captured row).
80
+ */
81
+ put(sessionId: string, state: SessionState, expectedRev?: number): Promise<void>;
82
+ /**
83
+ * The ONLY sanctioned way to change a session row (#59): one serialized section per session, the fn
84
+ * runs against a FRESH read, and the write is rev-checked. A door that holds work across an await
85
+ * passes a function, never a snapshot — that is what stopped a stale `put` from erasing another
86
+ * door's committed state. Nested calls for the same session are refused rather than deadlocked.
87
+ */
88
+ mutate(sessionId: string, fn: (state: SessionState) => SessionState | Promise<SessionState>): Promise<SessionState>;
71
89
  /** Knowledge projects linked for this workspace (record §8). Live view. */
72
90
  projects(): IterableIterator<[string, ProjectRecord]>;
73
91
  /** All session states in the domain (for collection publishing, §5). */
@@ -75,6 +93,9 @@ export interface RegistryStore {
75
93
  /** Durable kv (P2: enrichment model override). */
76
94
  getSetting(key: string): string | undefined;
77
95
  putSetting(key: string, value: string): Promise<void>;
96
+ /** Remove a kv row entirely (#71): `clear` restores the config fallback, which
97
+ * a stored value cannot express — stored `''` means deliberately OFF. */
98
+ deleteSetting(key: string): Promise<void>;
78
99
  /** P3 §7.1 rule records (status is NOT here — per-machine curation facts, §15). */
79
100
  rules(): IterableIterator<[string, import('./rules.ts').RuleRecord]>;
80
101
  getRule(id: string): import('./rules.ts').RuleRecord | undefined;