@hviana/sema 0.8.2 → 0.8.5

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 (126) hide show
  1. package/AGENTS.md +38 -37
  2. package/README.md +17 -38
  3. package/TRADEMARKS.md +0 -1
  4. package/dist/example/demo.js +85 -34
  5. package/dist/src/config.d.ts +11 -0
  6. package/dist/src/config.js +2 -0
  7. package/dist/src/geometry.d.ts +21 -10
  8. package/dist/src/geometry.js +21 -12
  9. package/dist/src/meter.d.ts +62 -0
  10. package/dist/src/meter.js +62 -0
  11. package/dist/src/mind/articulation.js +1 -1
  12. package/dist/src/mind/attention.d.ts +4 -0
  13. package/dist/src/mind/attention.js +167 -17
  14. package/dist/src/mind/canonical.d.ts +16 -0
  15. package/dist/src/mind/canonical.js +41 -0
  16. package/dist/src/mind/derivation.d.ts +201 -0
  17. package/dist/src/mind/derivation.js +327 -0
  18. package/dist/src/mind/graph-search.d.ts +2 -1
  19. package/dist/src/mind/graph-search.js +70 -29
  20. package/dist/src/mind/match.d.ts +3 -1
  21. package/dist/src/mind/match.js +7 -3
  22. package/dist/src/mind/mechanisms/alu.js +0 -2
  23. package/dist/src/mind/mechanisms/cast.d.ts +1 -5
  24. package/dist/src/mind/mechanisms/cast.js +16 -19
  25. package/dist/src/mind/mechanisms/confluence.d.ts +0 -3
  26. package/dist/src/mind/mechanisms/confluence.js +27 -9
  27. package/dist/src/mind/mechanisms/cover.js +17 -20
  28. package/dist/src/mind/mechanisms/extraction.d.ts +0 -1
  29. package/dist/src/mind/mechanisms/extraction.js +13 -8
  30. package/dist/src/mind/mechanisms/prefix-completion.js +0 -1
  31. package/dist/src/mind/mechanisms/recall.d.ts +0 -1
  32. package/dist/src/mind/mechanisms/recall.js +40 -13
  33. package/dist/src/mind/mechanisms/reference.js +3 -4
  34. package/dist/src/mind/mind.d.ts +4 -2
  35. package/dist/src/mind/mind.js +5 -4
  36. package/dist/src/mind/pipeline-mechanism.d.ts +7 -3
  37. package/dist/src/mind/pipeline.js +136 -44
  38. package/dist/src/mind/primitives.js +9 -1
  39. package/dist/src/mind/rationale.d.ts +21 -5
  40. package/dist/src/mind/rationale.js +16 -21
  41. package/dist/src/mind/reasoning.d.ts +12 -20
  42. package/dist/src/mind/reasoning.js +190 -106
  43. package/dist/src/mind/recognition.js +4 -8
  44. package/dist/src/mind/resonance.js +20 -1
  45. package/dist/src/mind/trace.js +1 -0
  46. package/dist/src/mind/traverse.js +6 -2
  47. package/dist/src/mind/types.d.ts +36 -13
  48. package/dist/src/mind/types.js +6 -3
  49. package/docs/INDEX.md +23 -24
  50. package/docs/INVARIANTS.md +16 -17
  51. package/docs/architecture/bounded-reads.md +5 -5
  52. package/docs/architecture/closure.md +65 -0
  53. package/docs/architecture/commonality.md +29 -20
  54. package/docs/architecture/cost-model.md +7 -7
  55. package/docs/architecture/determinism.md +7 -7
  56. package/docs/architecture/exact-vs-approximate.md +4 -4
  57. package/docs/architecture/factored-machinery.md +14 -14
  58. package/docs/architecture/match-project.md +2 -3
  59. package/docs/architecture/mechanism-market.md +16 -16
  60. package/docs/architecture/meter.md +10 -11
  61. package/docs/architecture/store.md +4 -4
  62. package/docs/architecture/thresholds.md +1 -1
  63. package/docs/failures/tempting-but-wrong.md +14 -5
  64. package/docs/harness/gates.md +7 -7
  65. package/docs/mechanisms/cast.md +2 -2
  66. package/docs/mechanisms/cover.md +4 -5
  67. package/docs/mechanisms/extraction.md +7 -7
  68. package/docs/mechanisms/recall.md +8 -9
  69. package/example/demo.ts +90 -37
  70. package/jsr.json +1 -1
  71. package/package.json +1 -1
  72. package/src/alu/README.md +11 -12
  73. package/src/config.ts +13 -0
  74. package/src/geometry.ts +21 -13
  75. package/src/meter.ts +62 -0
  76. package/src/mind/articulation.ts +0 -1
  77. package/src/mind/attention.ts +169 -17
  78. package/src/mind/canonical.ts +43 -0
  79. package/src/mind/derivation.ts +473 -0
  80. package/src/mind/graph-search.ts +76 -34
  81. package/src/mind/match.ts +7 -3
  82. package/src/mind/mechanisms/alu.ts +0 -2
  83. package/src/mind/mechanisms/cast.ts +20 -22
  84. package/src/mind/mechanisms/confluence.ts +27 -13
  85. package/src/mind/mechanisms/cover.ts +17 -20
  86. package/src/mind/mechanisms/extraction.ts +13 -9
  87. package/src/mind/mechanisms/prefix-completion.ts +0 -1
  88. package/src/mind/mechanisms/recall.ts +39 -13
  89. package/src/mind/mechanisms/reference.ts +2 -3
  90. package/src/mind/mind.ts +6 -4
  91. package/src/mind/pipeline-mechanism.ts +7 -3
  92. package/src/mind/pipeline.ts +160 -52
  93. package/src/mind/primitives.ts +9 -1
  94. package/src/mind/rationale.ts +27 -23
  95. package/src/mind/reasoning.ts +227 -120
  96. package/src/mind/recognition.ts +4 -8
  97. package/src/mind/resonance.ts +19 -1
  98. package/src/mind/trace.ts +1 -0
  99. package/src/mind/traverse.ts +7 -5
  100. package/src/mind/types.ts +41 -15
  101. package/test/105-derive-through-reports-its-refusal.test.mjs +24 -0
  102. package/test/118-the-join-reaches-a-key-off-the-cut.test.mjs +74 -0
  103. package/test/119-the-work-does-not-grow-with-the-corpus.test.mjs +122 -0
  104. package/test/120-composition-is-consequence.test.mjs +132 -0
  105. package/test/121-the-extension-does-not-grow-with-the-corpus.test.mjs +128 -0
  106. package/test/122-the-climb-search-does-not-grow-with-the-corpus.test.mjs +117 -0
  107. package/test/123-the-paired-formulas-agree.test.mjs +90 -0
  108. package/test/125-the-post-grounding-branch-publishes-its-operand.test.mjs +51 -0
  109. package/test/126-the-pipeline-does-not-name-mechanisms.test.mjs +42 -0
  110. package/test/128-the-leads-somewhere-pair-agrees.test.mjs +83 -0
  111. package/test/129-the-trace-payload-shape.test.mjs +164 -0
  112. package/test/133-the-decision-point-renders-the-state.test.mjs +204 -0
  113. package/test/134-the-law-explains-the-engines-own-refusal.test.mjs +237 -0
  114. package/test/135-one-law-any-producer.test.mjs +289 -0
  115. package/test/136-the-two-named-limits.test.mjs +205 -0
  116. package/test/137-the-law-lives-once-and-below.test.mjs +400 -0
  117. package/test/138-the-remainder-drains-only-what-a-move-declares.test.mjs +62 -0
  118. package/test/139-the-witness-is-engagement-not-explanation.test.mjs +51 -0
  119. package/test/140-irrelevant-supply-does-not-change-an-answer.test.mjs +48 -0
  120. package/test/141-the-question-is-paid-at-construction.test.mjs +98 -0
  121. package/test/32-confluence.test.mjs +68 -0
  122. package/test/36-already-answered-fusion.test.mjs +20 -2
  123. package/test/37-cluster-dispersion-fusion.test.mjs +30 -3
  124. package/test/38-reason-restate-guard.test.mjs +28 -2
  125. package/test/43-cast-analog-seat.test.mjs +10 -0
  126. package/test/55-cost-meter.test.mjs +862 -0
