@hviana/sema 0.8.9 → 0.9.1

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 (111) hide show
  1. package/AGENTS.md +7 -7
  2. package/dist/src/alu/src/index.d.ts +1 -1
  3. package/dist/src/alu/src/index.js +1 -1
  4. package/dist/src/alu/src/parser.js +2 -6
  5. package/dist/src/alu/src/resonance.d.ts +13 -0
  6. package/dist/src/alu/src/resonance.js +41 -0
  7. package/dist/src/alu/test/alu.test.js +39 -0
  8. package/dist/src/bytes.d.ts +6 -2
  9. package/dist/src/bytes.js +10 -4
  10. package/dist/src/canon.js +44 -0
  11. package/dist/src/geometry.d.ts +19 -1
  12. package/dist/src/geometry.js +125 -141
  13. package/dist/src/meter.d.ts +27 -0
  14. package/dist/src/meter.js +28 -1
  15. package/dist/src/mind/articulation.js +14 -1
  16. package/dist/src/mind/attention.d.ts +12 -0
  17. package/dist/src/mind/attention.js +44 -16
  18. package/dist/src/mind/bridge.js +3 -3
  19. package/dist/src/mind/derivation.d.ts +40 -0
  20. package/dist/src/mind/derivation.js +34 -0
  21. package/dist/src/mind/graph-search.d.ts +89 -15
  22. package/dist/src/mind/graph-search.js +345 -174
  23. package/dist/src/mind/learning.js +1 -1
  24. package/dist/src/mind/mechanisms/cover.d.ts +19 -3
  25. package/dist/src/mind/mechanisms/cover.js +101 -58
  26. package/dist/src/mind/mechanisms/recall.js +0 -1
  27. package/dist/src/mind/mind.js +2 -2
  28. package/dist/src/mind/pipeline.d.ts +5 -1
  29. package/dist/src/mind/pipeline.js +175 -87
  30. package/dist/src/mind/primitives.d.ts +25 -5
  31. package/dist/src/mind/primitives.js +107 -44
  32. package/dist/src/mind/reasoning.d.ts +18 -4
  33. package/dist/src/mind/reasoning.js +445 -321
  34. package/dist/src/mind/recognition.js +55 -73
  35. package/dist/src/mind/resonance.js +1 -11
  36. package/dist/src/mind/traverse.d.ts +3 -3
  37. package/dist/src/mind/traverse.js +3 -3
  38. package/dist/src/mind/types.d.ts +7 -1
  39. package/dist/src/store-sqlite.d.ts +25 -0
  40. package/dist/src/store-sqlite.js +89 -1
  41. package/dist/src/store.d.ts +48 -4
  42. package/dist/src/store.js +86 -6
  43. package/docs/INDEX.md +18 -18
  44. package/docs/INVARIANTS.md +16 -16
  45. package/docs/architecture/bounded-reads.md +1 -1
  46. package/docs/architecture/caches.md +5 -4
  47. package/docs/architecture/closure.md +45 -5
  48. package/docs/architecture/cost-model.md +16 -0
  49. package/docs/architecture/factored-machinery.md +14 -13
  50. package/docs/architecture/fold-contract.md +51 -1
  51. package/docs/architecture/mechanism-market.md +21 -0
  52. package/docs/architecture/memoization.md +3 -3
  53. package/docs/architecture/meter.md +2 -1
  54. package/docs/architecture/saturation.md +12 -0
  55. package/docs/architecture/store.md +25 -2
  56. package/docs/failures/tempting-but-wrong.md +13 -2
  57. package/docs/harness/gates.md +12 -10
  58. package/docs/mechanisms/cover.md +23 -6
  59. package/jsr.json +1 -1
  60. package/package.json +1 -1
  61. package/src/alu/README.md +10 -2
  62. package/src/alu/src/index.ts +1 -0
  63. package/src/alu/src/parser.ts +6 -6
  64. package/src/alu/src/resonance.ts +42 -0
  65. package/src/alu/test/alu.test.ts +40 -0
  66. package/src/bytes.ts +13 -3
  67. package/src/canon.ts +40 -0
  68. package/src/geometry.ts +183 -154
  69. package/src/meter.ts +28 -1
  70. package/src/mind/articulation.ts +14 -2
  71. package/src/mind/attention.ts +47 -25
  72. package/src/mind/bridge.ts +3 -3
  73. package/src/mind/derivation.ts +77 -0
  74. package/src/mind/graph-search.ts +449 -221
  75. package/src/mind/learning.ts +1 -7
  76. package/src/mind/match.ts +1 -2
  77. package/src/mind/mechanisms/cast.ts +1 -2
  78. package/src/mind/mechanisms/cover.ts +149 -84
  79. package/src/mind/mechanisms/extraction.ts +1 -2
  80. package/src/mind/mechanisms/prefix-completion.ts +1 -1
  81. package/src/mind/mechanisms/recall.ts +1 -3
  82. package/src/mind/mechanisms/reference.ts +1 -1
  83. package/src/mind/mind.ts +5 -30
  84. package/src/mind/pipeline.ts +206 -102
  85. package/src/mind/primitives.ts +119 -43
  86. package/src/mind/reasoning.ts +558 -413
  87. package/src/mind/recognition.ts +49 -65
  88. package/src/mind/resonance.ts +2 -16
  89. package/src/mind/trace.ts +1 -1
  90. package/src/mind/traverse.ts +3 -3
  91. package/src/mind/types.ts +9 -11
  92. package/src/store-sqlite.ts +92 -1
  93. package/src/store.ts +113 -7
  94. package/test/105-derive-through-reports-its-refusal.test.mjs +8 -5
  95. package/test/106-the-join-fires.test.mjs +21 -0
  96. package/test/111-the-cover-assembly-is-counted.test.mjs +8 -5
  97. package/test/128-the-leads-somewhere-pair-agrees.test.mjs +18 -12
  98. package/test/136-the-two-named-limits.test.mjs +3 -2
  99. package/test/137-the-law-lives-once-and-below.test.mjs +21 -0
  100. package/test/148-exact-shortcuts-agree.test.mjs +188 -0
  101. package/test/149-the-closure-engine.test.mjs +138 -0
  102. package/test/150-the-join-is-output-sensitive.test.mjs +66 -0
  103. package/test/151-the-cover-pays-for-what-it-reaches.test.mjs +142 -0
  104. package/test/152-the-read-side-names-as-the-write-side.test.mjs +146 -0
  105. package/test/153-a-cheaper-bound-is-looked-at-first.test.mjs +155 -0
  106. package/test/24-generalization.test.mjs +32 -0
  107. package/test/36-bloom.test.mjs +53 -0
  108. package/test/37-cluster-dispersion-fusion.test.mjs +75 -0
  109. package/test/48-recognise-turn-connective.test.mjs +3 -2
  110. package/test/55-cost-meter.test.mjs +4 -4
  111. package/test/90-connector-read-cap.test.mjs +7 -7
