@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.
Files changed (104) hide show
  1. package/README.md +2 -1
  2. package/cordis.patch.yml +54 -1
  3. package/docs/architecture.md +1 -466
  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 +145 -0
  20. package/lib/auto-save.js +318 -0
  21. package/lib/auto-save.js.map +1 -0
  22. package/lib/client.js +413 -11
  23. package/lib/client.js.map +1 -1
  24. package/lib/commands.d.ts +11 -1
  25. package/lib/commands.js +138 -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 +70 -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 +59 -0
  50. package/lib/index.js +215 -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 +82 -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 +72 -0
  82. package/lib/save-core.js +220 -0
  83. package/lib/save-core.js.map +1 -0
  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 +341 -91
  94. package/lib/tools.js.map +1 -1
  95. package/lib/types.d.ts +31 -1
  96. package/lib/types.js +12 -0
  97. package/lib/types.js.map +1 -1
  98. package/lib/version.d.ts +9 -0
  99. package/lib/version.js +10 -0
  100. package/lib/version.js.map +1 -0
  101. package/lib/warnings.d.ts +10 -0
  102. package/lib/warnings.js +18 -0
  103. package/lib/warnings.js.map +1 -0
  104. package/package.json +9 -3
@@ -0,0 +1,130 @@
1
+ import { type SessionState } from './registry.ts';
2
+ import type { SessionEventLike } from './types.ts';
3
+ /** The stored override key (durable kv, same table as `enrichment.model`). */
4
+ export declare const PROMPT_SETTING_KEY = "prompt.model";
5
+ export interface PromptRoute {
6
+ provider: string;
7
+ model: string;
8
+ }
9
+ export interface PromptSuggestionResult {
10
+ ok: boolean;
11
+ text: string;
12
+ }
13
+ export interface PromptConfig {
14
+ /** config default for the model; '' ⇒ off unless a stored override says otherwise. */
15
+ model: string;
16
+ /** hard total budget on the assembled input slice. */
17
+ contextChars: number;
18
+ /** cap on the accepted recommendation; over-cap is invalid, never truncated. */
19
+ maxChars: number;
20
+ }
21
+ export interface PromptDeps {
22
+ getSetting(key: string): string | undefined;
23
+ putSetting(key: string, value: string): Promise<void>;
24
+ deleteSetting(key: string): Promise<void>;
25
+ /** Active chapter records for the calling session's registry row (never the
26
+ * ancestor walk — the live tail carries the immediate context; this is the
27
+ * economy, and the choice is documented at the site that reads it). */
28
+ chaptersOf(sessionId: string): Promise<{
29
+ number: number;
30
+ title: string;
31
+ summary: string;
32
+ }[]>;
33
+ config: PromptConfig;
34
+ /** The last route the conversation used on this plane, or null. */
35
+ conversationRoute(): PromptRoute | null;
36
+ /** One auxiliary completion; may reject — `suggest` catches and refuses honestly. */
37
+ fetch(prompt: string, route: PromptRoute): Promise<string>;
38
+ warn?(msg: string): void;
39
+ }
40
+ /** A live session as the command hands it over: id, an optional title, and the
41
+ * surface events (what the working model sees — the same view the defining model reads). */
42
+ export interface SuggestSession {
43
+ id: string;
44
+ title?: string | undefined;
45
+ events: SessionEventLike[];
46
+ }
47
+ /** The surface the host command and `/chapters-status` speak to. */
48
+ export interface PromptSurface {
49
+ /** Effective spec after precedence; '' ⇒ OFF. */
50
+ effective(): {
51
+ spec: string;
52
+ source: 'override' | 'config' | 'unset';
53
+ };
54
+ setModel(spec: string | null): Promise<void>;
55
+ suggest(session: SuggestSession): Promise<PromptSuggestionResult>;
56
+ /** One line for /chapters-status — ON names the model, OFF names the command. */
57
+ statusLine(): string;
58
+ }
59
+ /**
60
+ * Effective spec = stored override ?? config default, trimmed. `undefined`
61
+ * (row absent — cleared) falls back; a STORED '' is the deliberate OFF.
62
+ */
63
+ export declare function effectivePromptSpec(setting: string | undefined, configModel: string): {
64
+ spec: string;
65
+ source: 'override' | 'config' | 'unset';
66
+ };
67
+ /**
68
+ * 'provider/model' routes as named; a bare model name rides the conversation's
69
+ * provider (the enrich-wire rule); '' is not a route. A bare name with no
70
+ * provider answers with its reason rather than guessing.
71
+ */
72
+ export declare function resolvePromptRoute(spec: string, conversation: PromptRoute | null): {
73
+ route: PromptRoute | null;
74
+ reason?: string;
75
+ };
76
+ /**
77
+ * The conversation tail from surface events, screened through the ONE injection
78
+ * predicate (docs/contract.md, #59): injected events drop, injected spans strip.
79
+ * Chronological; text-bearing human/assistant messages only.
80
+ */
81
+ export declare function tailFromEvents(events: SessionEventLike[]): {
82
+ role: 'user' | 'assistant';
83
+ text: string;
84
+ }[];
85
+ export interface SuggestFacts {
86
+ title?: string | undefined;
87
+ chapters: {
88
+ number: number;
89
+ title: string;
90
+ summary: string;
91
+ }[];
92
+ tail: {
93
+ role: 'user' | 'assistant';
94
+ text: string;
95
+ }[];
96
+ }
97
+ /**
98
+ * Assemble the auxiliary input: deterministic (same facts ⇒ same bytes), hard
99
+ * total ≤ `capChars`. Oldest chapter lines drop first, then oldest tail
100
+ * messages; every drop is ANNOUNCED in the input itself — a silent squeeze
101
+ * would make the answer look derived from bytes that were never sent.
102
+ */
103
+ export declare function buildSuggestInput(facts: SuggestFacts, capChars: number, maxChars: number): string;
104
+ /**
105
+ * Validate one answer. A wrapping code fence or one pair of wrapping quotes is
106
+ * formatting noise and comes off; the rest is the human's next prompt verbatim.
107
+ * Over-cap or empty is INVALID (never truncated) — the caller retries once and
108
+ * then refuses with the measured numbers.
109
+ */
110
+ export declare function validateSuggestion(raw: string, maxChars: number): {
111
+ ok: true;
112
+ text: string;
113
+ } | {
114
+ ok: false;
115
+ reason: string;
116
+ };
117
+ /**
118
+ * The watermarks this surface caches per session: the seq of the last surface
119
+ * event at generation. A new completed turn moves the seq and retires the
120
+ * suggestion; a restart simply loses the cache (it is a convenience, never truth).
121
+ */
122
+ export declare function createPromptSurface(deps: PromptDeps): PromptSurface;
123
+ /** Narrow slice: what `registerHostCommands` needs from the surface. */
124
+ export type PromptCommandSurface = PromptSurface;
125
+ /** Active chapters of one registry row, mapped to the facts the input uses. */
126
+ export declare function promptChaptersOf(state: SessionState): {
127
+ number: number;
128
+ title: string;
129
+ summary: string;
130
+ }[];
package/lib/prompt.js ADDED
@@ -0,0 +1,255 @@
1
+ /**
2
+ * dsh-chapters — prompt recommendations (#71, site §28).
3
+ *
4
+ * One model reads the context and DOES the work; a second model reads the context
5
+ * and DEFINES it: the next prompt the human would send, offered as a ghost layer
6
+ * in the composer. Off by default, and the model IS the switch: the effective spec
7
+ * (stored override ?? config `promptModel`) being non-empty is what enables the
8
+ * feature — one knob, no second boolean that could drift against it.
9
+ *
10
+ * Transport here is the PULL: the client overlay executes
11
+ * `/chapters-prompt suggest` over `remote.commands` (the door-proven wire) and this
12
+ * module answers from a one-per-turn-boundary cache or one bounded auxiliary call.
13
+ * The 0.2.x push upgrade (plugin record + projection) is in docs/backlog.md; the
14
+ * measured host facts are in docs/contract.md § Composer Ghost Layer.
15
+ *
16
+ * Invariants the answer MUST respect: the recommendation is UI state only. It is
17
+ * never archived, never enters a notice or a rule, never steers a model request —
18
+ * it reaches the log only when a human accepts it and sends, as their own message.
19
+ *
20
+ * @module dsh-chapters/src/prompt
21
+ */
22
+ import { textOfBlocks } from "./log-shape.js";
23
+ import { screenConversation, stripReminderSpans } from "./injections.js";
24
+ import { isActiveChapter } from "./registry.js";
25
+ /** The stored override key (durable kv, same table as `enrichment.model`). */
26
+ export const PROMPT_SETTING_KEY = 'prompt.model';
27
+ /**
28
+ * Effective spec = stored override ?? config default, trimmed. `undefined`
29
+ * (row absent — cleared) falls back; a STORED '' is the deliberate OFF.
30
+ */
31
+ export function effectivePromptSpec(setting, configModel) {
32
+ if (setting !== undefined)
33
+ return { spec: setting.trim(), source: 'override' };
34
+ const c = configModel.trim();
35
+ return { spec: c, source: c === '' ? 'unset' : 'config' };
36
+ }
37
+ /**
38
+ * 'provider/model' routes as named; a bare model name rides the conversation's
39
+ * provider (the enrich-wire rule); '' is not a route. A bare name with no
40
+ * provider answers with its reason rather than guessing.
41
+ */
42
+ export function resolvePromptRoute(spec, conversation) {
43
+ if (spec === '')
44
+ return { route: null, reason: 'prompt suggestions are off' };
45
+ const i = spec.indexOf('/');
46
+ if (i > 0 && i < spec.length - 1)
47
+ return { route: { provider: spec.slice(0, i), model: spec.slice(i + 1) } };
48
+ if (conversation !== null && conversation.provider !== '')
49
+ return { route: { provider: conversation.provider, model: spec } };
50
+ return { route: null, reason: `no provider to route "${spec}" — name it as provider/model` };
51
+ }
52
+ /**
53
+ * The conversation tail from surface events, screened through the ONE injection
54
+ * predicate (docs/contract.md, #59): injected events drop, injected spans strip.
55
+ * Chronological; text-bearing human/assistant messages only.
56
+ */
57
+ export function tailFromEvents(events) {
58
+ const out = [];
59
+ for (const ev of events) {
60
+ const data = (ev.data ?? {});
61
+ if (ev.type === 'user/message') {
62
+ const screen = screenConversation(data, textOfBlocks(data.content), 'user');
63
+ if (screen.omittedEvent)
64
+ continue;
65
+ const text = stripReminderSpans(screen.text).text.trim();
66
+ if (text !== '')
67
+ out.push({ role: 'user', text });
68
+ }
69
+ else if (ev.type === 'assistant/message') {
70
+ const message = (data.message ?? data);
71
+ const text = stripReminderSpans(textOfBlocks(message.content)).text.trim();
72
+ if (text !== '')
73
+ out.push({ role: 'assistant', text });
74
+ }
75
+ }
76
+ return out;
77
+ }
78
+ /** One message's inline budget before its own ellipsis — the cap is a cap, the
79
+ * ellipsis says so (loudness, #61). */
80
+ const MESSAGE_INLINE_CHARS = 500;
81
+ /**
82
+ * Assemble the auxiliary input: deterministic (same facts ⇒ same bytes), hard
83
+ * total ≤ `capChars`. Oldest chapter lines drop first, then oldest tail
84
+ * messages; every drop is ANNOUNCED in the input itself — a silent squeeze
85
+ * would make the answer look derived from bytes that were never sent.
86
+ */
87
+ export function buildSuggestInput(facts, capChars, maxChars) {
88
+ const chapters = [...facts.chapters];
89
+ let tail = [...facts.tail];
90
+ let droppedChapters = 0;
91
+ let droppedTail = 0;
92
+ let msgCap = MESSAGE_INLINE_CHARS;
93
+ const perMessage = (t) => (t.length > msgCap ? `${t.slice(0, msgCap - 1)}…` : t);
94
+ const render = (announce) => {
95
+ const head = `You are the second reader of a DSH session — a model that DEFINES the next step and never executes it.`;
96
+ const rules = `Write the ONE next prompt the human should send. A single imperative request; plain text; at most ${maxChars} characters; no preamble, no quotes, no markdown; it must follow from the latest state and propose nothing already done. Reply with ONLY that prompt.`;
97
+ const parts = [
98
+ head,
99
+ '',
100
+ `## Session${facts.title !== undefined && facts.title !== '' ? `: ${facts.title}` : ''}`,
101
+ '',
102
+ chapters.length > 0 ? '## Where it has been (chapters)' : '## Where it has been (no chapters yet)',
103
+ ...chapters.map((c) => `${c.number}. ${c.title} — ${c.summary}`),
104
+ '',
105
+ tail.length > 0 ? '## Live tail (oldest first)' : '## Live tail (empty)',
106
+ ...tail.map((m) => `${m.role === 'user' ? 'Human' : 'Assistant'}: ${perMessage(m.text)}`),
107
+ ];
108
+ if (announce !== '')
109
+ parts.push(announce);
110
+ parts.push('', rules);
111
+ return parts.join('\n');
112
+ };
113
+ // Trim to budget: chapters are the compressible past; the tail is the present.
114
+ // The ladder has a floor: a `capChars` smaller than the fixed skeleton (persona +
115
+ // section headers + reply rule) cannot be met by dropping — the last arm returns
116
+ // the skeleton as-is. The documented default (4000) is far above it.
117
+ for (;;) {
118
+ const announce = droppedChapters + droppedTail === 0
119
+ ? ''
120
+ : `\n(older context dropped to fit the ${capChars}-char budget: ${droppedChapters} chapter line(s), ${droppedTail} message(s))`;
121
+ const input = render(announce);
122
+ if (input.length <= capChars)
123
+ return input;
124
+ if (chapters.length > 0) {
125
+ chapters.shift();
126
+ droppedChapters += 1;
127
+ continue;
128
+ }
129
+ if (tail.length > 1) {
130
+ tail.shift();
131
+ droppedTail += 1;
132
+ continue;
133
+ }
134
+ if (msgCap > 40) {
135
+ msgCap = Math.max(40, Math.floor(msgCap / 2));
136
+ continue;
137
+ }
138
+ if (tail.length === 1) {
139
+ tail = [];
140
+ droppedTail += 1;
141
+ continue;
142
+ }
143
+ return input;
144
+ }
145
+ }
146
+ /**
147
+ * Validate one answer. A wrapping code fence or one pair of wrapping quotes is
148
+ * formatting noise and comes off; the rest is the human's next prompt verbatim.
149
+ * Over-cap or empty is INVALID (never truncated) — the caller retries once and
150
+ * then refuses with the measured numbers.
151
+ */
152
+ export function validateSuggestion(raw, maxChars) {
153
+ let text = raw.trim();
154
+ const fence = text.match(/^`{2,}[^\n]*\n([\s\S]*?)\n`{2,}\s*$/);
155
+ if (fence?.[1] !== undefined)
156
+ text = fence[1].trim();
157
+ const quoted = text.match(/^"([\s\S]+)"$/) ?? text.match(/^“([\s\S]+)”$/);
158
+ if (quoted?.[1] !== undefined)
159
+ text = quoted[1].trim();
160
+ if (text === '')
161
+ return { ok: false, reason: 'the model returned no text' };
162
+ if (text.length > maxChars)
163
+ return { ok: false, reason: `the answer is ${text.length} characters, over the ${maxChars}-char cap` };
164
+ return { ok: true, text };
165
+ }
166
+ /**
167
+ * The watermarks this surface caches per session: the seq of the last surface
168
+ * event at generation. A new completed turn moves the seq and retires the
169
+ * suggestion; a restart simply loses the cache (it is a convenience, never truth).
170
+ */
171
+ export function createPromptSurface(deps) {
172
+ const cache = new Map();
173
+ const inflight = new Map();
174
+ const refuse = (reason) => ({ ok: false, text: reason });
175
+ const effective = () => effectivePromptSpec(deps.getSetting(PROMPT_SETTING_KEY), deps.config.model);
176
+ const run = async (session, seq) => {
177
+ const { spec } = effective();
178
+ const { route, reason } = resolvePromptRoute(spec, deps.conversationRoute());
179
+ if (route === null)
180
+ return refuse(`prompt suggestions cannot run: ${reason ?? 'no route'}`);
181
+ let input;
182
+ let chaptersCount = 0;
183
+ try {
184
+ const chapters = await deps.chaptersOf(session.id);
185
+ chaptersCount = chapters.length;
186
+ const tail = tailFromEvents(session.events);
187
+ input = buildSuggestInput({ title: session.title, chapters, tail }, deps.config.contextChars, deps.config.maxChars);
188
+ }
189
+ catch (error) {
190
+ return refuse(`could not assemble the context: ${String(error?.message ?? error).slice(0, 160)}`);
191
+ }
192
+ const cap = `Remember: reply with ONLY the suggested prompt, plain text, at most ${deps.config.maxChars} characters.`;
193
+ try {
194
+ let text = await deps.fetch(input, route);
195
+ let verdict = validateSuggestion(text, deps.config.maxChars);
196
+ if (!verdict.ok) {
197
+ deps.warn?.(`prompt retry (${verdict.reason}) after ${chaptersCount} chapter line(s) + tail`);
198
+ text = await deps.fetch(`${input}\n${cap}`, route);
199
+ verdict = validateSuggestion(text, deps.config.maxChars);
200
+ }
201
+ if (!verdict.ok)
202
+ return refuse(`no recommendation: ${verdict.reason}`);
203
+ cache.set(session.id, { text: verdict.text, seq });
204
+ return { ok: true, text: verdict.text };
205
+ }
206
+ catch (error) {
207
+ const message = `prompt model call failed: ${String(error?.message ?? error).slice(0, 160)}`;
208
+ deps.warn?.(message);
209
+ return refuse(message);
210
+ }
211
+ };
212
+ return {
213
+ effective,
214
+ async setModel(spec) {
215
+ if (spec === null)
216
+ await deps.deleteSetting(PROMPT_SETTING_KEY);
217
+ else
218
+ await deps.putSetting(PROMPT_SETTING_KEY, spec.trim());
219
+ cache.clear();
220
+ inflight.clear();
221
+ },
222
+ async suggest(session) {
223
+ const { spec } = effective();
224
+ if (spec === '')
225
+ return refuse('prompt suggestions are off — enable with /chapters-prompt model <provider/model>');
226
+ const events = session.events;
227
+ if (events.length === 0)
228
+ return refuse('nothing to recommend yet — the session has no surface events');
229
+ const seq = events[events.length - 1].seq;
230
+ const tail = tailFromEvents(events);
231
+ if (!tail.some((m) => m.role === 'assistant'))
232
+ return refuse('nothing to recommend yet — no completed assistant turn');
233
+ const fresh = cache.get(session.id);
234
+ if (fresh !== undefined && fresh.seq === seq)
235
+ return { ok: true, text: fresh.text };
236
+ const pending = inflight.get(session.id);
237
+ if (pending !== undefined)
238
+ return await pending;
239
+ const job = run(session, seq).finally(() => { inflight.delete(session.id); });
240
+ inflight.set(session.id, job);
241
+ return await job;
242
+ },
243
+ statusLine() {
244
+ const { spec, source } = effective();
245
+ if (spec === '')
246
+ return `Prompt suggestions: OFF${source === 'override' ? ' (explicitly off — /chapters-prompt model <provider/model> to enable)' : ' — enable with /chapters-prompt model <provider/model>'}`;
247
+ return `Prompt suggestions: ON — ${spec} (${source}; the composer ghost offers its answer, accepted only by your keystroke)`;
248
+ },
249
+ };
250
+ }
251
+ /** Active chapters of one registry row, mapped to the facts the input uses. */
252
+ export function promptChaptersOf(state) {
253
+ return state.chapters.filter(isActiveChapter).map((c) => ({ number: c.number, title: c.title, summary: c.summary }));
254
+ }
255
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
package/lib/registry.d.ts CHANGED
@@ -31,6 +31,15 @@ export interface SessionState {
31
31
  */
32
32
  nextPlanNumber: number;
33
33
  chapters: ChapterRecord[];
34
+ /**
35
+ * Write counter, bumped by the store on every accepted put (#59). It exists so a read-modify-write
36
+ * can DETECT that the row moved underneath it — the row is one whole value and five doors plus the
37
+ * turn/end listener write it, so "last writer wins" is not a theoretical concern: in the field a
38
+ * stale enrichment put erased a compaction's committed manifest, reservation, `finalized` entry and
39
+ * chapter record while leaving its file on disk. Optional-with-default so a stored row from before
40
+ * the field parses unchanged.
41
+ */
42
+ rev?: number | undefined;
34
43
  /** attemptKey -> numbers, so an idempotent retry reuses rather than duplicates. */
35
44
  reservations: Record<string, number[]>;
36
45
  /**
@@ -95,8 +104,37 @@ export declare function reservePlan(state: SessionState, attemptId: string, coun
95
104
  state: SessionState;
96
105
  numbers: number[];
97
106
  };
98
- /** Append verified chapter records. A record with an existing (number) identity is a no-op — retries are safe. */
107
+ /**
108
+ * Append verified chapter records.
109
+ *
110
+ * Identity is `(number, sha256)`, never `number` alone (#59). An identical record is a no-op — that is
111
+ * what makes a retry after a crash safe. A record reusing a number for DIFFERENT bytes is not a retry,
112
+ * it is a collision: one door's reservation was rewound by a lost update and another door has now been
113
+ * handed a number whose file already exists. Measured in the field: chapter 15 was spent twice in
114
+ * `session-aac0254e`, and number-only dedupe silently DROPPED the second record while its 960 KB file
115
+ * stayed on disk — a chapter no TOC cites and no tool can find. Refusing is the only honest option.
116
+ */
117
+ /**
118
+ * The ONE test for "may this chapter be shown as knowledge?" (#60). Every derived layer — the TOC a
119
+ * child is seeded with, the enrichment queue, anything else that reads `state.chapters` — goes through
120
+ * this predicate, because a repair that left a husk in the corpus would be a lie a later reader cannot
121
+ * see past. Records written before this field existed are `undefined`, i.e. active: legacy archives
122
+ * are never silently hidden by a new filter.
123
+ */
124
+ export declare function isActiveChapter(c: {
125
+ status?: 'superseded' | undefined;
126
+ }): boolean;
99
127
  export declare function appendChapters(state: SessionState, records: readonly ChapterRecord[]): SessionState;
128
+ /**
129
+ * Free an attempt's retry key WITHOUT un-spacing its numbers (#59). Reserve-before-write exists so a
130
+ * crash cannot re-issue a number whose file may already be on disk; that guarantee must not be undone
131
+ * here. What this does is stop a burned key from living forever: a door that reserved and then failed
132
+ * (a read-back that did not verify, a refusal after writing) currently leaves `reservations[k]` behind
133
+ * with no file and no record, which is the state the field showed at numbers 20 and 37.
134
+ *
135
+ * `numbersSpent` stays monotonic — only the attempt key goes.
136
+ */
137
+ export declare function releaseReservation(state: SessionState, attemptId: string): SessionState;
100
138
  /**
101
139
  * Commit point for a continuation: the child inherits the PARENT's root and
102
140
  * gets its direct parent recorded. Set once, never rewritten — re-pointing a
package/lib/registry.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export const freshSession = (sessionId) => ({
2
+ rev: 0,
2
3
  parentSession: null,
3
4
  rootSession: sessionId,
4
5
  nextChapterNumber: 1,
@@ -36,11 +37,22 @@ export function reserve(state, attemptId, count) {
36
37
  }
37
38
  return { state, numbers: [...existing] };
38
39
  }
39
- const numbers = Array.from({ length: count }, (_, i) => state.nextChapterNumber + i);
40
+ // Never hand out a number the row already carries. A committed number with no matching
41
+ // reservation is a real state, not a bug: an orphan row written before this field existed, or a
42
+ // chapter whose record a lost update erased while its file survived (#59). Allocating into it would
43
+ // put two `0NN-*.md` in one tree, so allocation SKIPS it and spends the next free number instead.
44
+ const taken = new Set(state.chapters.map((c) => c.number));
45
+ const numbers = [];
46
+ let cursor = state.nextChapterNumber;
47
+ while (numbers.length < count) {
48
+ if (!taken.has(cursor))
49
+ numbers.push(cursor);
50
+ cursor += 1;
51
+ }
40
52
  return {
41
53
  state: {
42
54
  ...state,
43
- nextChapterNumber: state.nextChapterNumber + count,
55
+ nextChapterNumber: cursor,
44
56
  reservations: { ...state.reservations, [attemptId]: numbers },
45
57
  },
46
58
  numbers,
@@ -59,23 +71,78 @@ export function reservePlan(state, attemptId, count) {
59
71
  }
60
72
  return { state, numbers: [...existing] };
61
73
  }
62
- const numbers = Array.from({ length: count }, (_, i) => state.nextPlanNumber + i);
74
+ // Same skip rule as reserve(): plan documents are numbered independently but the collision
75
+ // hazard is identical (a lost update rewinding nextPlanNumber re-issues `plan-003` over a twin).
76
+ const taken = new Set(state.chapters.flatMap((c) => c.planDocs ?? [])
77
+ .map((d) => /plan-(\d+)-/.exec(d.path)?.[1]).filter((n) => n !== undefined).map(Number));
78
+ const numbers = [];
79
+ let cursor = state.nextPlanNumber;
80
+ while (numbers.length < count) {
81
+ if (!taken.has(cursor))
82
+ numbers.push(cursor);
83
+ cursor += 1;
84
+ }
63
85
  return {
64
86
  state: {
65
87
  ...state,
66
- nextPlanNumber: state.nextPlanNumber + count,
88
+ nextPlanNumber: cursor,
67
89
  reservations: { ...state.reservations, [key]: numbers },
68
90
  },
69
91
  numbers,
70
92
  };
71
93
  }
72
- /** Append verified chapter records. A record with an existing (number) identity is a no-op — retries are safe. */
94
+ /**
95
+ * Append verified chapter records.
96
+ *
97
+ * Identity is `(number, sha256)`, never `number` alone (#59). An identical record is a no-op — that is
98
+ * what makes a retry after a crash safe. A record reusing a number for DIFFERENT bytes is not a retry,
99
+ * it is a collision: one door's reservation was rewound by a lost update and another door has now been
100
+ * handed a number whose file already exists. Measured in the field: chapter 15 was spent twice in
101
+ * `session-aac0254e`, and number-only dedupe silently DROPPED the second record while its 960 KB file
102
+ * stayed on disk — a chapter no TOC cites and no tool can find. Refusing is the only honest option.
103
+ */
104
+ /**
105
+ * The ONE test for "may this chapter be shown as knowledge?" (#60). Every derived layer — the TOC a
106
+ * child is seeded with, the enrichment queue, anything else that reads `state.chapters` — goes through
107
+ * this predicate, because a repair that left a husk in the corpus would be a lie a later reader cannot
108
+ * see past. Records written before this field existed are `undefined`, i.e. active: legacy archives
109
+ * are never silently hidden by a new filter.
110
+ */
111
+ export function isActiveChapter(c) {
112
+ return c.status !== 'superseded';
113
+ }
73
114
  export function appendChapters(state, records) {
74
- const known = new Set(state.chapters.map((c) => c.number));
75
- const fresh = records.filter((r) => !known.has(r.number));
76
- if (fresh.length === 0)
115
+ const byNumber = new Map(state.chapters.map((c) => [c.number, c]));
116
+ const fresh = [];
117
+ for (const r of records) {
118
+ const prior = byNumber.get(r.number);
119
+ if (prior !== undefined) {
120
+ if (prior.sha256 !== r.sha256 || prior.path !== r.path) {
121
+ throw new Error(`appendChapters: chapter number ${r.number} is already committed with different content `
122
+ + `(${prior.path} sha ${prior.sha256.slice(0, 12)}…) — refusing to overwrite or silently drop `
123
+ + `${r.path} (sha ${r.sha256.slice(0, 12)}…). A reused number means a lost registry update (#59).`);
124
+ }
125
+ continue; // identical retry
126
+ }
127
+ fresh.push(r);
128
+ byNumber.set(r.number, r);
129
+ }
130
+ return fresh.length === 0 ? state : { ...state, chapters: [...state.chapters, ...fresh] };
131
+ }
132
+ /**
133
+ * Free an attempt's retry key WITHOUT un-spacing its numbers (#59). Reserve-before-write exists so a
134
+ * crash cannot re-issue a number whose file may already be on disk; that guarantee must not be undone
135
+ * here. What this does is stop a burned key from living forever: a door that reserved and then failed
136
+ * (a read-back that did not verify, a refusal after writing) currently leaves `reservations[k]` behind
137
+ * with no file and no record, which is the state the field showed at numbers 20 and 37.
138
+ *
139
+ * `numbersSpent` stays monotonic — only the attempt key goes.
140
+ */
141
+ export function releaseReservation(state, attemptId) {
142
+ if (state.reservations[attemptId] === undefined)
77
143
  return state;
78
- return { ...state, chapters: [...state.chapters, ...fresh] };
144
+ const { [attemptId]: _gone, ...rest } = state.reservations;
145
+ return { ...state, reservations: rest };
79
146
  }
80
147
  /**
81
148
  * Commit point for a continuation: the child inherits the PARENT's root and
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAmEA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAiB,EAAgB,EAAE,CAAC,CAAC;IAChE,GAAG,EAAE,CAAC;IACN,aAAa,EAAE,IAAI;IACnB,WAAW,EAAE,SAAS;IACtB,iBAAiB,EAAE,CAAC;IACpB,cAAc,EAAE,CAAC;IACjB,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,EAAE;IACZ,YAAY,EAAE,EAAE;IAChB,KAAK,EAAE,EAAE;IACT,SAAS,EAAE,EAAE;CACd,CAAC,CAAA;AAgBF,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAC1B,KAAmB,EACnB,YAAoB,EACpB,IAAgG;IAEhG,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,CAAA;AACtE,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,aAAa,CAAC,KAAmB,EAAE,YAAoB,EAAE,OAA0B;IACjG,IAAI,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC7D,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,EAAE,CAAA;AACtF,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAmB,EAAE,YAAoB,EAAW,EAAE,CAChF,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,SAAS,CAAA;AAE7C,wEAAwE;AACxE,MAAM,UAAU,OAAO,CAAC,KAAmB,EAAE,SAAiB,EAAE,KAAa;IAC3E,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,kDAAkD,KAAK,EAAE,CAAC,CAAA;IAC5E,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;IAC9C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAC9B,yEAAyE;YACzE,oEAAoE;YACpE,yDAAyD;YACzD,MAAM,IAAI,KAAK,CAAC,qBAAqB,SAAS,mBAAmB,QAAQ,CAAC,MAAM,uBAAuB,KAAK,EAAE,CAAC,CAAA;QACjH,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAA;IAC1C,CAAC;IACD,uFAAuF;IACvF,gGAAgG;IAChG,oGAAoG;IACpG,kGAAkG;IAClG,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IAC1D,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,MAAM,GAAG,KAAK,CAAC,iBAAiB,CAAA;IACpC,OAAO,OAAO,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,MAAM,IAAI,CAAC,CAAA;IACb,CAAC;IACD,OAAO;QACL,KAAK,EAAE;YACL,GAAG,KAAK;YACR,iBAAiB,EAAE,MAAM;YACzB,YAAY,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;SAC9D;QACD,OAAO;KACR,CAAA;AACH,CAAC;AACD,uEAAuE;AACvE,MAAM,UAAU,WAAW,CACzB,KAAmB,EACnB,SAAiB,EACjB,KAAa;IAEb,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,sDAAsD,KAAK,EAAE,CAAC,CAAA;IAChF,CAAC;IACD,MAAM,GAAG,GAAG,QAAQ,SAAS,EAAE,CAAA;IAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;IACxC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,mBAAmB,QAAQ,CAAC,MAAM,uBAAuB,KAAK,EAAE,CAAC,CAAA;QAC/G,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAA;IAC1C,CAAC;IACD,2FAA2F;IAC3F,iGAAiG;IACjG,MAAM,KAAK,GAAG,IAAI,GAAG,CACnB,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAAuD,CAAC,QAAQ,IAAI,EAAE,CAAC;SACnG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CACvG,CAAA;IACD,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,MAAM,GAAG,KAAK,CAAC,cAAc,CAAA;IACjC,OAAO,OAAO,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,MAAM,IAAI,CAAC,CAAA;IACb,CAAC;IACD,OAAO;QACL,KAAK,EAAE;YACL,GAAG,KAAK;YACR,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE;SACxD;QACD,OAAO;KACR,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,CAAwC;IACtE,OAAO,CAAC,CAAC,MAAM,KAAK,YAAY,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAmB,EAAE,OAAiC;IACnF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAClE,MAAM,KAAK,GAAoB,EAAE,CAAA;IACjC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QACpC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;gBACvD,MAAM,IAAI,KAAK,CACb,kCAAkC,CAAC,CAAC,MAAM,+CAA+C;sBACvF,IAAI,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,8CAA8C;sBAC7F,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,yDAAyD,CACnG,CAAA;YACH,CAAC;YACD,SAAQ,CAAC,kBAAkB;QAC7B,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACb,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAC3B,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,EAAE,CAAA;AAC3F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB,EAAE,SAAiB;IACvE,IAAI,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC7D,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC,YAAY,CAAA;IAC1D,OAAO,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAA;AACzC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAmB,EAAE,eAAuB,EAAE,iBAAyB;IAC/F,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,aAAa,KAAK,eAAe,IAAI,KAAK,CAAC,WAAW,KAAK,iBAAiB;YAAE,OAAO,KAAK,CAAA;QACpG,MAAM,IAAI,KAAK,CACb,+CAA+C,KAAK,CAAC,aAAa,UAAU,KAAK,CAAC,WAAW,IAAI;cAC/F,+BAA+B,eAAe,UAAU,iBAAiB,GAAG,CAC/E,CAAA;IACH,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAA;AACrF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAA6C,EAAE,SAAiB,EAAE,QAAQ,GAAG,IAAI;IAC5G,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,IAAI,MAAM,GAAuB,SAAS,CAAA;IAC1C,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAA;QAC9E,IAAI,IAAI,CAAC,IAAI,IAAI,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,EAAE,CAAC,CAAA;QACtF,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAChB,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAA;QACzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,CAAC,MAAM,CAAC,CAAA,CAAC,wCAAwC;YAClF,MAAM,IAAI,KAAK,CAAC,0CAA0C,MAAM,wBAAwB,CAAC,CAAA;QAC3F,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QACpB,MAAM,GAAG,KAAK,CAAC,aAAa,IAAI,SAAS,CAAA;IAC3C,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAaD;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,IAAuB,EACvB,GAA6C,EAC7C,MAAuD;IAEvD,MAAM,OAAO,GAAiB,EAAE,CAAA;IAChC,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5B,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9E,OAAO,CAAC,IAAI,CAAC;gBACX,aAAa,EAAE,SAAS;gBACxB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC;aACxB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,gBAAgB,CAAC,KAAmB,EAAE,GAAwB;IAC5E,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAC5D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IAC5H,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAA;AAC/D,CAAC"}
package/lib/render.d.ts CHANGED
@@ -20,9 +20,37 @@ export declare function artifactRef(text: string, opts: {
20
20
  * Validate model-supplied ranges. Never trust them (hard rule): an overlap duplicates archived text and a
21
21
  * gap silently drops history, so both are errors here rather than data that quietly goes missing.
22
22
  */
23
- export declare function validateRanges(chapters: ChapterRange[], archiveCeiling: number): ChapterRange[];
24
- /** Enumerate tool results in a range so the model can decide keep/defer against real sizes. */
25
- export declare function toolResultCandidates(events: SessionEventLike[], range: ChapterRange): ToolResultCandidate[];
23
+ export declare function validateRanges(chapters: ChapterRange[], archiveCeiling: number,
24
+ /** The watermark: ranges must start HERE, not wherever the submitter liked (#59). */
25
+ archiveFloor?: number): ChapterRange[];
26
+ /**
27
+ * Ranges whose render blew past the oversize ceiling (#59). `chapterTokenTarget` stays the SOFT target
28
+ * — split at the next boundary, never clip — and a modest overshoot is a warning, because a single
29
+ * indivisible turn can legitimately exceed it. Past `target × factor` the chapter is not a chapter:
30
+ * nobody can `read` it back inside the window it exists for, which is the whole point of archiving.
31
+ * The field held an 89,982-token chapter, an unrepaired 237,870-token orphan and eleven over 32K.
32
+ */
33
+ export declare function oversizeRanges(rendered: readonly {
34
+ range: ChapterRange;
35
+ stats: {
36
+ estimatedTokens: number;
37
+ };
38
+ }[], chapterTokenTarget: number, oversizeFactor: number): string[];
39
+ /**
40
+ * The capped public form (#61). `chapters_segment` once emitted 49,932 characters of its OWN output while
41
+ * also reporting `(Omitted 275266 bytes …)` — ≈81K est tokens into a 32K-class window, asking a small
42
+ * model to judge a candidate list it never received. Ranking is by size descending, so what a cap
43
+ * removes is the SMALL stuff: the judgment the model is actually asked for stays intact.
44
+ *
45
+ * The caller reports what was withheld — see `candidateTotals` — because a capped list that does not say
46
+ * it is capped is a silent truncation, and AGENTS.md forbids truncating to fit.
47
+ */
48
+ export declare function toolResultCandidates(events: SessionEventLike[], range: ChapterRange, capTokens?: number): ToolResultCandidate[];
49
+ /** Totals for the WHOLE candidate set, so a capped response can name what it left out. */
50
+ export declare function candidateTotals(events: SessionEventLike[], range: ChapterRange): {
51
+ count: number;
52
+ bytes: number;
53
+ };
26
54
  /**
27
55
  * Render one chapter. `overrides` is the model's sparse exception list; anything not named follows the
28
56
  * default rule (small results inline, large ones deferred). Either way the artifact is written, so a wrong