@treeseed/dsh-chapters 0.1.23 → 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.
- package/README.md +2 -1
- package/cordis.patch.yml +54 -1
- package/docs/architecture.md +1 -466
- package/docs/backlog.md +234 -4
- package/docs/contract.md +127 -1
- package/docs/development.md +248 -12
- package/docs/host-compaction-seam.md +108 -0
- package/docs/knowledge-repo.md +532 -16
- package/docs/project-state.md +323 -0
- package/docs/verify.md +217 -0
- package/lib/archive.d.ts +49 -1
- package/lib/archive.js +56 -2
- package/lib/archive.js.map +1 -1
- package/lib/arrival.js +24 -10
- package/lib/arrival.js.map +1 -1
- package/lib/attach.d.ts +46 -0
- package/lib/attach.js +120 -0
- package/lib/attach.js.map +1 -0
- package/lib/auto-save.d.ts +145 -0
- package/lib/auto-save.js +318 -0
- package/lib/auto-save.js.map +1 -0
- package/lib/client.js +413 -11
- package/lib/client.js.map +1 -1
- package/lib/commands.d.ts +11 -1
- package/lib/commands.js +138 -7
- package/lib/commands.js.map +1 -1
- package/lib/compose.js +25 -6
- package/lib/compose.js.map +1 -1
- package/lib/continue-core.d.ts +24 -1
- package/lib/continue-core.js +41 -9
- package/lib/continue-core.js.map +1 -1
- package/lib/diag.d.ts +47 -0
- package/lib/diag.js +141 -0
- package/lib/diag.js.map +1 -0
- package/lib/engine-core.d.ts +51 -1
- package/lib/engine-core.js +70 -7
- package/lib/engine-core.js.map +1 -1
- package/lib/engine.d.ts +93 -0
- package/lib/engine.js +477 -36
- package/lib/engine.js.map +1 -1
- package/lib/enrich-queue.js +28 -2
- package/lib/enrich-queue.js.map +1 -1
- package/lib/enrich-wire.d.ts +92 -2
- package/lib/enrich-wire.js +135 -17
- package/lib/enrich-wire.js.map +1 -1
- package/lib/enrich.d.ts +27 -2
- package/lib/enrich.js +114 -18
- package/lib/enrich.js.map +1 -1
- package/lib/index.d.ts +59 -0
- package/lib/index.js +215 -2
- package/lib/index.js.map +1 -1
- package/lib/indexing.d.ts +7 -0
- package/lib/indexing.js.map +1 -1
- package/lib/injections.d.ts +1 -0
- package/lib/injections.js +38 -0
- package/lib/injections.js.map +1 -1
- package/lib/last-resort.d.ts +24 -0
- package/lib/last-resort.js +27 -0
- package/lib/last-resort.js.map +1 -0
- package/lib/prompt.d.ts +130 -0
- package/lib/prompt.js +255 -0
- package/lib/prompt.js.map +1 -0
- package/lib/registry.d.ts +39 -1
- package/lib/registry.js +76 -9
- package/lib/registry.js.map +1 -1
- package/lib/render.d.ts +31 -3
- package/lib/render.js +82 -3
- package/lib/render.js.map +1 -1
- package/lib/repair-run.d.ts +49 -0
- package/lib/repair-run.js +423 -0
- package/lib/repair-run.js.map +1 -0
- package/lib/repair.d.ts +200 -0
- package/lib/repair.js +354 -0
- package/lib/repair.js.map +1 -0
- package/lib/rules-commands.d.ts +22 -0
- package/lib/rules-commands.js +177 -20
- package/lib/rules-commands.js.map +1 -1
- package/lib/rules.d.ts +184 -7
- package/lib/rules.js +312 -27
- package/lib/rules.js.map +1 -1
- package/lib/save-core.d.ts +72 -0
- package/lib/save-core.js +220 -0
- package/lib/save-core.js.map +1 -0
- package/lib/session-rules.d.ts +222 -0
- package/lib/session-rules.js +454 -0
- package/lib/session-rules.js.map +1 -0
- package/lib/store.d.ts +22 -1
- package/lib/store.js +94 -17
- package/lib/store.js.map +1 -1
- package/lib/sync.js +13 -1
- package/lib/sync.js.map +1 -1
- package/lib/tools.d.ts +14 -0
- package/lib/tools.js +341 -91
- package/lib/tools.js.map +1 -1
- package/lib/types.d.ts +31 -1
- package/lib/types.js +12 -0
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +9 -0
- package/lib/version.js +10 -0
- package/lib/version.js.map +1 -0
- package/lib/warnings.d.ts +10 -0
- package/lib/warnings.js +18 -0
- package/lib/warnings.js.map +1 -0
- 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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EACL,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,EAAE,QAAQ,GAEhG,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AASxC,MAAM,WAAW,GAAG,CAAC,IAA6B,EAAU,EAAE;IAC5D,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAA4B,CAAA;IACjE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAA6C,CAAC,CAAC,CAAC,EAAE,CAAA;IAC3G,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACrF,CAAC,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAmC,EAAE,MAAc;IAClF,MAAM,GAAG,GAAmB,EAAE,CAAA;IAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,IAAI,EAAE,CAAC,IAAI,KAAK,cAAc;YAAE,SAAQ;QACxC,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAA;QACvD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;YAAE,SAAQ;QACpC,MAAM,IAAI,GAAG,kBAAkB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QACvD,KAAK,MAAM,IAAI,IAAI,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;YAClD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAQ;YAC5B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACd,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAI9B;IACC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;IAC9F,KAAK,MAAM,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACvB,IAAI,CAAC,KAAK,IAAI;YAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACxC,CAAC;IACD,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC7B,IAAI,CAAC,KAAK,IAAI;gBAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACxC,CAAC;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAAE,CAAC,IAAI,CAAC,CAAA;IAC1B,OAAO,CAAC,CAAA;AACV,CAAC;AAqBD,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;AAE/E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAiB;IACnD,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;IAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IAClE,8EAA8E;IAC9E,4EAA4E;IAC5E,oCAAoC;IACpC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;IAClC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,SAAQ;YAChC,sEAAsE;YACtE,uEAAuE;YACvE,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;YAC7E,IAAI,MAAM,KAAK,IAAI;gBAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAAE,SAAQ;QACxC,2EAA2E;QAC3E,2EAA2E;QAC3E,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAA;QACpC,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC7F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QAC3D,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAA;QAClF,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QACtC,yEAAyE;QACzE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,cAAc,CAAC;YACpC,MAAM;YACN,QAAQ,EAAE,SAAS;YACnB,KAAK;YACL,aAAa,EAAE,IAAI,CAAC,SAAS;YAC7B,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACpE,IAAI,EAAE,GAAG,IAAI,IAAI;YACjB,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE,IAAI,CAAC,SAAS;YACvB,WAAW,EAAE,QAAQ;SACtB,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACnB,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;QAChE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACxB,OAAO,IAAI,CAAC,CAAA;IACd,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,GAAG,OAAO,EAAE,CAAA;AAC5D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAA0E,EAC1E,SAAiB,EACjB,QAAQ,GAAG,IAAI;IAEf,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,IAAI,MAAM,GAAuB,SAAS,CAAA;IAC1C,OAAO,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,QAAQ,EAAE,CAAC;QACzE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAChB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAChB,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,CAAA;QAC/B,IAAI,KAAK,KAAK,SAAS;YAAE,MAAK;QAC9B,MAAM,GAAG,KAAK,CAAC,aAAa,IAAI,SAAS,CAAA;IAC3C,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,EAAE,CAAA;AACtB,CAAC;AAED,2EAA2E;AAC3E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAA;AAI7C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAQrC;IACC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,IAAI,2BAA2B,CAAA;IAC7D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAA;IACnC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,KAAK,EAAE;YAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAC1D,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC,CAAA;IAC5B,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,IAAI,UAAU,IAAI,GAAG,EAAE,CAAC;YAAC,SAAS,GAAG,IAAI,CAAC;YAAC,MAAK;QAAC,CAAC;QAClD,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,EAAY,CAAA;QAClC,UAAU,IAAI,CAAC,CAAA;QACf,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;gBACjC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACtD,CAAC;YAAC,MAAM,CAAC,CAAC,yCAAyC,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,IAAI,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;YACzC,IAAI,CAAC;gBAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,wBAAwB,CAAC,CAAC;QAC9F,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAAC,CAAC;IAC/F,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAA;AACjC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAA+B;IACpE,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACvC,gFAAgF;IAChF,6EAA6E;IAC7E,oCAAoC;IACpC,MAAM,UAAU,GAAG,CAAC,EAAU,EAAiB,EAAE;QAC/C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAA8C,CAAA;YACtE,MAAM,MAAM,GAAG,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAqI,CAAA;YACjK,MAAM,OAAO,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO,IAAI,MAAM,EAAE,OAAO,CAAA;YACzD,MAAM,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,aAAa,CAAA;YACxC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACrD,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAA;QAAC,CAAC;IACzB,CAAC,CAAA;IACD,IAAI,OAAO,GAAsC,IAAI,CAAA;IACrD,MAAM,aAAa,GAAG,KAAK,EAAE,EAAU,EAA0B,EAAE;QACjE,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,GAAG,CAAC,cAAc,CAA0H,CAAA;gBACvJ,IAAI,EAAE,EAAE,YAAY,KAAK,SAAS;oBAAE,OAAO,IAAI,CAAA;gBAC/C,MAAM,CAAC,GAAG,IAAI,GAAG,EAAyB,CAAA;gBAC1C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC,YAAY,EAAE,IAAI,EAAE,EAAE,CAAC;oBAC9C,MAAM,GAAG,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,CAAA;oBACzB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;wBAC1C,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,aAAa,CAAA;wBACjC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;oBAC1D,CAAC;gBACH,CAAC;gBACD,OAAO,GAAG,CAAC,CAAA;YACb,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,IAAI,CAAA;YAAC,CAAC;QACzB,CAAC;QACD,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAA;IAChC,CAAC,CAAA;IACD,OAAO,KAAK,EAAE,EAAU,EAAE,EAAE;QAC1B,4EAA4E;QAC5E,oDAAoD;QACpD,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,CAAA;QAC3B,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;QAC9B,OAAO,aAAa,CAAC,EAAE,CAAC,CAAA;IAC1B,CAAC,CAAA;AACH,CAAC;AASD,MAAM,SAAS,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;AAEpJ;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,KAAa;IAC3D,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;IAC1B,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA;IAC9B,OAAO,SAAS,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;AACzC,CAAC;AA0BD;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAc;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IAClE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;IAClC,KAAK,MAAM,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC;QAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5F,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;IAC3C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,2EAA2E;QAC3E,uDAAuD;QACvD,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG;YAAE,MAAK;QAChC,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;QAChE,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAAE,SAAQ;QAC1D,mEAAmE;QACnE,4EAA4E;QAC5E,8EAA8E;QAC9E,4EAA4E;QAC5E,oEAAoE;QACpE,MAAM,KAAK,GAAc,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAA;QACtF,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,YAAY,CAAA;QACrC,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC7F,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,SAAS,CAAA;QAC7G,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACzD,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAA;QACjF,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC3C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,cAAc,CAAC;YACzD,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,CAAC,SAAS;YACtD,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACpE,IAAI,EAAE,GAAG,IAAI,IAAI;YACjB,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO;YAC/D,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa;SAC/C,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACnB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;QACpE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACxB,IAAI,KAAK,KAAK,QAAQ;YAAE,MAAM,IAAI,CAAC,CAAA;;YAC9B,QAAQ,IAAI,CAAC,CAAA;QAClB,IAAI,UAAU;YAAE,OAAO,IAAI,CAAC,CAAA;IAC9B,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAA;AACpD,CAAC;AAED;;gCAEgC;AAChC,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1D,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAQ;QACpE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACf,CAAC;IACD,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACzB,CAAC;AA6BD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAqB;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc;QAC9B,CAAC,CAAC,MAAM,eAAe,CAAC;YACpB,KAAK,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;YACvB,iBAAiB,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,MAAM,kBAAkB,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YACnG,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpE,CAAC;QACJ,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,GAAG,EAAU,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC/E,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,KAAK,SAAS,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;QAChF,CAAC,CAAC,IAAI,CAAC,cAAc;QACrB,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,KAAK,KAAK,SAAS;YACrC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;YAClE,CAAC,CAAC,SAAS,CAAA;IACf,OAAO,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE;QACvC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxF,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAQvC;IACC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAA;IAC/C,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;gBAAE,OAAM;YAC3B,IAAK,KAA2B,EAAE,IAAI,KAAK,UAAU;gBAAE,OAAM;YAC7D,MAAM,EAAE,GAAI,OAAuC,EAAE,EAAE,CAAA;YACvD,MAAM,MAAM,GAAI,OAA8E,EAAE,cAAc,EAAE,EAAE,CAAA;YAClH,MAAM,GAAG,GAAI,OAAqD,EAAE,MAAM,EAAE,GAAG,CAAA;YAC/E,MAAM,MAAM,GAAI,KAA0B,CAAC,GAAG,CAAA;YAC9C,IAAI,EAAE,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAM;YACjG,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YACvC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAM;YAC7B,0EAA0E;YAC1E,2EAA2E;YAC3E,IAAI,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAM;YAC9D,MAAM,GAAG,GAAG,GAAkB,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;iBAC1C,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,mBAAmB,CAAC;gBACvC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;gBAC7B,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE;gBAC3B,SAAS,EAAE,EAAE;gBACb,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE;gBACtB,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;gBACrB,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;iBACF,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;gBAAE,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA,CAAC,CAAC,CAAC;iBACjF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACnB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAC3B,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;QACrF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAClB,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}
|
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
|
-
|
|
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;
|