package/AGENTS.md CHANGED
@@ -57,13 +57,13 @@ Five invariants. Violate one and the system degrades silently — tests pin them
57
57
 
58
58
  Cross-cutting contracts (single-definition, imported everywhere):
59
59
  `contentLevels` in `src/geometry.ts` is the one boundary rule;
60
- `src/mind/derivation.ts` is the closure law; `src/mind/canonical.ts` is the
61
- canonical segmentation contract; `src/mind/junction.ts` is the shared
62
- content-addressed ascent; `Precomputed` in `src/mind/pipeline-mechanism.ts` is
63
- the per-response memo; `src/meter.ts` is the write-only work accounting surface.
64
- See `docs/INDEX.md` and `factored-machinery.md` for the contract table and
65
- ownership. Tie-breaks are corpus-determined, not interchangeable
66
- (`determinism.md`).
60
+ `src/mind/derivation.ts` is the closure law and its engine (`closeOver`);
61
+ `src/mind/canonical.ts` is the canonical segmentation contract;
62
+ `src/mind/junction.ts` is the shared content-addressed ascent; `Precomputed` in
63
+ `src/mind/pipeline-mechanism.ts` is the per-response memo; `src/meter.ts` is the
64
+ write-only work accounting surface. See `docs/INDEX.md` and
65
+ `factored-machinery.md` for the contract table and ownership. Tie-breaks are
66
+ corpus-determined, not interchangeable (`determinism.md`).
67
67
 
68
68
  ## 3. Where things live
69
69
 
@@ -1,6 +1,6 @@
1
1
  export { asBit, asInt, asReal, bit, coerce, decimalCodec, type Domain, formatReal, int, isNd, isNumeric, joinDomain, nd, parseValue, real, symbol, symbolSpans, tagOf, type Value, type ValueCodec, } from "./value.js";
