@hviana/sema 0.8.5 → 0.8.7

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.
@@ -199,15 +199,30 @@ export declare class Meter {
199
199
  * not computable at all. With it, the price of extending the answer is
200
200
  * `reasonSteps · STEP`, the ladder's own value for following an edge. */
201
201
  reasonSteps: number;
202
- /** Bytes of the grounding's UNCOVERED material the extension was justified by
203
- * — the union of the spans each step carried a `W`-window of. The gate
204
- * already computed WHICH span carried it per step and kept only a boolean;
205
- * this is that fact, accumulated. Read with {@link reasonSteps}: one is the
206
- * price, the other the explanation. */
202
+ /** Bytes the admitted steps ACCOUNTED for — the witnesses' spans, summed by the
203
+ * law's own function over the tail of the state's accounting. It is NOT what
204
+ * they carried: a step admitted by `reaches` may declare spans it holds no
205
+ * window of, and then it accounts without carrying and consumes nothing (see
206
+ * {@link closureDrainedBytes}). Read with {@link reasonSteps}: one is the
207
+ * price, the other the accounting. */
208
+ reasonAccountedBytes: number;
209
+ /** Bytes of the QUESTION's own material an admitted step CARRIED — the windows
210
+ * of the remainder its product holds, read by the law's one reading. The
211
+ * accounting may be larger (a `reaches` step declares spans it holds no window
212
+ * of) and the consumption is the window of a step that also reaches, so the
213
+ * three form a lattice: consumed ⊆ carried ⊆ accounted. */
207
214
  reasonCarriedBytes: number;
215
+ /** Times the layer OFFERED a continuation to the law. Read with
216
+ * {@link lawRejects}: the offers the law refused are the Model X contract being
217
+ * exercised, and `offerRuns - lawRejects` is the accepted transitions. */
218
+ offerRuns: number;
219
+ /** Times the law REFUSED the continuation the layer offered. A refusal is
220
+ * terminal by the layer's contract, so this counts the moments the contract
221
+ * mattered — zero means the walk never needed it. */
222
+ lawRejects: number;
208
223
  /** Bytes of the question's REMAINDER a step CONSUMED — the drop the law's own
209
224
  * `advance` makes when a declared move carries the material it accounts for.
210
- * Read with {@link reasonSteps} and {@link reasonCarriedBytes}: carrying is
225
+ * Read with {@link reasonSteps} and {@link reasonAccountedBytes}: carrying is
211
226
  * the engagement, this is the consumption, and before it the second was
212
227
  * invisible. */
213
228
  closureDrainedBytes: number;
package/dist/src/meter.js CHANGED
@@ -209,15 +209,30 @@ export class Meter {
209
209
  * not computable at all. With it, the price of extending the answer is
210
210
  * `reasonSteps · STEP`, the ladder's own value for following an edge. */
211
211
  reasonSteps = 0;
212
- /** Bytes of the grounding's UNCOVERED material the extension was justified by
213
- * — the union of the spans each step carried a `W`-window of. The gate
214
- * already computed WHICH span carried it per step and kept only a boolean;
215
- * this is that fact, accumulated. Read with {@link reasonSteps}: one is the
216
- * price, the other the explanation. */
212
+ /** Bytes the admitted steps ACCOUNTED for — the witnesses' spans, summed by the
213
+ * law's own function over the tail of the state's accounting. It is NOT what
214
+ * they carried: a step admitted by `reaches` may declare spans it holds no
215
+ * window of, and then it accounts without carrying and consumes nothing (see
216
+ * {@link closureDrainedBytes}). Read with {@link reasonSteps}: one is the
217
+ * price, the other the accounting. */
218
+ reasonAccountedBytes = 0;
219
+ /** Bytes of the QUESTION's own material an admitted step CARRIED — the windows
220
+ * of the remainder its product holds, read by the law's one reading. The
221
+ * accounting may be larger (a `reaches` step declares spans it holds no window
222
+ * of) and the consumption is the window of a step that also reaches, so the
223
+ * three form a lattice: consumed ⊆ carried ⊆ accounted. */
217
224
  reasonCarriedBytes = 0;
225
+ /** Times the layer OFFERED a continuation to the law. Read with
226
+ * {@link lawRejects}: the offers the law refused are the Model X contract being
227
+ * exercised, and `offerRuns - lawRejects` is the accepted transitions. */
228
+ offerRuns = 0;
229
+ /** Times the law REFUSED the continuation the layer offered. A refusal is
230
+ * terminal by the layer's contract, so this counts the moments the contract
231
+ * mattered — zero means the walk never needed it. */
232
+ lawRejects = 0;
218
233
  /** Bytes of the question's REMAINDER a step CONSUMED — the drop the law's own
219
234
  * `advance` makes when a declared move carries the material it accounts for.
220
- * Read with {@link reasonSteps} and {@link reasonCarriedBytes}: carrying is
235
+ * Read with {@link reasonSteps} and {@link reasonAccountedBytes}: carrying is
221
236
  * the engagement, this is the consumption, and before it the second was
222
237
  * invisible. */
223
238
  closureDrainedBytes = 0;
@@ -203,6 +203,11 @@ export interface ClimbConsensusData {
203
203
  total: number;
204
204
  };
205
205
  regions?: ConsensusRegionTrace[];
206
+ /** A NAME SHARED WITH THE LAW, AND NOT THE SAME THING: these are the reaches
207
+ * the consensus climb took, for the trace. The law's `Continuation.reaches`
208
+ * (derivation.ts) is a producer's claim that a step moves — different type,
209
+ * different owner, no relation. Kept distinct rather than renamed because the
210
+ * trace's own vocabulary is about the climb, not about derivation. */
206
211
  reaches?: ConsensusReachTrace[];
207
212
  crossRegion?: {
208
213
  eligibleRegions: number;
@@ -176,14 +176,19 @@ export declare function advance(d: DerivationState, t: Continuation, explains: R
176
176
  /** What a layer offers the law: the next continuation of a state, or null when
177
177
  * it has none. A layer OFFERS; the law disposes.
178
178
  *
179
- * ONE OFFER, AND IT IS THE LAYER'S LAST: {@link closure} stops when the law
180
- * refuses what was offered, so a refusal is read as "no continuation exists".
181
- * A layer must not offer candidates one at a time and expect the walk to
182
- * continue after a refusal — its own fallbacks belong inside this function.
183
- * The producers do exactly that: they choose between the forward absorb and the
184
- * pivot before offering, and return null only when neither exists, which is why
185
- * the one offer the law can still refuse (a pivot without ownership, whose
186
- * material the answer does not carry) really is the last one. */
179
+ * ONE OFFER AT A TIME: {@link closure} asks again only while the law admits what
180
+ * was offered, so a refusal ends the walk. A layer must not offer candidates
181
+ * one at a time and expect the walk to continue after a refusal — its own
182
+ * fallbacks belong inside this function, and the producers do exactly that:
183
+ * they choose between the forward absorb and the pivot before offering.
184
+ *
185
+ * WHAT `null` DOES NOT SAY: it is not a claim of exhaustion. A layer returns
186
+ * null for several distinct reasons — it exhausted its enumeration, it refused a
187
+ * candidate it had reached, or a read bound cut one off — and the law cannot tell
188
+ * them apart: it reads every one as "no continuation exists". The layer's reasons
189
+ * are therefore made OBSERVABLE where they happen, on the rationale's own channel
190
+ * (`enumerationExhausted`, `pivotCandidateRefused`, `absorbCandidateRefused`), and
191
+ * it is there — not here — that a reader learns which of them stopped the walk. */
187
192
  export type Offer = (d: DerivationState) => Promise<Continuation | null>;
188
193
  /** THE CLOSURE — the walk of {@link advance} over the continuations `offer`
189
194
  * proposes, run until the layer has nothing further to offer or the law refuses
@@ -196,6 +201,6 @@ export type Offer = (d: DerivationState) => Promise<Continuation | null>;
196
201
  * Nothing here counts steps, and nothing here decides admissibility. */
197
202
  export declare function closure(d: DerivationState, query: Uint8Array, W: number, offer: Offer,
198
203
  /** Called for each step the law admits, with the state before and after. */
199
- onTaken?: (before: DerivationState, after: DerivationState) => void,
204
+ onTaken?: (before: DerivationState, after: DerivationState, witnesses: ReadonlyArray<Witness>) => void,
200
205
  /** Called when the law refused the continuation the layer offered. */
201
206
  onRefused?: (at: DerivationState) => void): Promise<DerivationState>;
@@ -234,6 +234,15 @@ export function closed(d) {
234
234
  export function admissible(d, t, query, W) {
235
235
  if (d.fixed)
236
236
  return null;
237
+ // THE GATE THAT NEVER FIRES, AND WHY IT STAYS. `contains` is the step's claim
238
+ // to BE a continuation of this product at all, and the formula is stated with
239
+ // it (see the doc above). Every producer in the engine declares it true — the
240
+ // three literals in reasoning.ts are constants, measured — so this line cannot
241
+ // reject anything today. It stays because the claim is what makes an `Offer`
242
+ // answerable: a layer that handed over something it does not call a
243
+ // continuation would be refused here rather than trusted. What it must NOT be
244
+ // is a place where a producer says "false" to end a walk: the walk ends by
245
+ // offering nothing (`null`), which is the contract.
237
246
  if (!t.contains)
238
247
  return null;
239
248
  if (closed(d))
@@ -321,7 +330,7 @@ onRefused) {
321
330
  return d;
322
331
  }
323
332
  const next = advance(d, t, explains);
324
- onTaken?.(d, next);
333
+ onTaken?.(d, next, explains);
325
334
  d = next;
326
335
  }
327
336
  }
@@ -451,6 +451,15 @@ export async function think(ctx, query, mechs) {
451
451
  meter.postGroundingRemainderSpans += uncovered.length;
452
452
  meter.postGroundingRemainderBytes += unaccountedBytes(uncovered);
453
453
  }
454
+ // THE WALK THAT DOES NOT RUN, NAMED — the audit's point 1 could not attribute
455
+ // real questions that stopped with no note anywhere. They never reached the
456
+ // offer: `decided.complete` says the grounding supplied a fixed point, so the
457
+ // walk is skipped BY DESIGN and the state is the grounding's own. That state
458
+ // carries `fixed: true` (the trace already reports it), and the law's first
459
+ // clause refuses any continuation against it — so there is no `null` here to
460
+ // read as exhaustion, and the `Offer` contract is not in play at all. The
461
+ // silent stop is therefore a NAMED state, not a gap.
462
+ //
454
463
  // THE WALK CONSUMES AND RETURNS A STATE. It is handed the derivation's own —
455
464
  // the grounding's product, accounting, remainder and cost — and hands back the
456
465
  // state it advanced to, so what follows reads a state rather than bytes plus a
@@ -39,6 +39,13 @@ export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
39
39
  // broad structural gate; pinned by test/31-audit.
40
40
  const qId = pre.queryResolved;
41
41
  if (qId !== null && ctx.store.prevCount(qId) > 0) {
42
+ // THE ECHO GUARD, NAMED — the audit's point 1 could not attribute two real
43
+ // walks that stopped with no note at all, and this is where one of them went:
44
+ // the query IS a learnt continuation, so the reasoner returns the grounded
45
+ // read-out and never offers anything. Without this note the walk looks like
46
+ // an unexplained stop; with it, "no note" can only mean the offer was never
47
+ // asked. No decision changes: the guard returns exactly where it did.
48
+ ctx.trace?.step("echoGuard", [rItem(query, "query")], [], "the query is itself a learnt continuation — answering the grounded read-out without walking");
42
49
  return d0;
43
50
  }
44
51
  // Consume a node and its neighbours for pivot-cycle prevention — CAPPED at
@@ -183,6 +190,8 @@ export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
183
190
  // admitted.
184
191
  let pending = null;
185
192
  const offer = async (d) => {
193
+ if (ctx.meter)
194
+ ctx.meter.offerRuns += 1;
186
195
  const cur = d.product;
187
196
  // The first step's `cur` IS the grounding's product, so the guard above
188
197
  // already resolved it and read its reverse edges — reuse both.
@@ -196,8 +205,30 @@ export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
196
205
  // fixpoint falls through to the pivot step instead. Offered with `moves`:
197
206
  // completing the answer's OWN learnt form is an identity step, not a claim
198
207
  // about the asker's material.
199
- if (curId !== null &&
200
- ctx.store.nextFirst(curId, bound).some((n) => !consumed.has(n))) {
208
+ // THE READ, KEPT — its LENGTH is free and it is the only instrument the
209
+ // bound has: a read that came back full may have been cut off, and a read
210
+ // that came back short did not. No second read is paid for this.
211
+ const outs = curId !== null ? ctx.store.nextFirst(curId, bound) : [];
212
+ const saturated = outs.length >= bound;
213
+ // THE RE-REACH, NAMED — the audit's structural-identity case (its test G), and
214
+ // the silent path it hid in. MEASURED, and this is the point's closure: across
215
+ // thirteen real questions on the trained store — the roteiro's ten plus three —
216
+ // this note fires ZERO times. The case, if it exists in this engine, is not on
217
+ // that material. It is not unimplementable, it is unobserved, and the audit's
218
+ // own rule is that an unobserved claim stays declared as unobserved: a
219
+ // distinction between legitimate convergence and a cycle cannot be implemented
220
+ // responsibly before it is seen, and the brief's section 13 puts measurement
221
+ // before code. What exists here is the instrument that will show it the moment
222
+ // it happens. When every successor of the current product has already
223
+ // been spoken for, the absorb above is skipped WITHOUT a word and the walk
224
+ // falls to the pivot: the same structure reached again is indistinguishable
225
+ // from having none. This note does not change the decision — it makes the
226
+ // state observable first, because the distinction between a legitimate
227
+ // convergence and a cycle cannot be implemented responsibly until it is seen.
228
+ if (curId !== null && outs.length > 0 && !outs.some((n) => !consumed.has(n))) {
229
+ ctx.trace?.step("reachAlreadySpokenFor", [rItem(cur, "answer")], [], `every continuation of this product (${outs.length}) has already been spoken for — the same structure, reached again`);
230
+ }
231
+ if (curId !== null && outs.some((n) => !consumed.has(n))) {
201
232
  const fwd = await follow(ctx, curId, qv);
202
233
  const fwdId = fwd !== null ? resolve(ctx, fwd) : null;
203
234
  if (fwd !== null && !bytesEqual(fwd, cur) &&
@@ -207,16 +238,55 @@ export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
207
238
  pending = { kind: "absorb", cur, curId, fwd };
208
239
  return { product: fwd, contains: true, reaches: true, cost: STEP };
209
240
  }
241
+ // THE ABSORB'S OWN REFUSALS, TOLD APART — a learnt continuation existed
242
+ // and was declined, so the walk falls through to the pivot rather than
243
+ // ending. Which of the four reasons it was is exactly what the rationale
244
+ // could not say before: `null` at the end of the walk is one state, and
245
+ // this is another (AGENTS §6 — the note belongs where the mechanism emits
246
+ // it, not in a counter beside it).
247
+ const whyAbsorb = fwd === null
248
+ ? "the unconsumed edge led nowhere"
249
+ : bytesEqual(fwd, cur)
250
+ ? "the continuation reached was the product already in hand"
251
+ : fwdId !== null && consumed.has(fwdId)
252
+ ? "the continuation reached had already been spoken for"
253
+ : "this continuation only restates the question";
254
+ ctx.trace?.step("absorbCandidateRefused", [rItem(cur, "answer")], [], `${whyAbsorb} — falling through to the pivot`);
255
+ }
256
+ if (saturated) {
257
+ // THE BUDGET, NAMED — the audit's point 2. `nextFirst` is read with a cap
258
+ // (`hubBound`), so a saturated read may have hidden a continuation, and the
259
+ // walk would end as if none existed. The note says MAY, because the length
260
+ // cannot tell a full read from a truncated one; it can only tell that this
261
+ // is where a cut would have happened.
262
+ ctx.trace?.step("readBoundSaturated", [rItem(cur, "answer")], [], "the read came back full — a continuation may have been cut off by the bound");
210
263
  }
211
264
  // Pivot: the longest unconsumed learnt context the answer contains.
212
265
  consumeAll(curId);
213
266
  const pivot = await pivotInto(ctx, cur, consumed, voiced);
214
- if (pivot === null)
267
+ if (pivot === null) {
268
+ // THE ENUMERATION, DECLARED — the one duty the law cannot perform for the
269
+ // layer: only the layer knows that it has nothing left to offer. Before
270
+ // this note the walk ended in a bare `null`, indistinguishable from the
271
+ // causes below it; a reader of the rationale could not tell "exhausted"
272
+ // from "the candidate was refused" (AGENTS §6: a gap in instrumentation
273
+ // is a defect IN the instrumentation).
274
+ ctx.trace?.step("enumerationExhausted", [rItem(cur, "answer")], [], "the layer has no further candidate — its enumeration is exhausted");
215
275
  return null;
276
+ }
216
277
  const fc = await follow(ctx, pivot, qv);
217
278
  consumeAll(pivot);
218
279
  if (fc === null || bytesEqual(fc, cur) ||
219
280
  restates(query, fc, 0, { proper: true })) {
281
+ // THE THREE REFUSALS, TOLD APART — a candidate was reached and declined,
282
+ // which is NOT the same state as having none: the law reads both as "no
283
+ // continuation", but the rationale should not.
284
+ const why = fc === null
285
+ ? "the pivot's context carried no continuation"
286
+ : bytesEqual(fc, cur)
287
+ ? "the continuation reached was the product already in hand"
288
+ : "this continuation only restates the question";
289
+ ctx.trace?.step("pivotCandidateRefused", [rItem(cur, "answer")], [], `${why} — refused`);
220
290
  return null;
221
291
  }
222
292
  pending = { kind: "pivot", cur, pivot, fc };
@@ -232,8 +302,9 @@ export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
232
302
  cost: STEP,
233
303
  };
234
304
  };
235
- const closed_ = await closure(d0, query, W, offer, (before, after) => {
305
+ const closed_ = await closure(d0, query, W, offer, (before, after, witnesses) => {
236
306
  if (ctx.meter) {
307
+ ctx.meter.reasonCarriedBytes += witnesses.reduce((n, w) => n + (w.window ? w.window[1] - w.window[0] : 0), 0);
237
308
  ctx.meter.closureDrainedBytes += unaccountedBytes(before.remainder) -
238
309
  unaccountedBytes(after.remainder);
239
310
  }
@@ -251,6 +322,10 @@ export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
251
322
  ctx.trace?.step("pivotStep", [rItem(p.cur, "answer"), rNode(ctx, p.pivot, "pivot")], [rItem(p.fc, "answer", resolve(ctx, p.fc) ?? undefined)], "pivot on the shared span this answer contains, then step forward across that fact");
252
323
  }
253
324
  }, (at) => {
325
+ // THE LAW REFUSED — counted where it happened, before the bookkeeping
326
+ // below decides whether there is a step to report.
327
+ if (ctx.meter)
328
+ ctx.meter.lawRejects += 1;
254
329
  const p = pending;
255
330
  pending = null;
256
331
  if (p === null || p.kind !== "pivot")
@@ -266,6 +341,20 @@ export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
266
341
  ctx.trace?.step("pivotRefused", [rItem(p.cur, "answer"), rItem(query, "query")], at.remainder.map(([a, b]) => rItem(query.subarray(a, b), "uncovered")), `the step carries none of the question material the grounding left ` +
267
342
  `unaccounted (${left} byte(s) in ${at.remainder.length} span(s)) — refused`);
268
343
  });
344
+ // THE WALK'S OWN ENDING, NAMED — the audit's point 1. Every stop INSIDE the
345
+ // offer now has a note, but a walk can also end without the offer ever being
346
+ // asked, or after a step the law took and then found nothing to follow: those
347
+ // ended as "no note at all", indistinguishable from a mechanism that never ran.
348
+ // Two endings cover every remaining case, and together they make a silent stop
349
+ // impossible — which is what the `Offer` contract needs, because `null` is read
350
+ // as exhaustion and exhaustion must be a statement, not an absence.
351
+ t ??= ctx.trace?.enter("reason", [rItem(startedFrom, "grounded")]);
352
+ if (closed_.remainder.length === 0) {
353
+ ctx.trace?.step("walkClosed", [rItem(closed_.product, "answer")], [], "the derivation is closed — nothing is left unaccounted for");
354
+ }
355
+ else {
356
+ ctx.trace?.step("walkEndedWithoutOffer", [rItem(closed_.product, "answer")], closed_.remainder.map(([a, b]) => rItem(query.subarray(a, b), "uncovered")), `the walk ended with ${unaccountedBytes(closed_.remainder)} byte(s) unaccounted and no further offer — no structural continuation`);
357
+ }
269
358
  // INSTRUMENTATION ONLY — the extension's two facts, untraced (meter.ts
270
359
  // contract 1: a counter never reaches a decision). They are what a caller
271
360
  // needs to PRICE the extension instead of taking it unconditionally, and both
@@ -279,7 +368,7 @@ export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
279
368
  // over the tail of the state's list — and computed ONLY when a meter is
280
369
  // attached: this used to slice and MAP a fresh array on every response, for a
281
370
  // counter that usually does not exist. One allocation, under the meter.
282
- ctx.meter.reasonCarriedBytes += unaccountedBytes(closed_.accounted.slice(d0.accounted.length));
371
+ ctx.meter.reasonAccountedBytes += unaccountedBytes(closed_.accounted.slice(d0.accounted.length));
283
372
  }
284
373
  t?.done([rItem(closed_.product, "answer", resolve(ctx, closed_.product) ?? undefined)],
285
374
  // A FIXPOINT: no further step was offered, or the law refused the one that
@@ -489,6 +578,7 @@ primarySpans = []) {
489
578
  return { ...state, product: out };
490
579
  const fused = advance(state, fusedT, fusedWitness);
491
580
  if (ctx.meter) {
581
+ ctx.meter.reasonCarriedBytes += fusedWitness.reduce((n, w) => n + (w.window ? w.window[1] - w.window[0] : 0), 0);
492
582
  ctx.meter.closureDrainedBytes += unaccountedBytes(state.remainder) -
493
583
  unaccountedBytes(fused.remainder);
494
584
  }
@@ -95,7 +95,13 @@ export declare function hubCap<T>(ctx: MindContext, ids: readonly T[]): readonly
95
95
  /** Whether `descendant` lies within `ancestor`'s subtree — a structural DAG
96
96
  * relation read off the hash-consed `kids` lists, by a bounded explicit-stack
97
97
  * descent. Used by articulation to keep a voice from revoicing a fragment
98
- * OF that voice. */
98
+ * OF that voice.
99
+ *
100
+ * A HOMONYM, NOT A RELATIVE: the law's `Continuation.contains` (derivation.ts) is
101
+ * a producer's claim that its step IS a continuation, a boolean on one step. This
102
+ * is a structural relation between two nodes in the DAG. The shared word is
103
+ * deliberate vocabulary — "containment" is what both name — and the two must not be
104
+ * read as one. */
99
105
  export declare function contains(ctx: MindContext, ancestor: number, descendant: number): boolean;
100
106
  /** Whether a continuation edge joins the two forms, in either direction —
101
107
  * the EXACT half's veto on calling them synonyms.
@@ -495,7 +495,13 @@ export function hubCap(ctx, ids) {
495
495
  /** Whether `descendant` lies within `ancestor`'s subtree — a structural DAG
496
496
  * relation read off the hash-consed `kids` lists, by a bounded explicit-stack
497
497
  * descent. Used by articulation to keep a voice from revoicing a fragment
498
- * OF that voice. */
498
+ * OF that voice.
499
+ *
500
+ * A HOMONYM, NOT A RELATIVE: the law's `Continuation.contains` (derivation.ts) is
501
+ * a producer's claim that its step IS a continuation, a boolean on one step. This
502
+ * is a structural relation between two nodes in the DAG. The shared word is
503
+ * deliberate vocabulary — "containment" is what both name — and the two must not be
504
+ * read as one. */
499
505
  export function contains(ctx, ancestor, descendant) {
500
506
  if (ancestor === descendant)
501
507
  return true;
package/docs/INDEX.md CHANGED
@@ -36,7 +36,7 @@ proof in `test/` (pins that fail when the law is broken).
36
36
  | 11 | `docs/architecture/memoization.md` | `Precomputed` is per-response lazy cache (promise-cached async); `beginResponse`/`endResponse` lifecycle | `test/42` |
37
37
  | 12 | `docs/architecture/saturation.md` | Every walk names a deciding saturation beside its cap; cap is safety net, not decision | `test/27`, `test/16` |
38
38
  | 13 | `docs/architecture/meter.md` | `meter.ts` is write-only work accounting; counts are exact, phases nest | `test/55` |
39
- | 14 | `docs/architecture/closure.md` | A derivation is closed when its structure accounts for the question's remainder; every transition asks that law | `test/133`–`140` |
39
+ | 14 | `docs/architecture/closure.md` | A derivation is closed when its structure accounts for the question's remainder; every transition asks that law | `test/133`–`147` |
40
40
 
41
41
  ## Mechanisms (8)
42
42
 
@@ -15,4 +15,4 @@
15
15
  | 11 | Honest degradation | `src/mind/pipeline.ts:weight=moves+PASS*unaccounted` `src/store.ts:BoundedMap:miss→re-derive` | `test/28` `test/84` | `store.md`+`caches.md` |
16
16
  | 12 | Meter contracts | `src/meter.ts:Meter,PhaseCost,time` `src/mind/pipeline-mechanism.ts:Precomputed.shared` | `test/55` | `meter.md` |
17
17
  | 13 | Saturation | `traverse.ts:edgeAncestors,types.ts:SaturationReason` `src/mind/junction.ts:junctionContainersFrom` `src/mind/resonance.ts:pivotInto` | `test/27` `test/16` | `saturation.md` |
18
- | 14 | Closure | `src/mind/derivation.ts:closed,admissible,advance` | `test/133`–`140` | `closure.md` |
18
+ | 14 | Closure | `src/mind/derivation.ts:closed,admissible,advance` | `test/133`–`147` | `closure.md` |
@@ -23,6 +23,14 @@ field, no producer field, and no count of any kind. The witnesses `contains` and
23
23
  `moves` are the LAYER's: it holds the structure and hands them in, so the law
24
24
  never probes the store.
25
25
 
26
+ ## The boundary
27
+
28
+ `product` (what it stands on, its identity being `resolve(product)`),
29
+ `accounted` (the price its steps summed) and `remainder` (the question's debt,
30
+ at one quantum) are the derivation's own. What it REACHED and what it SPENT are
31
+ the layer's (`reaches`, `consumed`), never a field here: the law decides
32
+ admission and consumption, the layer decides frontier and termination.
33
+
26
34
  ## The transition
27
35
 
28
36
  `admissible(state, continuation, query, W)` returns the witnesses the step pays
@@ -33,6 +41,10 @@ consumes what its window proves. Whole-span draining was refuted by `test/110`,
33
41
  the window alone by `test/138`. The state is BORN owing what its product does
34
42
  not carry.
35
43
 
44
+ The walk ends when the layer stops offering or the law refuses; owning no
45
+ search, the law cannot prevent a cycle — that is the layer's, over the structure
46
+ it holds.
47
+
36
48
  ## One cost home
37
49
 
38
50
  `moves + PASS · unaccountedBytes` is computed in ONE place (`pipeline.ts`,
@@ -60,6 +72,9 @@ vocabulary, not the tracer's.
60
72
  ## Pins
61
73
 
62
74
  - `test/133`–`137` — the law's home, readings, refusal, limits.
63
- - `test/138` — a cycle cannot close it; a carried move can.
75
+ - `test/138` — a cycle cannot close it, a carried move can.
64
76
  - `test/139` — carrying is ENGAGEMENT, not explanation.
65
- - `test/140` — irrelevant supply changes no answer.
77
+ - `test/140` — irrelevant supply changes nothing.
78
+ - `test/142`, `test/143` — the offer's contract, and the cycles.
79
+ - `test/144`, `test/146`, `test/147` — identity is content, the `contains` gate,
80
+ the witness ladder.
@@ -13,7 +13,7 @@ Guards honest silence, determinism, and every pinned contract. Silence:
13
13
  unrelated queries ground to nothing (`test/28`, `50`, `56`, `67`, `76`, `84`).
14
14
  Determinism: same seed + deposit order + query gives byte-identical answer
15
15
  (`test/20`). Every invariant is pinned, the closure law included
16
- (`test/133`–`140`). §14–25 (pipeline), §64 (derived thresholds), AGENTS.md §2
16
+ (`test/133`–`147`). §14–25 (pipeline), §64 (derived thresholds), AGENTS.md §2
17
17
  invariants 1–5.
18
18
 
19
19
  ## 2 — Work accounting (profiler)
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://jsr.io/schema/config-file.v1.json",
3
3
  "name": "@hviana/sema",
4
- "version": "0.8.5",
4
+ "version": "0.8.7",
5
5
  "exports": "./src/index.ts"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hviana/sema",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "Sema: a non-parametric, instance-based reasoning system.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/meter.ts CHANGED
@@ -267,15 +267,30 @@ export class Meter {
267
267
  * not computable at all. With it, the price of extending the answer is
268
268
  * `reasonSteps · STEP`, the ladder's own value for following an edge. */
269
269
  reasonSteps = 0;
270
- /** Bytes of the grounding's UNCOVERED material the extension was justified by
271
- * — the union of the spans each step carried a `W`-window of. The gate
272
- * already computed WHICH span carried it per step and kept only a boolean;
273
- * this is that fact, accumulated. Read with {@link reasonSteps}: one is the
274
- * price, the other the explanation. */
270
+ /** Bytes the admitted steps ACCOUNTED for — the witnesses' spans, summed by the
271
+ * law's own function over the tail of the state's accounting. It is NOT what
272
+ * they carried: a step admitted by `reaches` may declare spans it holds no
273
+ * window of, and then it accounts without carrying and consumes nothing (see
274
+ * {@link closureDrainedBytes}). Read with {@link reasonSteps}: one is the
275
+ * price, the other the accounting. */
276
+ reasonAccountedBytes = 0;
277
+ /** Bytes of the QUESTION's own material an admitted step CARRIED — the windows
278
+ * of the remainder its product holds, read by the law's one reading. The
279
+ * accounting may be larger (a `reaches` step declares spans it holds no window
280
+ * of) and the consumption is the window of a step that also reaches, so the
281
+ * three form a lattice: consumed ⊆ carried ⊆ accounted. */
275
282
  reasonCarriedBytes = 0;
283
+ /** Times the layer OFFERED a continuation to the law. Read with
284
+ * {@link lawRejects}: the offers the law refused are the Model X contract being
285
+ * exercised, and `offerRuns - lawRejects` is the accepted transitions. */
286
+ offerRuns = 0;
287
+ /** Times the law REFUSED the continuation the layer offered. A refusal is
288
+ * terminal by the layer's contract, so this counts the moments the contract
289
+ * mattered — zero means the walk never needed it. */
290
+ lawRejects = 0;
276
291
  /** Bytes of the question's REMAINDER a step CONSUMED — the drop the law's own
277
292
  * `advance` makes when a declared move carries the material it accounts for.
278
- * Read with {@link reasonSteps} and {@link reasonCarriedBytes}: carrying is
293
+ * Read with {@link reasonSteps} and {@link reasonAccountedBytes}: carrying is
279
294
  * the engagement, this is the consumption, and before it the second was
280
295
  * invisible. */
281
296
  closureDrainedBytes = 0;
@@ -347,6 +347,11 @@ export interface ClimbConsensusData {
347
347
  };
348
348
 
349
349
  regions?: ConsensusRegionTrace[];
350
+ /** A NAME SHARED WITH THE LAW, AND NOT THE SAME THING: these are the reaches
351
+ * the consensus climb took, for the trace. The law's `Continuation.reaches`
352
+ * (derivation.ts) is a producer's claim that a step moves — different type,
353
+ * different owner, no relation. Kept distinct rather than renamed because the
354
+ * trace's own vocabulary is about the climb, not about derivation. */
350
355
  reaches?: ConsensusReachTrace[];
351
356
 
352
357
  crossRegion?: {
@@ -359,6 +359,15 @@ export function admissible(
359
359
  W: number,
360
360
  ): ReadonlyArray<Witness> | null {
361
361
  if (d.fixed) return null;
362
+ // THE GATE THAT NEVER FIRES, AND WHY IT STAYS. `contains` is the step's claim
363
+ // to BE a continuation of this product at all, and the formula is stated with
364
+ // it (see the doc above). Every producer in the engine declares it true — the
365
+ // three literals in reasoning.ts are constants, measured — so this line cannot
366
+ // reject anything today. It stays because the claim is what makes an `Offer`
367
+ // answerable: a layer that handed over something it does not call a
368
+ // continuation would be refused here rather than trusted. What it must NOT be
369
+ // is a place where a producer says "false" to end a walk: the walk ends by
370
+ // offering nothing (`null`), which is the contract.
362
371
  if (!t.contains) return null;
363
372
  if (closed(d)) return (t.explains ?? []).map((span) => ({ span }));
364
373
  // MOVES BEFORE CARRIES, and that order is not arbitrary: a transition that
@@ -429,14 +438,19 @@ export function advance(
429
438
  /** What a layer offers the law: the next continuation of a state, or null when
430
439
  * it has none. A layer OFFERS; the law disposes.
431
440
  *
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. */
441
+ * ONE OFFER AT A TIME: {@link closure} asks again only while the law admits what
442
+ * was offered, so a refusal ends the walk. A layer must not offer candidates
443
+ * one at a time and expect the walk to continue after a refusal — its own
444
+ * fallbacks belong inside this function, and the producers do exactly that:
445
+ * they choose between the forward absorb and the pivot before offering.
446
+ *
447
+ * WHAT `null` DOES NOT SAY: it is not a claim of exhaustion. A layer returns
448
+ * null for several distinct reasons — it exhausted its enumeration, it refused a
449
+ * candidate it had reached, or a read bound cut one off — and the law cannot tell
450
+ * them apart: it reads every one as "no continuation exists". The layer's reasons
451
+ * are therefore made OBSERVABLE where they happen, on the rationale's own channel
452
+ * (`enumerationExhausted`, `pivotCandidateRefused`, `absorbCandidateRefused`), and
453
+ * it is there — not here — that a reader learns which of them stopped the walk. */
440
454
  export type Offer = (d: DerivationState) => Promise<Continuation | null>;
441
455
 
442
456
  /** THE CLOSURE — the walk of {@link advance} over the continuations `offer`
@@ -454,7 +468,11 @@ export async function closure(
454
468
  W: number,
455
469
  offer: Offer,
456
470
  /** Called for each step the law admits, with the state before and after. */
457
- onTaken?: (before: DerivationState, after: DerivationState) => void,
471
+ onTaken?: (
472
+ before: DerivationState,
473
+ after: DerivationState,
474
+ witnesses: ReadonlyArray<Witness>,
475
+ ) => void,
458
476
  /** Called when the law refused the continuation the layer offered. */
459
477
  onRefused?: (at: DerivationState) => void,
460
478
  ): Promise<DerivationState> {
@@ -467,7 +485,7 @@ export async function closure(
467
485
  return d;
468
486
  }
469
487
  const next = advance(d, t, explains);
470
- onTaken?.(d, next);
488
+ onTaken?.(d, next, explains);
471
489
  d = next;
472
490
  }
473
491
  }
@@ -664,6 +664,15 @@ export async function think(
664
664
  meter.postGroundingRemainderSpans += uncovered.length;
665
665
  meter.postGroundingRemainderBytes += unaccountedBytes(uncovered);
666
666
  }
667
+ // THE WALK THAT DOES NOT RUN, NAMED — the audit's point 1 could not attribute
668
+ // real questions that stopped with no note anywhere. They never reached the
669
+ // offer: `decided.complete` says the grounding supplied a fixed point, so the
670
+ // walk is skipped BY DESIGN and the state is the grounding's own. That state
671
+ // carries `fixed: true` (the trace already reports it), and the law's first
672
+ // clause refuses any continuation against it — so there is no `null` here to
673
+ // read as exhaustion, and the `Offer` contract is not in play at all. The
674
+ // silent stop is therefore a NAMED state, not a gap.
675
+ //
667
676
  // THE WALK CONSUMES AND RETURNS A STATE. It is handed the derivation's own —
668
677
  // the grounding's product, accounting, remainder and cost — and hands back the
669
678
  // state it advanced to, so what follows reads a state rather than bytes plus a