@@ -0,0 +1,473 @@
1
+ // derivation.ts — the derivation unit and the one closure law it is governed by.
2
+ //
3
+ // THE LAW, in the quantities this engine already has:
4
+ //
5
+ // A derivation is CLOSED for a query when the structure it built accounts for
6
+ // the query's REMAINDER under the engine's own identity and admission rules,
7
+ // and every step is priced on the one ladder.
8
+ //
9
+ // It is written ONCE, here, because that is where a rule has to live — in the
10
+ // behaviour of the code and at the sites it governs (this repository's own
11
+ // history records the same conclusion: a law written as a document was deleted,
12
+ // and the reason given was that the rule belongs in the code). The tiers that
13
+ // can evaluate it ask it; the tiers that cannot say so and are named below.
14
+ //
15
+ // WHAT THE LAW READS, AND WHAT IT NEVER READS. It reads the state and the
16
+ // proposed continuation, and nothing else: no mechanism, no store, no context,
17
+ // no producer. Its whole purpose is that a product may be consumed by ANY
18
+ // transition the law admits, so that composition is the next transition of the
19
+ // same state rather than an operation with its own dispatch.
20
+ //
21
+ // THE TWO WITNESSES ARE THE LAYER'S. `contains` (does the transition's
22
+ // structure hold the product? a node in its tree, or one contiguous byte run)
23
+ // and `moves` (does it reach structure this derivation has not consumed?) are
24
+ // resolved by the layer that knows the structure, and handed in. The law never
25
+ // re-derives them, and never probes the store to ask: a predicate that would
26
+ // scan the store for every candidate would raise the cost of a step, which is
27
+ // why `traverse.ts`'s own admission predicate is LENT to the chart rather than
28
+ // called through it.
29
+ //
30
+ // THE UNIT: product, accounted, remainder, cost, and the two declarations a
31
+ // producer makes about its own result (`fixed`, `used`). No identity field
32
+ // (the product is content-addressed, so its identity is `resolve(product)` — a
33
+ // pure function, and a cache is not a field); no structure field (that is what
34
+ // identity is computed over); no frontier field (a continuation is computed by
35
+ // the layer that can offer one); no producer field (docs/architecture's own
36
+ // contract: `provenance` is observability and nothing compares it); and no
37
+ // count of any kind — no depth, hop, visited, once or cap.
38
+ //
39
+ // FIVE QUESTIONS, FIVE OWNERS — and no two of them may be answered by one fact:
40
+ //
41
+ // ENGAGEMENT does the step touch the question's material? `carries`: admits,
42
+ // consumes nothing
43
+ // PROGRESS does it reach structure not yet consumed? `reaches`: admits
44
+ // AND consumes what the
45
+ // step's window carries
46
+ // ACCOUNTING what does the step price? the span, into
47
+ // `accounted`
48
+ // CLOSURE is the question's material all accounted for? `closed` — the law
49
+ // TERMINATION why does the walk stop at all? the LAYER: its offer
50
+ // ends, or the law refuses
51
+ // one; never a depth
52
+ // CYCLES why is a loop not a walk? the LAYER's consumed
53
+ // set; the law only sees
54
+ // that a cycle never
55
+ // closes it (test/138.1)
56
+ //
57
+ // A step can progress without closing the derivation; a derivation can be closed
58
+ // with no further transition (the grounding built it closed); and neither of those
59
+ // is termination.
60
+ //
61
+ // THE REMAINDER TRAVELS UNCHANGED, and this was measured, not assumed: letting
62
+ // `advance` drain the witness made a step that engaged the gap CLOSE the
63
+ // derivation, the next step was admitted unconstrained, and test/110's
64
+ // three-link chain drifted one hop past its satisfying answer. A step ENGAGES
65
+ // the question's leftover; it does not consume it. Termination is therefore not
66
+ // the law's: it is the walker's cycle protection over a finite graph.
67
+ //
68
+ // TWO LIMITS ARE PROVED AND LEFT OUT (test/136 pins both, with the measurements):
69
+ // 1. the CHART cannot evaluate accounting — its interface has no parameter for
70
+ // it, and carrying it per chart item was measured and rejected. The chart
71
+ // evaluates the same law read off an item: identity is the item's `key`,
72
+ // continuation is the rule's existence, progress is the frontier advancing,
73
+ // closure is the goal test, and `fix` is `fixed`.
74
+ // 2. closure by the QUERY'S POSITION in the graph is not a term of the unit:
75
+ // `reason`'s echo guards stop a derivation although the remainder is not
76
+ // empty, and recall's reverse-recall tiers close it with an empty
77
+ // accounting. That is a fact about the asker's material in the store,
78
+ // available only to the layer holding it — recorded as a limit, not patched
79
+ // into the unit as a foreign key.
80
+ //
81
+ // LAYERING: this module imports `../bytes.js` only. It sits BELOW
82
+ // `graph-search.ts`, `match.ts`, `rationale.ts` and `pipeline.ts`, so each may
83
+ // ask it and none may be asked by it. The span algebra lives here too: it is
84
+ // the law's vocabulary — gap arithmetic over the asker's bytes — and not the
85
+ // tracer's, which is why it moved out of `rationale.ts`.
86
+
87
+ import { indexOf } from "../bytes.js";
88
+
89
+ /** A half-open `[start, end)` span of the asker's own bytes. */
90
+ export type Span = readonly [number, number];
91
+
92
+ // ── The span algebra: the law's vocabulary ──────────────────────────────────
93
+
94
+ /** The BYTE COUNT of a span list — what the currency calls `unaccounted` in
95
+ * `weight = moves + PASS·unaccounted`. ONE definition: this was four copies of
96
+ * the same `reduce` before the architecture audit collapsed them. */
97
+ export function unaccountedBytes(spans: ReadonlyArray<Span>): number {
98
+ let total = 0;
99
+ for (const [a, b] of spans) total += b - a;
100
+ return total;
101
+ }
102
+
103
+ /** The `[start, end)` gaps of `[0, queryLen)` NOT covered by `accounted` — the
104
+ * union-of-spans reading the ladder prices at PASS per byte, and the raw
105
+ * material every closure decision measures. Clipped to the question, sorted
106
+ * and merged, so two overlapping spans account for their union once. */
107
+ export function unexplainedSpans(
108
+ queryLen: number,
109
+ accounted: ReadonlyArray<Span>,
110
+ ): Array<[number, number]> {
111
+ const sorted = accounted
112
+ .map(([s, e]) =>
113
+ [Math.max(0, s), Math.min(queryLen, e)] as [number, number]
114
+ )
115
+ .filter(([s, e]) => e > s)
116
+ .sort((a, b) => a[0] - b[0]);
117
+ const gaps: Array<[number, number]> = [];
118
+ let reach = 0;
119
+ for (const [s, e] of sorted) {
120
+ if (s > reach) gaps.push([reach, s]);
121
+ if (e > reach) reach = e;
122
+ }
123
+ if (reach < queryLen) gaps.push([reach, queryLen]);
124
+ return gaps;
125
+ }
126
+
127
+ /** THE REMAINDER: what no step has accounted for, with every span below one
128
+ * river-fold quantum dropped. `W` is the mind's own line between bridging
129
+ * punctuation and a substantive phrase — the same floor `liftedScaffolding`
130
+ * and the honesty-density bar use — so a remainder under it licenses nothing
131
+ * and blocks nothing. This is the law's measure. */
132
+ export function remainderOf(
133
+ queryLen: number,
134
+ explained: ReadonlyArray<Span>,
135
+ W: number,
136
+ ): Array<[number, number]> {
137
+ return unexplainedSpans(queryLen, explained).filter(([a, b]) => b - a >= W);
138
+ }
139
+
140
+ /** A WITNESS — what a transition carries, in one reading: the SPAN it accounts
141
+ * for, and the WINDOW of the question the product itself holds. The window is
142
+ * present only when the product holds question material, and it is the ONLY
143
+ * thing the question's remainder may be consumed by. */
144
+ export interface Witness {
145
+ readonly span: Span;
146
+ readonly window?: Span;
147
+ }
148
+
149
+ /** The window of `span` that `product` holds, or null: the ONE reading of
150
+ * coverage — used by {@link carries}, by the move branch, and by the GROUNDING
151
+ * when it decides what its answer has actually paid for. */
152
+ export function windowOf(
153
+ span: Span,
154
+ product: Uint8Array,
155
+ query: Uint8Array,
156
+ W: number,
157
+ ): Span | null {
158
+ const [a, b] = span;
159
+ for (let i = a; i + W <= b; i++) {
160
+ if (indexOf(product, query.subarray(i, i + W), 0) >= 0) return [i, i + W];
161
+ }
162
+ return null;
163
+ }
164
+
165
+ /** PROGRESS, by coverage: the first member of `remainder` that `product` carries
166
+ * a whole quantum of, or null when it carries none. The window is taken from
167
+ * `query`, the asker's own bytes, so the test is "this product restates a
168
+ * quantum of what was left unaccounted", never a similarity score.
169
+ *
170
+ * One witness per step, deterministically the FIRST in remainder order: the
171
+ * measure stays a single member of a finite list, which is what makes the walk
172
+ * reviewable — and, because the remainder is not drained, it is the walker's
173
+ * cycle protection (not this) that terminates a chain. */
174
+ export function carries(
175
+ remainder: ReadonlyArray<Span>,
176
+ product: Uint8Array,
177
+ query: Uint8Array,
178
+ W: number,
179
+ ): Array<Witness> | null {
180
+ for (const span of remainder) {
181
+ const window = windowOf(span, product, query, W);
182
+ if (window !== null) return [{ span, window }];
183
+ }
184
+ return null;
185
+ }
186
+
187
+ // ── The restatement reading ─────────────────────────────────────────────────
188
+
189
+ /** Whether `bytes` RESTATES the question — says nothing the asker did not just
190
+ * say — and is therefore not an answer. This is a closure condition: a
191
+ * derivation whose product is already the question has added nothing, and the
192
+ * engine asks it in five places. ONE definition, asked everywhere, with the
193
+ * DIFFERENCES between those places supplied as WITNESSES by the caller — never
194
+ * as a mechanism or a producer. If this function ever needs to know who
195
+ * produced the bytes to decide, the right conclusion is that a witness is
196
+ * missing, not that it should dispatch.
197
+ *
198
+ * `floor` is one river-fold quantum: below it, byte overlap is chance, not
199
+ * evidence — the same line `identityBar`, the bridge's `attestedQ` and
200
+ * recognition's site floor all draw. `0` disables the floor, which is the
201
+ * reading the callers that ask before any structure exists use.
202
+ *
203
+ * THE THREE READINGS the callers need, and why each is a witness rather than a
204
+ * branch here:
205
+ *
206
+ * • `proper` — a PROPER part of the question (strictly shorter). This is the
207
+ * reading every tier that rejects a fragment uses.
208
+ * • `whole` — the EQUALITY reading: only "the answer IS the question" counts,
209
+ * because the caller has already handled a proper fragment elsewhere (a
210
+ * recall tier's own subspan tests).
211
+ * • `equate` — the response's own notion of "the same text" (whatever
212
+ * `src/canon.ts` equates: case, width, whitespace). A caller that has one
213
+ * passes it; a caller that does not gets the byte-exact reading. It is the
214
+ * same fallback `resolve` already makes when an exact lookup misses.
215
+ *
216
+ * The LITERAL EXEMPTION is deliberately NOT here: whether a span is the site's
217
+ * own bytes at its own position is the CALLER's knowledge, and a caller states
218
+ * it by not asking (see `segRestatesQuery` in types.ts, which returns false for
219
+ * a literal span before reaching this). */
220
+ export function restates(
221
+ query: Uint8Array,
222
+ bytes: Uint8Array,
223
+ floor = 0,
224
+ witnesses: {
225
+ equate?: ((b: Uint8Array) => Uint8Array) | null;
226
+ proper?: boolean;
227
+ whole?: boolean;
228
+ /** THE POSITIONAL WITNESS: search from this offset, because the caller has
229
+ * established that only material at or after it counts. A transcript pasted
230
+ * into a single response is the case that needs it — a caller's own prior
231
+ * answer lies LATER in the query, after the root that would restate it — and
232
+ * the reasoner's per-root `alreadyAnswered` guard asks exactly that question.
233
+ * Omitted, the search starts at 0 and the reading is the plain one. */
234
+ from?: number;
235
+ } = {},
236
+ ): boolean {
237
+ const equate = witnesses.equate ?? null;
238
+ const from = witnesses.from ?? 0;
239
+ const q = equate === null ? query : equate(query);
240
+ const b = equate === null ? bytes : equate(bytes);
241
+ if (b.length > q.length || b.length < floor) return false;
242
+ if (witnesses.whole === true) {
243
+ return b.length === q.length && indexOf(q, b, from) >= 0;
244
+ }
245
+ if (witnesses.proper === true && b.length === q.length) return false;
246
+ return indexOf(q, b, from) >= 0;
247
+ }
248
+
249
+ /** Whether the query span `[from, to)` lies inside a COMPLETED ASSISTANT TURN —
250
+ * material the engine has already produced, so it is context rather than
251
+ * something the asker is asserting. Recognition and attention still see the
252
+ * full transcript; what excludes these spans is the closure reading "this was
253
+ * already answered", and it is a closure reading rather than a budget: a window
254
+ * inside a prior reply is not a fresh constraint.
255
+ *
256
+ * ONE definition of it. `cursor` is the CALLER's own progress through `turns`
257
+ * (they are ascending and each caller scans its candidates in ascending order),
258
+ * so the amortised search is preserved exactly and a caller passes the same
259
+ * holder for a whole scan: extracting the reading must not cost the scan. */
260
+ export function insideAnsweredTurn(
261
+ turns: ReadonlyArray<Span>,
262
+ cursor: { at: number },
263
+ from: number,
264
+ to: number,
265
+ ): boolean {
266
+ while (cursor.at < turns.length && turns[cursor.at][1] <= from) cursor.at++;
267
+ const turn = turns[cursor.at];
268
+ return turn !== undefined && turn[0] <= from && to <= turn[1];
269
+ }
270
+
271
+ // ── The unit ────────────────────────────────────────────────────────────────
272
+
273
+ /** THE derivation state — the unit that crosses one inference.
274
+ *
275
+ * Every field is read by the law or by the market's one cost ladder, and
276
+ * nothing else travels. A count of steps is a consequence (the cost), and
277
+ * cycle protection belongs to the layer that walks a graph. */
278
+ export interface DerivationState {
279
+ /** PRODUCT — the structure produced: what this derivation stands on. */
280
+ readonly product: Uint8Array;
281
+ /** ACCOUNTED — the asker's spans the producing transition priced. A COST
282
+ * quantity, and the producing mechanism's own judgement of what its answer
283
+ * explains: cover leaves its computed spans out so the PASS-bridged bytes
284
+ * they account for stay charged, while a corroborated substitution DOES
285
+ * account for its span, because the mechanism paid a move for it. */
286
+ readonly accounted: ReadonlyArray<Span>;
287
+ /** REMAINDER — the asker's material no step has accounted for, each member at
288
+ * or above one quantum. Empty means the derivation is CLOSED. */
289
+ readonly remainder: ReadonlyArray<Span>;
290
+ /** COST — position on the one ladder (`graph-search.ts`'s MICRO/STEP/CONCEPT/
291
+ * PASS); the market takes the lattice minimum over it. */
292
+ readonly cost: number;
293
+ /** FIXED — the producer SUPPLIED a fixed point: the query IS the context, so
294
+ * no transition may consume this state. Declared, never inferred. */
295
+ readonly fixed?: boolean;
296
+ /** USED — what the product speaks for. An EMPTY set is itself a declaration
297
+ * ("this answer voices nothing"); omitted means the layer must re-recognise
298
+ * the product to decide for itself. */
299
+ readonly used?: ReadonlySet<number>;
300
+ }
301
+
302
+ /** A candidate continuation, as reported by the layer that knows the structure.
303
+ * The layer says what it has; the law decides. */
304
+ export interface Continuation {
305
+ /** The structure the transition would make the derivation's product. */
306
+ readonly product: Uint8Array;
307
+ /** CONTAINS — the transition's structure holds the product: a node in its
308
+ * tree, or one contiguous byte run of it. Resolved by the reporter.
309
+ *
310
+ * SUFFICIENT FOR EVERY DECISION THIS CORE MAKES, and a boolean is the minimum:
311
+ * the law reads it ONCE, as the admission gate, and that decision is binary —
312
+ * may this state be consumed by this transition at all? Every other decision
313
+ * is fed by other witnesses, never by this one: the product's identity is
314
+ * `resolve(product)`, progress is the window a move carries or the
315
+ * `reaches` declaration, accounting is the span. Carrying the reporter's
316
+ * structure here would therefore be a dump of mechanism internals bought for
317
+ * nothing. The producers make it true by construction — a continuation is
318
+ * built from the CURRENT product's own structure, never from a different one
319
+ * — and test/133 pins the refusal when a reporter declares false. */
320
+ readonly contains: boolean;
321
+ /** REACHES — the transition MOVES: it reaches structure this derivation has
322
+ * not consumed
323
+ * (a node outside the walker's own set). The second species of progress: a
324
+ * step need not excuse itself with question material when it moves to new
325
+ * structure. Resolved by the reporter, declared by the transition — never
326
+ * inferred from its producer. */
327
+ readonly reaches?: boolean;
328
+ /** What the transition accounts for, when it declares it. A transition
329
+ * taken from a CLOSED state has nothing to progress on, so it is the one
330
+ * case that must say what it accounts for; a transition that carries the
331
+ * remainder declares nothing and the law's own witness is used. */
332
+ readonly explains?: ReadonlyArray<Span>;
333
+ /** The transition's own moves, in ladder units. */
334
+ readonly cost: number;
335
+ }
336
+
337
+ /** CLOSED — nothing of the asker's material is left unaccounted. */
338
+ export function closed(d: DerivationState): boolean {
339
+ return d.remainder.length === 0;
340
+ }
341
+
342
+ /** THE LAW, evaluated once.
343
+ *
344
+ * Returns the spans the transition accounts for — the witness that lets it be
345
+ * taken — or `null` when it is inadmissible. Evaluating it once and advancing
346
+ * the state with {@link advance} is the whole of a transition; asking twice for
347
+ * the same pair would repeat the scan, which this module must not make anyone
348
+ * do.
349
+ *
350
+ * ¬FIXED ∧ CONTAINS ∧ ( CLOSED ∨ CARRIES ∨ MOVES )
351
+ *
352
+ * Cost is not a term: it is the lattice order the market minimises over, and
353
+ * neither is any budget — a cap decides with a number of work, this decides
354
+ * with the remainder. */
355
+ export function admissible(
356
+ d: DerivationState,
357
+ t: Continuation,
358
+ query: Uint8Array,
359
+ W: number,
360
+ ): ReadonlyArray<Witness> | null {
361
+ if (d.fixed) return null;
362
+ if (!t.contains) return null;
363
+ if (closed(d)) return (t.explains ?? []).map((span) => ({ span }));
364
+ // MOVES BEFORE CARRIES, and that order is not arbitrary: a transition that
365
+ // DECLARES it moves (the walker's forward-absorb, and a pivot whose producing
366
+ // mechanism declared the anchors it speaks for) is admitted on that ground
367
+ // alone and records no coverage witness — which is exactly how the code it
368
+ // replaces behaved, where the brake was skipped whenever the producer owned
369
+ // the shape of its answer. Reading coverage first would attribute to such a
370
+ // step material it was never asked to account for.
371
+ if (t.reaches) {
372
+ // A MOVE IS ITS OWN GROUND — it needs no question material to be admitted.
373
+ // What it CARRIES is nonetheless the one thing the question's remainder may
374
+ // be consumed by, and it is read here by the SAME reading the carries
375
+ // admission uses: one definition, so the law cannot be told a span it does
376
+ // not hold. A move that carries nothing consumes nothing.
377
+ const carried = carries(d.remainder, t.product, query, W);
378
+ if (carried !== null) return carried;
379
+ return (t.explains ?? []).map((span) => ({ span }));
380
+ }
381
+ return carries(d.remainder, t.product, query, W);
382
+ }
383
+
384
+ /** ADVANCE — the transition itself: the state that results from consuming `t`
385
+ * with the witness {@link admissible} returned. The product moves, the
386
+ * accounting accumulates what the step engaged, and the cost is added.
387
+ *
388
+ * THE REMAINDER DRAINS ONLY ON A DECLARED MOVE, and that is measured, not
389
+ * assumed: a step admitted by CARRIES holds question material, which any
390
+ * repetition also holds — draining on it let a cycle empty the remainder and
391
+ * close a derivation whose product never changed. A MOVE (structure this
392
+ * derivation has not consumed) cannot be produced by repetition, so it is the
393
+ * one evidence on which the question's leftover may be consumed. The result is
394
+ * no longer a supplied fixed point. */
395
+ function drain(
396
+ remainder: ReadonlyArray<Span>,
397
+ explains: ReadonlyArray<Span>,
398
+ ): Array<[number, number]> {
399
+ return remainder.flatMap(([a, b]): Array<[number, number]> => {
400
+ const cut = explains.find(([x, y]) => x >= a && y <= b);
401
+ if (!cut) return [[a, b]];
402
+ const [x, y] = cut;
403
+ const w = y - x;
404
+ const out: Array<[number, number]> = [];
405
+ if (x - a >= w) out.push([a, x]);
406
+ if (b - y >= w) out.push([y, b]);
407
+ return out;
408
+ });
409
+ }
410
+
411
+ export function advance(
412
+ d: DerivationState,
413
+ t: Continuation,
414
+ explains: ReadonlyArray<Witness>,
415
+ ): DerivationState {
416
+ const spans = explains.map((w) => w.span);
417
+ const carried = t.reaches === true
418
+ ? explains.flatMap((w) => (w.window ? [w.window] : []))
419
+ : [];
420
+ return {
421
+ product: t.product,
422
+ accounted: spans.length === 0 ? d.accounted : [...d.accounted, ...spans],
423
+ remainder: carried.length === 0 ? d.remainder : drain(d.remainder, carried),
424
+ cost: d.cost + t.cost,
425
+ used: d.used,
426
+ };
427
+ }
428
+
429
+ /** What a layer offers the law: the next continuation of a state, or null when
430
+ * it has none. A layer OFFERS; the law disposes.
431
+ *
432
+ * ONE OFFER, AND IT IS THE LAYER'S LAST: {@link closure} stops when the law
433
+ * refuses what was offered, so a refusal is read as "no continuation exists".
434
+ * A layer must not offer candidates one at a time and expect the walk to
435
+ * continue after a refusal — its own fallbacks belong inside this function.
436
+ * The producers do exactly that: they choose between the forward absorb and the
437
+ * pivot before offering, and return null only when neither exists, which is why
438
+ * the one offer the law can still refuse (a pivot without ownership, whose
439
+ * material the answer does not carry) really is the last one. */
440
+ export type Offer = (d: DerivationState) => Promise<Continuation | null>;
441
+
442
+ /** THE CLOSURE — the walk of {@link advance} over the continuations `offer`
443
+ * proposes, run until the layer has nothing further to offer or the law refuses
444
+ * the one it offered.
445
+ *
446
+ * ADMISSION is entirely the law's; TERMINATION is the layer's, and deliberately
447
+ * so. The remainder does not descend (draining it was implemented and refuted
448
+ * — see the module note), so a walk cannot run forever only because the layer
449
+ * offering continuations keeps its own cycle protection over a finite graph.
450
+ * Nothing here counts steps, and nothing here decides admissibility. */
451
+ export async function closure(
452
+ d: DerivationState,
453
+ query: Uint8Array,
454
+ W: number,
455
+ offer: Offer,
456
+ /** Called for each step the law admits, with the state before and after. */
457
+ onTaken?: (before: DerivationState, after: DerivationState) => void,
458
+ /** Called when the law refused the continuation the layer offered. */
459
+ onRefused?: (at: DerivationState) => void,
460
+ ): Promise<DerivationState> {
461
+ for (;;) {
462
+ const t = await offer(d);
463
+ if (t === null) return d;
464
+ const explains = admissible(d, t, query, W);
465
+ if (explains === null) {
466
+ onRefused?.(d);
467
+ return d;
468
+ }
469
+ const next = advance(d, t, explains);
470
+ onTaken?.(d, next);
471
+ d = next;
472
+ }
473
+ }