2
2
  export { type Arity, type EvalExpr, type InfixSyntax, NO_RESONANCE, type OpContext, type Operation, OperationRegistry, type OpFn, type OpRuntime, type OpTraits, type ResonanceSync, } from "./operation.js";
3
- export { type AluResonance, type ConceptAnchor, NO_ALU_RESONANCE, prefetchOpposites, prefetchRecognisedOps, prefetchResonance, } from "./resonance.js";
3
+ export { type AluResonance, type ConceptAnchor, NO_ALU_RESONANCE, prefetchOpposites, prefetchRecognisedOps, prefetchResonance, withOppositesOnDemand, } from "./resonance.js";
4
4
  export { type ApplyScalar, ExprGrammar, freeVariables, type IsUnaryFn, type Token, tokenize, } from "./expr.js";
5
5
  export { type AluHost, type ComputedSpan, QueryParser, type Span, STRUCTURAL_HOST, } from "./parser.js";
6
6
  export { registerNd } from "./kernel-nd.js";
@@ -8,7 +8,7 @@
8
8
  // See ./../README.md for the kernel and its derivation DAG.
9
9
  export { asBit, asInt, asReal, bit, coerce, decimalCodec, formatReal, int, isNd, isNumeric, joinDomain, nd, parseValue, real, symbol, symbolSpans, tagOf, } from "./value.js";
10
10
  export { NO_RESONANCE, OperationRegistry, } from "./operation.js";
11
- export { NO_ALU_RESONANCE, prefetchOpposites, prefetchRecognisedOps, prefetchResonance, } from "./resonance.js";
11
+ export { NO_ALU_RESONANCE, prefetchOpposites, prefetchRecognisedOps, prefetchResonance, withOppositesOnDemand, } from "./resonance.js";
12
12
  export { ExprGrammar, freeVariables, tokenize, } from "./expr.js";
13
13
  export { QueryParser, STRUCTURAL_HOST, } from "./parser.js";
14
14
  export { registerNd } from "./kernel-nd.js";
@@ -40,8 +40,7 @@
40
40
  // that names an operation literally or by resonance, applied to a
41
41
  // following operand ("opposite large" → "small"), or, for the numerical
42
42
  // layer, to a following EXPRESSION ("derivative of x^2 at 3").
43
- import { prefetchOpposites, prefetchResonance, } from "./resonance.js";
44
- import { NO_RESONANCE } from "./operation.js";
43
+ import { prefetchResonance, withOppositesOnDemand, } from "./resonance.js";
45
44
  import { int, real, symbol, symbolSpans } from "./value.js";
46
45
  import { nonSpaceRuns } from "./text.js";
47
46
  import { bytesEqual, latin1 } from "../../bytes.js";
