@miadi/inquiry-weave 0.8.3 → 0.9.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 CHANGED
@@ -176,6 +176,20 @@ The artefact owns `.weave.yaml`; the episode owns `inquiry/weave.yaml`, which
176
176
  also records sync hashes and counts. These files are authoritative. The
177
177
  medicine wheel is their registered projection.
178
178
 
179
+ ## Chronicle attention through MCP
180
+
181
+ `inquiry-weave-mcp` exposes the same ATTENTION read/answer service used by the
182
+ Miadi API and `passages attention answer`:
183
+
184
+ - `chronicle_attention_list` — open items across the Chronicle, optionally one episode;
185
+ - `chronicle_attention_get` — one item plus safe excerpts from episode-local `depth[]` references;
186
+ - `chronicle_attention_answer` — one explicit answer, atomically written and projected.
187
+
188
+ Set `MIADI_CHRONICLE_ROOT`. Set `MIADI_CHRONICLE_MW_URL` (or `MW_API_URL`) to
189
+ the one Chronicle wheel. With no wheel URL, an answer still lands in the
190
+ authoritative file and the tool returns `pending`; it never falls back to a
191
+ repo-local wheel. See [jgwill/Miadi#631](https://github.com/jgwill/Miadi/issues/631).
192
+
179
193
  ## Episode lineage
180
194
 
181
195
  ```bash
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The read/write half of Chronicle ATTENTION.
3
+ *
4
+ * `attention.json` remains authoritative. This module gives the CLI, app API,
5
+ * and MCP one transaction for answering an item: resolve the episode, refuse
6
+ * malformed or stale state, atomically write the answer, project that exact
7
+ * item to the Chronicle wheel, and leave a truthful receipt. The wheel leg is
8
+ * fail-open by design; a human answer is never rolled back because projection
9
+ * is temporarily unavailable.
10
+ *
11
+ * Ref: jgwill/Miadi#631
12
+ */
13
+ import { ATTENTION_FILE, type AttentionItem } from "@miadi/episodic-memory-schema";
14
+ import { type AttentionNodeOutcome, type RegisterAttentionNodeOptions } from "./attention-node.js";
15
+ export { ATTENTION_FILE };
16
+ export declare const ATTENTION_RECEIPT_FILE = ".mw-attention.json";
17
+ export declare const ATTENTION_MAX_BYTES = 200000;
18
+ export declare const ATTENTION_ANSWER_MAX_CHARS = 10000;
19
+ export type AttentionRegistration = "registered" | "pending" | "unregistered";
20
+ export interface AttentionContext {
21
+ ref: string;
22
+ file: string;
23
+ fragment?: string;
24
+ exists: boolean;
25
+ excerpt?: string;
26
+ error?: string;
27
+ }
28
+ export interface ChronicleAttentionItem extends AttentionItem {
29
+ episode: string;
30
+ registration: AttentionRegistration;
31
+ context?: AttentionContext[];
32
+ }
33
+ export interface EpisodeAttention {
34
+ episode: string;
35
+ items: ChronicleAttentionItem[];
36
+ parseError?: string;
37
+ }
38
+ export interface ReadAttentionOptions {
39
+ includeContext?: boolean;
40
+ }
41
+ export interface ListAttentionOptions extends ReadAttentionOptions {
42
+ episode?: string;
43
+ state?: "open" | "answered" | "all";
44
+ }
45
+ export type AttentionServiceErrorCode = "invalid_answer" | "store_not_found" | "malformed_store" | "item_not_found" | "already_answered" | "stale_state" | "busy";
46
+ export declare class AttentionServiceError extends Error {
47
+ readonly code: AttentionServiceErrorCode;
48
+ constructor(code: AttentionServiceErrorCode, message: string);
49
+ }
50
+ /** Resolve one depth reference inside its episode. Never follows an escape. */
51
+ export declare function readAttentionContext(episodeDir: string, ref: string): Promise<AttentionContext>;
52
+ /** Read one episode's store and its truthful wheel registration state. */
53
+ export declare function readEpisodeAttention(chronicleRoot: string, episodeRef: string, options?: ReadAttentionOptions): Promise<EpisodeAttention | null>;
54
+ /** List attention across the Chronicle, newest-asked first. */
55
+ export declare function listChronicleAttention(chronicleRoot: string, options?: ListAttentionOptions): Promise<ChronicleAttentionItem[]>;
56
+ /** Get one stable item identity, optionally with episode-local context. */
57
+ export declare function getChronicleAttention(chronicleRoot: string, episodeRef: string, itemId: string, options?: ReadAttentionOptions): Promise<ChronicleAttentionItem>;
58
+ export interface AnswerAttentionOptions extends RegisterAttentionNodeOptions {
59
+ mwUrl?: string;
60
+ now?: Date;
61
+ expectedState?: "open" | "answered";
62
+ }
63
+ export interface AnswerAttentionResult {
64
+ episode: string;
65
+ item: ChronicleAttentionItem;
66
+ projection: AttentionNodeOutcome;
67
+ synchronized: boolean;
68
+ receipt: string;
69
+ }
70
+ /**
71
+ * Record one explicit answer. The lock makes the open→answered guard real for
72
+ * concurrent app/MCP/CLI callers; no caller can silently overwrite another.
73
+ */
74
+ export declare function answerChronicleAttention(chronicleRoot: string, episodeRef: string, itemId: string, answerText: string, options?: AnswerAttentionOptions): Promise<AnswerAttentionResult>;
75
+ //# sourceMappingURL=attention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attention.d.ts","sourceRoot":"","sources":["../src/attention.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,356 @@
1
+ /**
2
+ * The read/write half of Chronicle ATTENTION.
3
+ *
4
+ * `attention.json` remains authoritative. This module gives the CLI, app API,
5
+ * and MCP one transaction for answering an item: resolve the episode, refuse
6
+ * malformed or stale state, atomically write the answer, project that exact
7
+ * item to the Chronicle wheel, and leave a truthful receipt. The wheel leg is
8
+ * fail-open by design; a human answer is never rolled back because projection
9
+ * is temporarily unavailable.
10
+ *
11
+ * Ref: jgwill/Miadi#631
12
+ */
13
+ import { ATTENTION_FILE, parseAttentionStore, serializeAttentionStore, } from "@miadi/episodic-memory-schema";
14
+ import { open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
15
+ import { isAbsolute, join, relative, resolve } from "node:path";
16
+ import { resolveEpisode } from "./episode.js";
17
+ import { attentionNodeId, syncEpisodeAttention, } from "./attention-node.js";
18
+ export { ATTENTION_FILE };
19
+ export const ATTENTION_RECEIPT_FILE = ".mw-attention.json";
20
+ export const ATTENTION_MAX_BYTES = 200_000;
21
+ export const ATTENTION_ANSWER_MAX_CHARS = 10_000;
22
+ export class AttentionServiceError extends Error {
23
+ code;
24
+ constructor(code, message) {
25
+ super(message);
26
+ this.code = code;
27
+ this.name = "AttentionServiceError";
28
+ }
29
+ }
30
+ function isNodeError(error, code) {
31
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
32
+ }
33
+ async function readReceipt(episodeDir) {
34
+ const file = join(episodeDir, ATTENTION_RECEIPT_FILE);
35
+ try {
36
+ const fileStat = await stat(file);
37
+ if (!fileStat.isFile() || fileStat.size > ATTENTION_MAX_BYTES)
38
+ return null;
39
+ return {
40
+ receipt: JSON.parse(await readFile(file, "utf8")),
41
+ mtimeMs: fileStat.mtimeMs,
42
+ };
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ function registrationFor(item, fileMtimeMs, receiptRead) {
49
+ if (!receiptRead)
50
+ return "unregistered";
51
+ const entry = receiptRead.receipt.items?.[item.id];
52
+ if (!entry)
53
+ return "unregistered";
54
+ if (entry.state !== "registered" || !entry.verified)
55
+ return "pending";
56
+ if (fileMtimeMs > receiptRead.mtimeMs + 2000)
57
+ return "pending";
58
+ return "registered";
59
+ }
60
+ function splitDepthRef(ref) {
61
+ const at = ref.indexOf("#");
62
+ if (at < 0)
63
+ return { file: ref };
64
+ const fragment = ref.slice(at + 1).trim();
65
+ return { file: ref.slice(0, at), fragment: fragment || undefined };
66
+ }
67
+ function normaliseAnchor(value) {
68
+ return value
69
+ .trim()
70
+ .toLowerCase()
71
+ .replace(/^#+\s*/, "")
72
+ .replace(/["'`]/g, "")
73
+ .replace(/[^a-z0-9]+/g, "-")
74
+ .replace(/^-+|-+$/g, "");
75
+ }
76
+ function contextExcerpt(content, fragment) {
77
+ const lines = content.split(/\r?\n/);
78
+ if (!fragment)
79
+ return lines.slice(0, 24).join("\n").slice(0, 6000);
80
+ let decoded = fragment;
81
+ try {
82
+ decoded = decodeURIComponent(fragment);
83
+ }
84
+ catch {
85
+ // A literal fragment is still useful search text.
86
+ }
87
+ const wanted = normaliseAnchor(decoded);
88
+ let found = lines.findIndex((line) => normaliseAnchor(line) === wanted);
89
+ if (found < 0) {
90
+ found = lines.findIndex((line) => normaliseAnchor(line).includes(wanted));
91
+ }
92
+ if (found < 0) {
93
+ found = lines.findIndex((line) => line.toLowerCase().includes(decoded.toLowerCase()));
94
+ }
95
+ if (found < 0)
96
+ return lines.slice(0, 24).join("\n").slice(0, 6000);
97
+ return lines.slice(Math.max(0, found - 3), Math.min(lines.length, found + 18)).join("\n").slice(0, 6000);
98
+ }
99
+ /** Resolve one depth reference inside its episode. Never follows an escape. */
100
+ export async function readAttentionContext(episodeDir, ref) {
101
+ const parsed = splitDepthRef(ref);
102
+ const filePart = parsed.file.trim();
103
+ if (!filePart || isAbsolute(filePart)) {
104
+ return { ref, file: filePart, fragment: parsed.fragment, exists: false, error: "depth path must be episode-relative" };
105
+ }
106
+ const target = resolve(episodeDir, filePart);
107
+ const rel = relative(episodeDir, target);
108
+ if (rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(rel)) {
109
+ return { ref, file: filePart, fragment: parsed.fragment, exists: false, error: "depth path leaves the episode" };
110
+ }
111
+ try {
112
+ const fileStat = await stat(target);
113
+ if (!fileStat.isFile()) {
114
+ return { ref, file: filePart, fragment: parsed.fragment, exists: false, error: "depth target is not a file" };
115
+ }
116
+ if (fileStat.size > ATTENTION_MAX_BYTES) {
117
+ return { ref, file: filePart, fragment: parsed.fragment, exists: true, error: "depth target is too large to preview" };
118
+ }
119
+ const content = await readFile(target, "utf8");
120
+ return {
121
+ ref,
122
+ file: filePart,
123
+ fragment: parsed.fragment,
124
+ exists: true,
125
+ excerpt: contextExcerpt(content, parsed.fragment),
126
+ };
127
+ }
128
+ catch (error) {
129
+ if (isNodeError(error, "ENOENT")) {
130
+ return { ref, file: filePart, fragment: parsed.fragment, exists: false, error: "depth target does not exist" };
131
+ }
132
+ return {
133
+ ref,
134
+ file: filePart,
135
+ fragment: parsed.fragment,
136
+ exists: false,
137
+ error: error instanceof Error ? error.message : String(error),
138
+ };
139
+ }
140
+ }
141
+ /** Read one episode's store and its truthful wheel registration state. */
142
+ export async function readEpisodeAttention(chronicleRoot, episodeRef, options = {}) {
143
+ const episode = await resolveEpisode(chronicleRoot, episodeRef);
144
+ const file = join(episode.dir, ATTENTION_FILE);
145
+ let raw;
146
+ let fileMtimeMs;
147
+ try {
148
+ const fileStat = await stat(file);
149
+ if (!fileStat.isFile())
150
+ return null;
151
+ if (fileStat.size > ATTENTION_MAX_BYTES) {
152
+ return { episode: episode.name, items: [], parseError: `${ATTENTION_FILE} exceeds ${ATTENTION_MAX_BYTES} bytes` };
153
+ }
154
+ fileMtimeMs = fileStat.mtimeMs;
155
+ raw = await readFile(file, "utf8");
156
+ }
157
+ catch (error) {
158
+ if (isNodeError(error, "ENOENT"))
159
+ return null;
160
+ throw error;
161
+ }
162
+ const parsed = parseAttentionStore(raw);
163
+ if (parsed.parseError)
164
+ return { episode: episode.name, items: [], parseError: parsed.parseError };
165
+ const receipt = await readReceipt(episode.dir);
166
+ const items = await Promise.all(parsed.items.map(async (item) => ({
167
+ ...item,
168
+ episode: episode.name,
169
+ registration: registrationFor(item, fileMtimeMs, receipt),
170
+ context: options.includeContext && item.depth
171
+ ? await Promise.all(item.depth.map((depth) => readAttentionContext(episode.dir, depth)))
172
+ : undefined,
173
+ })));
174
+ return { episode: episode.name, items };
175
+ }
176
+ /** List attention across the Chronicle, newest-asked first. */
177
+ export async function listChronicleAttention(chronicleRoot, options = {}) {
178
+ const state = options.state ?? "open";
179
+ const episodes = options.episode
180
+ ? [options.episode]
181
+ : (await readdir(chronicleRoot, { withFileTypes: true }))
182
+ .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
183
+ .map((entry) => entry.name);
184
+ const out = [];
185
+ for (const episode of episodes) {
186
+ let attention;
187
+ try {
188
+ attention = await readEpisodeAttention(chronicleRoot, episode, options);
189
+ }
190
+ catch {
191
+ continue;
192
+ }
193
+ if (!attention || attention.parseError)
194
+ continue;
195
+ for (const item of attention.items) {
196
+ if (state === "all" || item.state === state)
197
+ out.push(item);
198
+ }
199
+ }
200
+ out.sort((a, b) => (b.asked ?? "").localeCompare(a.asked ?? ""));
201
+ return out;
202
+ }
203
+ /** Get one stable item identity, optionally with episode-local context. */
204
+ export async function getChronicleAttention(chronicleRoot, episodeRef, itemId, options = {}) {
205
+ const attention = await readEpisodeAttention(chronicleRoot, episodeRef, options);
206
+ if (!attention) {
207
+ throw new AttentionServiceError("store_not_found", `episode ${episodeRef} has no ${ATTENTION_FILE}`);
208
+ }
209
+ if (attention.parseError) {
210
+ throw new AttentionServiceError("malformed_store", `${ATTENTION_FILE} is malformed: ${attention.parseError}`);
211
+ }
212
+ const item = attention.items.find((candidate) => candidate.id === itemId);
213
+ if (!item) {
214
+ throw new AttentionServiceError("item_not_found", `attention item '${itemId}' was not found in ${attention.episode}`);
215
+ }
216
+ return item;
217
+ }
218
+ async function writeReceipt(episodeDir, mwUrl, itemId, outcome, now) {
219
+ const file = join(episodeDir, ATTENTION_RECEIPT_FILE);
220
+ let prior = {};
221
+ try {
222
+ prior = JSON.parse(await readFile(file, "utf8"));
223
+ }
224
+ catch {
225
+ // First receipt, or unreadable prior: replace it with a truthful one.
226
+ }
227
+ const synchronized = outcome.state !== "pending" && outcome.verified;
228
+ const receipt = {
229
+ ...prior,
230
+ mw_url: mwUrl,
231
+ synced_at: now.toISOString(),
232
+ items: {
233
+ ...(prior.items ?? {}),
234
+ [itemId]: {
235
+ node_id: outcome.node_id,
236
+ state: outcome.state === "pending" ? "pending" : "registered",
237
+ verified: outcome.verified,
238
+ verified_at: synchronized ? now.toISOString() : undefined,
239
+ error: outcome.error,
240
+ },
241
+ },
242
+ };
243
+ const tmp = `${file}.tmp-${process.pid}-${now.getTime()}`;
244
+ await writeFile(tmp, JSON.stringify(receipt, null, 2) + "\n", "utf8");
245
+ await rename(tmp, file);
246
+ return file;
247
+ }
248
+ /**
249
+ * Record one explicit answer. The lock makes the open→answered guard real for
250
+ * concurrent app/MCP/CLI callers; no caller can silently overwrite another.
251
+ */
252
+ export async function answerChronicleAttention(chronicleRoot, episodeRef, itemId, answerText, options = {}) {
253
+ const answer = answerText.trim();
254
+ if (!answer)
255
+ throw new AttentionServiceError("invalid_answer", "answer must not be empty");
256
+ if (answer.length > ATTENTION_ANSWER_MAX_CHARS) {
257
+ throw new AttentionServiceError("invalid_answer", `answer exceeds ${ATTENTION_ANSWER_MAX_CHARS} characters`);
258
+ }
259
+ const episode = await resolveEpisode(chronicleRoot, episodeRef);
260
+ const file = join(episode.dir, ATTENTION_FILE);
261
+ const lockFile = `${file}.lock`;
262
+ let lock;
263
+ try {
264
+ lock = await open(lockFile, "wx");
265
+ }
266
+ catch (error) {
267
+ if (isNodeError(error, "EEXIST")) {
268
+ throw new AttentionServiceError("busy", `${ATTENTION_FILE} is being updated; retry the answer`);
269
+ }
270
+ throw error;
271
+ }
272
+ let item;
273
+ const now = options.now ?? new Date();
274
+ try {
275
+ let raw;
276
+ let fileMode;
277
+ try {
278
+ const fileStat = await stat(file);
279
+ if (!fileStat.isFile())
280
+ throw new AttentionServiceError("store_not_found", `${file} is not a file`);
281
+ if (fileStat.size > ATTENTION_MAX_BYTES) {
282
+ throw new AttentionServiceError("malformed_store", `${ATTENTION_FILE} exceeds ${ATTENTION_MAX_BYTES} bytes`);
283
+ }
284
+ fileMode = fileStat.mode;
285
+ raw = await readFile(file, "utf8");
286
+ }
287
+ catch (error) {
288
+ if (isNodeError(error, "ENOENT")) {
289
+ throw new AttentionServiceError("store_not_found", `episode ${episode.name} has no ${ATTENTION_FILE}`);
290
+ }
291
+ throw error;
292
+ }
293
+ const parsed = parseAttentionStore(raw);
294
+ if (parsed.parseError) {
295
+ throw new AttentionServiceError("malformed_store", `${ATTENTION_FILE} is malformed: ${parsed.parseError}`);
296
+ }
297
+ const index = parsed.items.findIndex((candidate) => candidate.id === itemId);
298
+ if (index < 0) {
299
+ throw new AttentionServiceError("item_not_found", `attention item '${itemId}' was not found in ${episode.name}`);
300
+ }
301
+ const current = parsed.items[index];
302
+ const expected = options.expectedState ?? "open";
303
+ if (current.state !== expected) {
304
+ if (current.state === "answered") {
305
+ throw new AttentionServiceError("already_answered", `attention item '${itemId}' is already answered`);
306
+ }
307
+ throw new AttentionServiceError("stale_state", `attention item '${itemId}' is ${current.state}; caller expected ${expected}`);
308
+ }
309
+ if (current.state !== "open") {
310
+ throw new AttentionServiceError("already_answered", `attention item '${itemId}' is already answered`);
311
+ }
312
+ item = {
313
+ ...current,
314
+ state: "answered",
315
+ answer,
316
+ answered_at: now.toISOString().slice(0, 10),
317
+ };
318
+ parsed.items[index] = item;
319
+ const tmp = `${file}.tmp-${process.pid}-${now.getTime()}`;
320
+ try {
321
+ await writeFile(tmp, serializeAttentionStore(parsed.items), { encoding: "utf8", mode: fileMode });
322
+ await rename(tmp, file);
323
+ }
324
+ catch (error) {
325
+ await rm(tmp, { force: true });
326
+ throw error;
327
+ }
328
+ }
329
+ finally {
330
+ await lock.close();
331
+ await rm(lockFile, { force: true });
332
+ }
333
+ const mwUrl = (options.mwUrl ?? "").replace(/\/+$/, "");
334
+ const projection = mwUrl
335
+ ? (await syncEpisodeAttention(mwUrl, episode.name, [item], { fetchImpl: options.fetchImpl })).outcomes[0]
336
+ : {
337
+ state: "pending",
338
+ node_id: attentionNodeId(episode.name, item.id),
339
+ verified: false,
340
+ error: "no Chronicle medicine-wheel URL configured",
341
+ };
342
+ const receipt = await writeReceipt(episode.dir, mwUrl, item.id, projection, now);
343
+ const synchronized = projection.state !== "pending" && projection.verified;
344
+ return {
345
+ episode: episode.name,
346
+ item: {
347
+ ...item,
348
+ episode: episode.name,
349
+ registration: synchronized ? "registered" : "pending",
350
+ },
351
+ projection,
352
+ synchronized,
353
+ receipt: relative(chronicleRoot, receipt),
354
+ };
355
+ }
356
+ //# sourceMappingURL=attention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attention.js","sourceRoot":"","sources":["../src/attention.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The five-stage closing gate for one Chronicle vessel — created, committed,
3
+ * pushed, registered, receipt-verified — each proven by running its probe and
4
+ * reporting what the command ACTUALLY said.
5
+ *
6
+ * Mirrored from packages/voice-mcp/src/closing.ts (proveClosing), probe for
7
+ * probe and drift name for drift name, because that package cannot be imported
8
+ * here (it is a consumer of this one and carries the voice stack). One
9
+ * deliberate difference: William's word of 2026-09-04 gives stages 2 and 3
10
+ * (committed, pushed) to the API, so the owed actions name the door's verb
11
+ * before the raw git commands. voice-mcp's copy is the coordinator's to retire.
12
+ *
13
+ * Ref: jgwill/Miadi#621.
14
+ */
15
+ /** The retired ceremony wheel. A card registered there is lost; refuse the host. */
16
+ export declare const POISONED_WHEEL_HOST = "tail3b11eb";
17
+ export declare const WHEEL_PROBE_TIMEOUT_MS = 5000;
18
+ export declare class RefusedWheelError extends Error {
19
+ constructor(url: string);
20
+ }
21
+ export declare function refusePoisonedWheel(wheelUrl: string): void;
22
+ export type ClosingStage = "created" | "committed" | "pushed" | "registered" | "receipt-verified";
23
+ export interface ClosingStageProof {
24
+ stage: ClosingStage;
25
+ /** `owed` is a first-class state — the honest word for "not proven yet". */
26
+ state: "proven" | "owed" | "failed";
27
+ /** The command run, verbatim, so a human can re-run it. */
28
+ probe: string;
29
+ /** What that command actually said. Never a summary of what it should say. */
30
+ observed: string;
31
+ }
32
+ export interface ClosingReport {
33
+ episode: string;
34
+ closing: ClosingStageProof[];
35
+ drift: string[];
36
+ owedActions: string[];
37
+ }
38
+ export interface ClosingConfig {
39
+ chronicleRoot: string;
40
+ /** The git root holding the chronicle — measure it with chronicleGitRoot(). */
41
+ gitRoot: string;
42
+ wheelUrl: string;
43
+ fetchImpl?: typeof fetch;
44
+ }
45
+ export declare const MW_RECEIPT_BASENAME = ".mw-registration.json";
46
+ interface WheelReading {
47
+ status: number | null;
48
+ observed: string;
49
+ }
50
+ /** GET the episode card off the wheel. `null` means the wheel did not answer at all. */
51
+ export declare function readWheelCard(wheelUrl: string, nodeId: string, fetchImpl?: typeof fetch): Promise<WheelReading>;
52
+ /**
53
+ * Run all five proofs for one episode folder, read-only.
54
+ * `folder` is the directory name under the chronicle root.
55
+ */
56
+ export declare function proveEpisodeClosing(config: ClosingConfig, folder: string): Promise<ClosingReport>;
57
+ /** True only when all five stages are proven. */
58
+ export declare function closingIsProven(report: Pick<ClosingReport, "closing">): boolean;
59
+ export {};
60
+ //# sourceMappingURL=closing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closing.d.ts","sourceRoot":"","sources":["../src/closing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,eAAe,CAAA;AAE/C,eAAO,MAAM,sBAAsB,OAAQ,CAAA;AAE3C,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,GAAG,EAAE,MAAM;CAOxB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1D;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,kBAAkB,CAAA;AAEjG,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,YAAY,CAAA;IACnB,4EAA4E;IAC5E,KAAK,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;IACnC,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAA;IACrB,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB;AAED,eAAO,MAAM,mBAAmB,0BAA0B,CAAA;AAE1D,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wFAAwF;AACxF,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,YAAY,CAAC,CAevB;AA+BD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA2HvG;AAED,iDAAiD;AACjD,wBAAgB,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG,OAAO,CAE/E"}