@@ -494,10 +493,7 @@ export class QueryParser {
494
493
  return null; // under-supplied → decline
495
494
  const args = picked.map((t) => t.value ?? query.subarray(t.i, t.j));
496
495
  const symbols = picked.flatMap((t, k) => t.kind === "term" ? [args[k]] : []);
497
- const resonance = symbols.length > 0
498
- ? await prefetchOpposites(this.resonance, symbols)
499
- : NO_RESONANCE;
500
- const bytes = this.alu.applyBytes(name, args, resonance);
496
+ const bytes = await withOppositesOnDemand(this.resonance, symbols, (resonance) => this.alu.applyBytes(name, args, resonance));
501
497
  if (bytes === null)
502
498
  return null;
503
499
  if (symbols.length === args.length && args.some((a) => bytesEqual(bytes, a))) {
@@ -35,6 +35,19 @@ export declare const NO_ALU_RESONANCE: AluResonance;
35
35
  * chart uses). This is the async→sync bridge for the polymorphic inverse,
36
36
  * mirroring how a concept hop's target is pre-resolved and read synchronously. */
37
37
  export declare function prefetchOpposites(resonance: AluResonance, symbols: Iterable<Uint8Array>): Promise<ResonanceSync>;
38
+ /** {@link prefetchOpposites} resolved ON DEMAND: run `apply` against a
39
+ * synchronous snapshot that answers only the opposites already resolved, and
40
+ * when the computation ASKED for one of `symbols` that is not yet resolved,
41
+ * resolve it through the host and run `apply` again — until a run asks for
42
+ * nothing new. The result is `apply(prefetchOpposites(resonance, symbols))`
43
+ * exactly: an opposite outside `symbols` reads null in both, every one inside
44
+ * that the computation reads is the host's answer in both, and `apply` is
45
+ * pure, so a run that read the same answers returns the same bytes. What it
46
+ * saves is every host call no computation reads: only the polymorphic
47
+ * inverse reads opposites, yet the eager prefetch paid one per symbol operand
48
+ * for ANY operation — on SEMA a halo-index query each, measured at 30-200 ms
49
+ * of a plain dialogue turn's parse that computed nothing. */
50
+ export declare function withOppositesOnDemand<R>(resonance: AluResonance, symbols: Iterable<Uint8Array>, apply: (sync: ResonanceSync) => R): Promise<R>;
38
51
  /** Pre-resolve BOTH capabilities a computation may need synchronously — the
39
52
  * resonant opposite of a symbol (for the polymorphic inverse) AND the operation
40
53
  * a symbol's MEANING names (for a higher-order nd op's function argument) — over
@@ -71,6 +71,47 @@ export async function prefetchOpposites(resonance, symbols) {
71
71
  recogniseOp: () => null,
72
72
  };
73
73
  }
74
+ /** {@link prefetchOpposites} resolved ON DEMAND: run `apply` against a
75
+ * synchronous snapshot that answers only the opposites already resolved, and
76
+ * when the computation ASKED for one of `symbols` that is not yet resolved,
77
+ * resolve it through the host and run `apply` again — until a run asks for
78
+ * nothing new. The result is `apply(prefetchOpposites(resonance, symbols))`
79
+ * exactly: an opposite outside `symbols` reads null in both, every one inside
80
+ * that the computation reads is the host's answer in both, and `apply` is
81
+ * pure, so a run that read the same answers returns the same bytes. What it
82
+ * saves is every host call no computation reads: only the polymorphic
83
+ * inverse reads opposites, yet the eager prefetch paid one per symbol operand
84
+ * for ANY operation — on SEMA a halo-index query each, measured at 30-200 ms
85
+ * of a plain dialogue turn's parse that computed nothing. */
86
+ export async function withOppositesOnDemand(resonance, symbols, apply) {
87
+ const allowed = new Set();
88
+ for (const bytes of symbols)
89
+ allowed.add(latin1(bytes));
90
+ const table = new Map();
91
+ const asked = new Map();
92
+ const sync = {
93
+ opposite: (bytes) => {
94
+ const key = latin1(bytes);
95
+ if (!allowed.has(key))
96
+ return null;
97
+ const known = table.get(key);
98
+ if (known !== undefined)
99
+ return known;
100
+ asked.set(key, bytes);
101
+ return null;
102
+ },
103
+ recogniseOp: () => null,
104
+ };
105
+ for (;;) {
106
+ const out = apply(sync);
107
+ if (asked.size === 0)
108
+ return out;
109
+ for (const [key, bytes] of asked) {
110
+ table.set(key, (await resonance.opposite(bytes)) ?? null);
111
+ }
112
+ asked.clear();
113
+ }
114
+ }
74
115
  /** Pre-resolve BOTH capabilities a computation may need synchronously — the
75
116
  * resonant opposite of a symbol (for the polymorphic inverse) AND the operation
76
117
  * a symbol's MEANING names (for a higher-order nd op's function argument) — over
@@ -432,6 +432,45 @@ test("conceptAnchors exposes the operation vocabulary for resonant recognition",
432
432
  assert.ok(!forms.includes("0"));
433
433
  }
434
434
  });
435
+ test("a symbol's opposite is asked of the host only when the operation reads it", async () => {
436
+ // Only the polymorphic inverse reads an opposite. Resolving every symbol
437
+ // operand's opposite up front paid one host call per operand for ANY
438
+ // operation — on SEMA a halo-index query each, 30-200 ms of a plain
439
+ // dialogue turn's parse that computed nothing.
440
+ const asked = [];
441
+ const host = {
442
+ meaningOf: async () => null,
443
+ continuation: async (b) => {
444
+ asked.push(dec(b));
445
+ return dec(b) === "large" ? enc("small") : null;
446
+ },
447
+ segment: (bytes) => {
448
+ const runs = [];
449
+ for (let i = 0; i < bytes.length;) {
450
+ if (bytes[i] === 32) {
451
+ i++;
452
+ continue;
453
+ }
454
+ let j = i;
455
+ while (j < bytes.length && bytes[j] !== 32)
456
+ j++;
457
+ runs.push({ i, j });
458
+ i = j;
459
+ }
460
+ return runs;
461
+ },
462
+ reach: Number.POSITIVE_INFINITY,
463
+ };
464
+ const u = new Alu({}, host);
465
+ // An operation that does not read opposites: nothing computed, nothing asked.
466
+ assert.deepEqual(await u.parse(enc("sqrt large")), []);
467
+ assert.deepEqual(asked, []);
468
+ // The inverse reads it: asked once, and grounded exactly as before.
469
+ const out = await u.parse(enc("opposite large"));
470
+ assert.equal(out.length, 1);
471
+ assert.equal(dec(out[0].bytes), "small");
472
+ assert.deepEqual(asked, ["large"]);
473
+ });
435
474
  test("prefetchRecognisedOps bridges async recognition to a sync map", async () => {
436
475
  // A stub host resonance: "the rate of change of" means a derivative.
437
476
  const stub = {
@@ -6,8 +6,12 @@ export declare function concatBytes(parts: Uint8Array[]): Uint8Array;
6
6
  /** Join two byte spans — the hot two-operand case of {@link concatBytes},
7
7
  * fused without the array wrapper for the search's inner fuse loop. */
8
8
  export declare function concat2(a: Uint8Array, b: Uint8Array): Uint8Array;
9
- /** Latin-1 view of a byte span — a stable, lossless string key for chart
10
- * memoization (every byte 0–255 maps to one code unit). */
9
+ /** Latin-1 view of a byte span — ONE code unit per byte, so it is injective
10
+ * and safe as an exact cache key (every byte 0–255 maps to one code unit).
11
+ * Batched `String.fromCharCode`, chunked to stay within the engine's argument
12
+ * limit on long spans; the one definition every content-keyed memo uses.
13
+ * `apply` takes the typed array as its argument list directly — a spread
14
+ * walks it through the iterator protocol first, ~2.7× slower per short key. */
11
15
  export declare function latin1(b: Uint8Array): string;
12
16
  /** First index ≥ `from` at which `needle` occurs in `hay`, or -1. A short naive
13
17
  * scan — used only to locate a result span inside a learnt framing form. */
package/dist/src/bytes.js CHANGED
@@ -35,12 +35,18 @@ export function concat2(a, b) {
35
35
  out.set(b, a.length);
36
36
  return out;
37
37
  }
38
- /** Latin-1 view of a byte span — a stable, lossless string key for chart
39
- * memoization (every byte 0–255 maps to one code unit). */
38
+ /** Latin-1 view of a byte span — ONE code unit per byte, so it is injective
39
+ * and safe as an exact cache key (every byte 0–255 maps to one code unit).
40
+ * Batched `String.fromCharCode`, chunked to stay within the engine's argument
41
+ * limit on long spans; the one definition every content-keyed memo uses.
42
+ * `apply` takes the typed array as its argument list directly — a spread
43
+ * walks it through the iterator protocol first, ~2.7× slower per short key. */
40
44
  export function latin1(b) {
45
+ const n = b.length;
41
46
  let s = "";
42
- for (let k = 0; k < b.length; k++)
43
- s += String.fromCharCode(b[k]);
47
+ for (let i = 0; i < n; i += 4096) {
48
+ s += String.fromCharCode.apply(null, b.subarray(i, Math.min(i + 4096, n)));
49
+ }
44
50
  return s;
45
51
  }
46
52
  /** First index ≥ `from` at which `needle` occurs in `hay`, or -1. A short naive
package/dist/src/canon.js CHANGED
@@ -37,6 +37,12 @@ const enc = new TextEncoder();
37
37
  * deliberately conservative: punctuation, digits and word order are content
38
38
  * and pass through untouched. */
39
39
  export function textCanon(bytes) {
40
+ if (isAscii(bytes))
41
+ return asciiCanon(bytes);
42
+ return unicodeCanon(bytes);
43
+ }
44
+ /** The general reading — the DEFINITION of {@link textCanon}. */
45
+ function unicodeCanon(bytes) {
40
46
  const s = dec
41
47
  .decode(bytes)
42
48
  .normalize("NFKC")
@@ -44,6 +50,44 @@ export function textCanon(bytes) {
44
50
  .replace(/(\S)\s+(?=\S)/g, "$1 ");
45
51
  return enc.encode(s);
46
52
  }
53
+ function isAscii(bytes) {
54
+ for (let i = 0; i < bytes.length; i++)
55
+ if (bytes[i] >= 0x80)
56
+ return false;
57
+ return true;
58
+ }
59
+ /** {@link unicodeCanon} on ASCII input, byte for byte, without the string
60
+ * round trip. Exact, not approximate: NFKC is the identity on ASCII, the
61
+ * lowercase of ASCII is A–Z → a–z, and the ASCII members of the regex's `\s`
62
+ * are TAB, LF, VT, FF, CR and SPACE — so an interior run of them becomes one
63
+ * space and an edge run stays verbatim, exactly as the regex rewrites it.
64
+ * The canonicalizer runs once per probed span on the recognition and join
65
+ * paths, so its constant is paid thousands of times per response; test/148
66
+ * pins the agreement over random ASCII and over the edge cases. */
67
+ function asciiCanon(bytes) {
68
+ const n = bytes.length;
69
+ const out = new Uint8Array(n);
70
+ let o = 0;
71
+ const ws = (b) => b === 0x20 || (b >= 0x09 && b <= 0x0d);
72
+ for (let i = 0; i < n;) {
73
+ const b = bytes[i];
74
+ if (!ws(b)) {
75
+ out[o++] = b >= 0x41 && b <= 0x5a ? b + 0x20 : b;
76
+ i++;
77
+ continue;
78
+ }
79
+ let j = i;
80
+ while (j < n && ws(bytes[j]))
81
+ j++;
82
+ if (i > 0 && j < n)
83
+ out[o++] = 0x20;
84
+ else
85
+ for (let k = i; k < j; k++)
86
+ out[o++] = bytes[k];
87
+ i = j;
88
+ }
89
+ return o === n ? out : out.slice(0, o);
90
+ }
47
91
  /** 32-bit FNV-1a over a canonical key — the integer the store's canon index
48
92
  * is keyed on. Same construction as the node table's content hash; a
49
93
  * collision is resolved by verifying canon(stored) === key, never trusted. */
@@ -204,7 +204,7 @@ export interface ContentFold {
204
204
  * streams. Verifying the bytes here would cost O(prefix) and defeat the
205
205
  * whole point, so the obligation sits with the caller, and every caller
206
206
  * discharges it structurally rather than by care: `perceiveDeposit` looks the
207
- * entry up under `latin1Key(bytes.subarray(0, L))` — the prefix's own bytes
207
+ * entry up under `latin1(bytes.subarray(0, L))` — the prefix's own bytes
208
208
  * ARE the cache key — and a conversation's fold state advances only by
209
209
  * append. A new caller that cannot make the same structural argument must
210
210
  * pass no `prev` at all; the cold path is always correct.
@@ -214,6 +214,24 @@ export declare function contentFoldIncremental(space: Space, alphabet: Alphabet,
214
214
  tree: Sema;
215
215
  fold: ContentFold;
216
216
  };
217
+ /** The node a byte stream's content fold NAMES — `foldTree` over
218
+ * {@link contentFoldSpan}'s tree, without building a single vector.
219
+ *
220
+ * A fold names a node only when every child is named, so identity needs the
221
+ * tree's SHAPE and the store's answer for each node — and the shape is a
222
+ * function of the bytes (cuts and levels) plus, inside an over-long row, each
223
+ * item's {@link itemKey}: eight coordinates of its raw gist. Those are read
224
+ * lazily through {@link boundCoord}, bit-identical to the coordinates the
225
+ * vector fold computes, so the grouping (the SAME {@link groupByLevel}) cannot
226
+ * differ. `segment(from, to)` names a level-0 segment (one flat node over
227
+ * single-byte atoms, or the atom itself); `branch(kids, from, to)` names the
228
+ * group covering [from, to) — `kids` holds null for an unnamed child, because
229
+ * the store names a branch by its BYTES when its children do not name it
230
+ * (the write side's step 1b, store.ts `intern`), so an unnamed child does not
231
+ * settle its ancestors. Every item's grouping key is read from its content,
232
+ * named or not, for the same reason. An empty stream is the caller's: its
233
+ * fold is the alphabet's zero-byte leaf, not a segment. */
234
+ export declare function contentIdentity(space: Space, alphabet: Alphabet, bytes: Uint8Array, segment: (from: number, to: number) => number | null, branch: (kids: ReadonlyArray<number | null>, from: number, to: number) => number | null): number | null;
217
235
  /** A stable-prefix fold's reusable state: the segment edge offsets and each
218
236
  * segment's independently-folded root ({@link riverFoldRaw} output). A
219
237
  * grown stream whose boundary set EXTENDS a previous fold's reuses every