@hviana/sema 0.9.4 → 0.9.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 (45) hide show
  1. package/AGENTS.md +16 -7
  2. package/dist/src/mind/learning.js +11 -12
  3. package/dist/src/mind/mind.js +4 -4
  4. package/dist/src/mind/types.d.ts +10 -15
  5. package/dist/src/store.d.ts +10 -10
  6. package/dist/src/store.js +5 -5
  7. package/docs/INDEX.md +62 -63
  8. package/docs/INVARIANTS.md +36 -18
  9. package/docs/architecture/bounded-reads.md +31 -71
  10. package/docs/architecture/caches.md +61 -81
  11. package/docs/architecture/closure.md +88 -107
  12. package/docs/architecture/commonality.md +47 -38
  13. package/docs/architecture/cost-model.md +57 -79
  14. package/docs/architecture/determinism.md +43 -55
  15. package/docs/architecture/evidence.md +158 -235
  16. package/docs/architecture/exact-vs-approximate.md +41 -35
  17. package/docs/architecture/factored-machinery.md +34 -20
  18. package/docs/architecture/fold-contract.md +110 -118
  19. package/docs/architecture/halo-sketch.md +105 -96
  20. package/docs/architecture/match-project.md +51 -42
  21. package/docs/architecture/mechanism-market.md +87 -91
  22. package/docs/architecture/memoization.md +60 -74
  23. package/docs/architecture/meter.md +37 -47
  24. package/docs/architecture/saturation.md +75 -101
  25. package/docs/architecture/store.md +118 -99
  26. package/docs/architecture/thresholds.md +66 -73
  27. package/docs/failures/tempting-but-wrong.md +139 -165
  28. package/docs/harness/gates.md +27 -32
  29. package/docs/mechanisms/alu.md +22 -69
  30. package/docs/mechanisms/cast.md +76 -71
  31. package/docs/mechanisms/confluence.md +22 -29
  32. package/docs/mechanisms/cover.md +58 -66
  33. package/docs/mechanisms/extraction.md +33 -37
  34. package/docs/mechanisms/prefix-completion.md +36 -39
  35. package/docs/mechanisms/recall.md +60 -53
  36. package/docs/mechanisms/reference.md +63 -49
  37. package/jsr.json +1 -1
  38. package/package.json +1 -1
  39. package/src/alu/README.md +90 -298
  40. package/src/derive/README.md +94 -256
  41. package/src/mind/learning.ts +11 -12
  42. package/src/mind/mind.ts +4 -4
  43. package/src/mind/types.ts +10 -15
  44. package/src/rabitq-ivf/README.md +11 -8
  45. package/src/store.ts +5 -5
@@ -1,61 +1,70 @@
1
1
  # Match → Project → Gate
2
2
 
3
- Every grounding mechanism is a configuration of one shared operation in
4
- `src/mind/match.ts`. The family is defined once and imported many times;
5
- duplicating it forks the corpus contract, moving it hides who owns the gate.
3
+ > **Law:** every grounding mechanism is a configuration
4
+ > `(matcher, direction, gate)` over one shared family in `src/mind/match.ts`.
5
+ > The family reports and moves. Only the consumer that speaks decides whether a
6
+ > shape may be voiced.
6
7
 
7
- ## The shared family in `mind/match.ts`
8
+ ## The family
8
9
 
9
- The match layer locates structure, the project layer moves along the store, and
10
- the gate layer decides whether the shape licences voicing. All three are pure
11
- functions over bytes and the store — no mechanism owns a private copy.
10
+ **Match: where the question sits in a learnt form.**
12
11
 
13
- ## The triple
12
+ - `locate` — the graded ladder, exact bytes → halo role → gist
13
+ (`exact-vs-approximate.md`).
14
+ - `alignRuns` — literal `W`-gram runs, the weave.
15
+ - `alignGraded` — literal runs plus halo-matched sites and climb proposals.
16
+ - `alignAround` / `frameSlots` — a seeded frame whose gaps are contracted.
17
+ - `bestHaloMate` — the best halo match within a list.
18
+ - `analogyStrength` / `sharedFrameStrength` — distributional and structural
19
+ analogy.
14
20
 
15
- | Role | Symbols | What it does |
16
- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
17
- | **Match** (locate structure) | `locate` (exact → halo → gist ladder), `alignRuns` (literal W-gram weave), `alignGraded` (literal + halo gaps), `alignAround` / `frameSlots` (seeded frame with contracted gaps), `bestHaloMate` (in-list halo), `analogyStrength` / `sharedFrameStrength` (distributional + structural analogy) | Finds where a query sits in a learnt form. |
18
- | **Project** (direction) | `follow` (forward to fixpoint, first hop may `conceptHop`), `reverseContext` (reverse to context), `project` (forward else reverse), `conceptHop` (halo sibling with edge) | Moves along the store from the match — forward toward answers, reverse toward contexts. |
19
- | **Gate** (structural licence) | `isSpanShaped` (OPEN reading — sparse subsequence), `containsSpan` (STRICT reading — contiguous run or resolved node), `skillExemplar` (anchor → context + answer), `carriesFillers` (substitution carriage — strict voicing licence) | Two readings; not interchangeable. |
21
+ **Project: which way to move along the store from the match.**
20
22
 
21
- Mechanisms declare only `(matcher, direction, gate)`. Thresholds behind gates
22
- live in `src/geometry.ts` — the match layer never invents a cutoff.
23
+ - `follow` — forward to a fixpoint. The first hop may be a `conceptHop`, which
24
+ borrows a halo sibling's edge.
25
+ - `reverseContext` — backward, to a context.
26
+ - `project` — forward, else backward.
23
27
 
24
- The graded ladder inside `locate` is exact → distributional → geometric:
25
- content-addressed identity first, halo similarity second, gist resonance last.
26
- Reordering the ladder or letting an approximate score override an exact hit is a
27
- correctness bug (see `exact-vs-approximate.md`).
28
+ **Gate: whether the shape licenses voicing.** There are two readings of
29
+ "contained", and they are not interchangeable:
28
30
 
29
- ## Frame reading — matcher reports, gate judges, inventory elects nothing
31
+ - `isSpanShaped` — the open reading: a sparse subsequence.
32
+ - `containsSpan` — the strict reading: a contiguous run, or a resolved node.
30
33
 
31
- `frameSlots` is the shared frame reader. It contracts every gap via
32
- `contractGap` to its varying core, tags it `substitution` / `insertion` /
33
- `deletion`, and attaches `covered` — the bytes the frame accounts for. It
34
- applies no gate; it reports.
34
+ Two more gate functions: `skillExemplar` maps an anchor to its context and
35
+ answer, and `carriesFillers` is the substitution licence.
35
36
 
36
- `carriesFillers` is the substitution gate. It judges byte-exactly:
37
+ Every threshold behind a gate is derived in `geometry.ts`. The match layer never
38
+ invents a cutoff.
37
39
 
38
- ```
39
- substituteAll(contA, fillersA → fillersB) == contB
40
- ```
40
+ ## Frame reading — the matcher reports, the gate judges, the inventory elects nothing
41
41
 
42
- If the equality holds, voicing through the slot is a derivation; if not, the
43
- slot cannot carry. This is the only place that decision is made.
42
+ - **`frameSlots` reports.** It contracts every gap to its varying core
43
+ (`contractGap`), tags it as a `substitution`, `insertion` or `deletion`, and
44
+ attaches `covered`, the bytes the frame accounts for. It applies no gate.
45
+ - **`carriesFillers` judges, byte for byte:**
46
+ `substituteAll(contA, fillersA → fillersB) == contB`. If the equality holds,
47
+ voicing through the slot is a derivation. This is the only place that decision
48
+ is made.
49
+ - **`Precomputed.frames` is the inventory.** It enumerates every pairing and
50
+ elects nothing.
44
51
 
45
- `Precomputed.frames` is the inventory. It enumerates every frame pairing the
46
- match layer finds and elects nothing — ranking and refusal belong to the
47
- consumer.
52
+ ## Why gates belong to the consumer
48
53
 
49
- ## Voicing gates belong to the consumer
54
+ A gate placed in the shared layer refuses on everyone's behalf. When
55
+ `reference`'s voicing gates lived in `frameSlots`, the shared reading became
56
+ shaped like `reference`, and it hid three of four real pairings from every other
57
+ consumer, including a definite description standing where a noun stands. So each
58
+ consumer owns its own refusal:
50
59
 
51
- The shared layer never refuses on a consumer's behalf. Reference owns its four
52
- gates: frame dominates the query, each slot reaches `W` on both sides, no
53
- insertion/deletion, fillers pairwise distinct — plus `carriesFillers` on the
54
- chosen pair. CAST, recall, and cover each apply their own gate over the same
55
- shared inventory. Moving a gate into `match.ts` would hide who owns the refusal.
60
+ - `reference` requires that the frame dominates the query, every slot reaches
61
+ `W` on both sides, there are substitutions only, the fillers are distinct, and
62
+ `carriesFillers` holds.
63
+ - CAST, `recall` and `cover` apply their own gates to the same inventory.
56
64
 
57
65
  ## Pins
58
66
 
59
- - `test/47` — frame reading split (matcher vs gate vs inventory).
60
- - `test/50` — CAST / reference voicing via `carriesFillers`.
61
- - `test/24` / `test/76` — match/project family and span-shape readings.
67
+ - `test/47` — the frame reading split into matcher, inventory and gate.
68
+ - `test/50` — voicing through `carriesFillers` in CAST and `reference`.
69
+ - `test/24`, `test/76` — the match and project family and its span-shape
70
+ readings.
@@ -1,116 +1,112 @@
1
- # Mechanism Market — The Free-Will Architecture
1
+ # Mechanism Market — Many Ways of Thinking, One Price
2
2
 
3
- Every grounding mechanism — including the ALU and user extensions — speaks one
4
- interface (`mind/pipeline-mechanism.ts`). The decider in `mind/pipeline.ts`
5
- (`think`) holds a plain list and never branches on which mechanism it holds.
3
+ > **Law:** every grounding mechanism, including the ALU and user extensions,
4
+ > speaks one interface (`mind/pipeline-mechanism.ts`). The decider (`think`,
5
+ > `mind/pipeline.ts`) weighs every candidate in one currency and never asks
6
+ > which mechanism produced it.
6
7
 
7
- ## Interface
8
+ A question can be answered in several ways that are not interchangeable: compose
9
+ it, carry structure between woven forms, intersect conditions, read a frame,
10
+ voice a slot, recall the nearest form, complete a beginning, or compute. Sema
11
+ keeps all of them and lets the evidence choose.
12
+
13
+ ## The interface
8
14
 
9
15
  ```ts
10
16
  interface PipelineMechanism {
11
- parse?(query: Uint8Array): Promise<ComputedSpan[]>; // authoritative spans
12
- floor(ctx, query, pre, worthRunning): Promise<number | null>; // bound or null
13
- run(ctx, query, pre): Promise<MechanismResult[]>; // candidates
17
+ parse?(query): Promise<ComputedSpan[]>; // authoritative spans, collected before any floor
18
+ floor(ctx, query, pre, worthRunning): Promise<number | null>; // admissible bound, or null
19
+ run(ctx, query, pre): Promise<MechanismResult[]>;
14
20
  }
15
21
  interface MechanismResult {
16
- bytes: Uint8Array;
17
- accounted: Array<[number, number]>;
18
- moves: number;
19
- used?: ReadonlySet<number>;
20
- scaffolding?: number;
21
- provenance?: string;
22
- complete?: boolean;
22
+ bytes;
23
+ accounted: Array<[number, number]>; // spans of the query explained
24
+ moves: number; // work done, on the cost ladder
25
+ used?;
26
+ scaffolding?;
27
+ provenance?;
28
+ complete?;
23
29
  }
24
30
  ```
25
31
 
26
- - `parse` is optional; all results are collected into `Precomputed.computed`
27
- before any `floor`/`run`.
28
- - `floor` returns `null` when structurally impossible, otherwise an admissible
29
- lower bound (never overstates cost).
30
- - `run` returns candidates with travelling evidence (below).
32
+ `floor` returns `null` when the mechanism cannot apply. Otherwise it returns a
33
+ lower bound that never overstates the cost.
31
34
 
32
- ## Decider
35
+ ## The decider
33
36
 
34
- `think` iterates `defaultMechanisms` in list order:
37
+ The default order is
38
+ `cover, cast, confluence, extraction, reference, recall,
39
+ prefix-completion`,
40
+ then the ALU (`aluToMechanism`), then extensions. A mechanism reports what it
41
+ did, never a price. The decider prices it in one place (`cost-model.md`):
35
42
 
36
43
  ```
37
- defaultMechanisms = [cover, cast, confluence, extraction, reference, recall,
38
- prefix-completion] + ALU (`aluToMechanism`) + extensions
44
+ weight = moves + PASS · unaccountedBytes grade = ⌊weight / STEP⌋
39
45
  ```
40
46
 
41
- Weight is one currency: `weight = moves + PASS · unaccountedBytes` where
42
- `unaccountedBytes = unexplainedSpans(query.length, accounted)`. Comparison is at
43
- `STEP` grade ; equal grade prefers fewer `scaffolding` bytes, then list order.
47
+ The lowest grade wins. At equal grade, fewer `scaffolding` bytes win, then the
48
+ earlier mechanism in the declared order.
44
49
 
45
50
  ## Four constraints
46
51
 
47
- 1. **Decoupled** — zero cross-imports between `mind/mechanisms/*`. Adding one
48
- never touches another; no mechanism asks what already decided.
49
- 2. **Declared competence** — binary structural gates inside `floor`/`run` (query
50
- length, anchor shape, weave existence). Never a learned score; rationale
51
- states why a mechanism abstained.
52
- 3. **Visible budget** — every corpus-scale loop is capped at a named constant:
53
- `√N` via `hubBound`/`hubCap` and `k = 2·recallQueryK` (`Precomputed.k`).
54
- Enforced at the store.
55
- 4. **Evidence travels** — every candidate carries `accounted` (query spans
56
- explained) and `moves` (priced on `MICRO/STEP/CONCEPT/PASS`); optionally
57
- `scaffolding` (answer bytes from unrecognised spans — equal-grade tie-break),
58
- `complete` (trained-form continuation reached via identity; post-grounding
59
- must not extend) and `provenance`. The decider honours them without knowing
60
- who set them.
52
+ 1. **Decoupled.** No mechanism imports another or asks what already decided.
53
+ Adding one touches no other.
54
+ 2. **Declared competence.** A mechanism abstains through binary structural gates
55
+ (query length, anchor shape, whether a weave exists), never through a learned
56
+ score, and the rationale says why it abstained.
57
+ 3. **Visible budget.** Every loop at corpus scale is capped by a named bound:
58
+ `hubBound`, or `Precomputed.k = 2·recallQueryK` (`bounded-reads.md`).
59
+ 4. **Evidence travels.** A candidate carries:
60
+ - `accounted` and `moves`;
61
+ - optionally `scaffolding`, the answer bytes lifted from unrecognised spans,
62
+ which only breaks ties;
63
+ - `complete`, a continuation reached by identity, which nothing after
64
+ grounding may extend;
65
+ - `used`, the anchors it speaks for;
66
+ - `provenance`.
67
+
68
+ The decider honours all of these without knowing who set them.
61
69
 
62
70
  ## Two disciplines
63
71
 
64
- - **Admissible-floor pruning.** `floor` runs for every mechanism in list order
65
- before any `run`. `run` fires only if `worthRunning(floor)` where
66
- `worthRunning = (floor) => best === null || grade(floor) < grade(best.weight)`.
67
- Cover runs first so a near-zero-cost computed span prunes the rest through the
68
- same mechanism — not a special case.
69
-
70
- - **A cheaper bound is looked at first.** Before mechanism `m` first-touches
71
- anything, every LATER mechanism whose floor grade is strictly below `m`'s runs
72
- ahead of it (cheapest first); the lowest grade they reach is `bound`, and any
73
- mechanism floored above `bound` is skipped (`meter.mechanismsBounded`). The
74
- bound is learnt by calling `floor` with a `worthRunning` that refuses — the
75
- investment discipline makes that free. The DECISION is the declared order's: a
76
- run-ahead mechanism bounds the final grade whether or not the declared order
77
- would have run it (if pruned, the incumbent already sat at or below its
78
- floor); every candidate above `bound` loses to the winner, and every mechanism
79
- floored at or below it meets the same run-or-prune decision, so `consider`
80
- replays the same candidates in declared order. Equal floors are not skipped,
81
- so an earlier mechanism keeps the tie it would win. Running ahead is never
82
- extra work: only a mechanism floored at or below `p` can prune `p`, and each
83
- such mechanism has already run or runs ahead of `p`. Measured on the
84
- 31.7M-node store: a lowercased Persian turn (#83) went from 18.0 s to 1.2 s,
85
- and #114 from 1.9 s to 0.7 s. In both, a grade-1 recall or prefix answer no
86
- longer waits behind CAST's climb and weave. Of 42 composition-regime queries,
87
- none changed its answer.
88
-
89
- - **Investment discipline.** `worthRunning` is passed _into_ `floor`. A floor
90
- that would first-touch an expensive shared analysis (`pre.attention()` climb,
91
- `pre.weave()`, `pre.resonance()`) checks `worthRunning(cheapestBound)` first
92
- and returns the uninvested bound if it loses. Never compute a shared analysis
93
- just to discard it. `cast.ts`/`extraction.ts` are the references.
94
-
95
- ## Accounting
96
-
97
- - **Extraction:** located frames are always evidence; the span between them
98
- counts only when _both_ borders were located. An open-ended read is priced by
99
- exclusion (`PASS`/byte).
100
- - **Reverse reading:** `reverseContext` produces bytes but explains nothing
101
- forward: `accounted = []`, weight ≈ `PASS·|query|` — last resort by
102
- arithmetic, not rule.
103
- - **Paid acts are accounted:** the bridge's corroborated substitutions cost
104
- `CONCEPT` each in `moves`, so their spans must be `accounted`; otherwise the
105
- same act is charged twice (`PASS`/byte dominates).
106
-
107
- `accounted` is a cost-ladder quantity; `cover.ts` leaves masked computed spans
108
- out so `PASS`-bridged bytes are still charged. `narrowDecision` and
109
- `thinGrounding` are observational only.
72
+ **Never compute what cannot change the decision.** `floor` runs for every
73
+ mechanism before any `run`, and a mechanism runs only if
74
+ `worthRunning(floor) = best === null || grade(floor) < grade(best)`. Every
75
+ `floor` that would first touch an expensive shared analysis (`attention()`,
76
+ `weave()`, `resonance()`) asks `worthRunning` first, and returns its uninvested
77
+ bound if it would lose. `cast.ts` and `extraction.ts` are the references.
78
+
79
+ **Look at a cheaper bound first.** Before a mechanism first touches anything,
80
+ every later mechanism whose floor grade is strictly lower runs ahead of it,
81
+ cheapest first. The lowest grade they reach becomes a bound, and any mechanism
82
+ floored above that bound is skipped (`meter.mechanismsBounded`). The decision
83
+ stays the declared order's:
84
+
85
+ - every candidate above the bound loses to the winner;
86
+ - every mechanism at or below the bound meets the same decision it would have
87
+ met in order;
88
+ - equal floors are never skipped.
89
+
90
+ This is never extra work. On the 31.7M-node store a lowercased Persian turn went
91
+ from 18.0 s to 1.2 s, and another query from 1.9 s to 0.7 s, because a grade-1
92
+ recall no longer waited behind CAST's climb. Of 42 composition queries, none
93
+ changed its answer.
94
+
95
+ ## Accounting rules
96
+
97
+ - **Extraction.** Located frames are evidence. The span between two frames
98
+ counts only when both borders were located, and an open-ended read is priced
99
+ by exclusion.
100
+ - **Reverse reading.** `reverseContext` produces bytes but explains nothing
101
+ forward, so `accounted = []`. It is the last resort by arithmetic, not by
102
+ rule.
103
+ - **A paid act is accounted.** The bridge charges `CONCEPT` per substitution, so
104
+ the substituted spans are `accounted`. Otherwise one act is charged twice, and
105
+ `PASS` per byte decides against it.
110
106
 
111
107
  ## Pins
112
108
 
113
- - `test/01-floor` — floor geometry.
114
- - `test/04-think` — decider, admissible pruning, investment discipline.
115
- - `test/153` — run-ahead bounds: the composition market is skipped below CAST's
116
- floor, and the decision equals a declared-order oracle.
109
+ - `test/01` — the floor's geometry.
110
+ - `test/04` — the decider, admissible pruning and the investment discipline.
111
+ - `test/153` — the run-ahead bound: a mechanism floored above it is skipped, and
112
+ the decision equals the declared-order oracle.
@@ -1,96 +1,82 @@
1
- # Memoization — Shared Evidence Without Duplication
1
+ # Memoization — Shared Evidence, Computed Once
2
2
 
3
- > **Law:** asking never writes, so structural reads are pure during one
4
- > response. Memoization elides probes, not evidence.
3
+ > **Law:** asking never writes, so within one response every structural read is
4
+ > pure. A memo may skip a probe. It may never change what inference computes.
5
5
 
6
- Two layers: `Precomputed` (response-scoped shared analyses) and `Mind`
7
- per-response memos. Both are accelerators that must not change what inference
8
- computes.
6
+ **Why.** One question is read by up to eight mechanisms, and the same analysis
7
+ (the consensus climb, the weave, a resonance query) must not be paid eight
8
+ times. Nor may whoever asked first be billed for everyone.
9
9
 
10
- ## Precomputed — one response, one container
10
+ ## `Precomputed` — one response, one container (`mind/pipeline-mechanism.ts`)
11
11
 
12
- `Precomputed` (`src/mind/pipeline-mechanism.ts`) is the sole place a response's
13
- shared evidence lives. Created by `think` (`src/mind/pipeline.ts`) before the
14
- mechanism loop.
12
+ `think` creates it before any mechanism runs. It is the only place a response's
13
+ shared evidence lives.
15
14
 
16
- ### Eager — populated before any `floor`/`run`
15
+ **Eager**, populated before any `floor` or `run`:
17
16
 
18
- - `rec: Recognition` — structural + canonical decomposition (`recognise`)
19
- - `computed: ComputedSpan[]` — `parse()` results from all mechanisms (e.g. ALU)
20
- - `guide: Vec` — query gist, the response-wide disambiguation guide
21
- - `k: number` — `cfg.recallQueryK * 2`, the breadth for resonance/weave/climb
17
+ - `rec`, the recognition;
18
+ - `computed`, every mechanism's `parse` spans;
19
+ - `guide`, the query gist;
20
+ - `k = 2·recallQueryK`.
22
21
 
23
- ### Lazy — computed on first touch, cached by promise
22
+ **Lazy**, cached by promise, so the first caller starts the work and every later
23
+ caller awaits it:
24
24
 
25
- Expensive analyses are `async` and cached by promise: the first caller starts
26
- the work, every later caller awaits the same promise.
25
+ - `attention()`, the consensus climb;
26
+ - `weave()`;
27
+ - `resonance()`, the single top-`k` ANN query;
28
+ - `frames()`;
29
+ - `spanShapedOf(anchor)` / `spanShapedAll()`;
30
+ - the window identities `queryWindows`, `queryResolved` and `windowsOf`;
31
+ - `reachMemo`.
27
32
 
28
- - `attention()` — `climbAttentionAll` (roots + ranked anchors)
29
- - `weave()` — `alignGraded` over top-k anchors
30
- - `resonance()` — `store.resonate(guide, k)` (single ANN query)
31
- - `frames()` — `frameSlots` inventory from resonance
32
- - `spanShapedOf(anchor)` / `spanShapedAll()` — per-anchor `skillExemplar`,
33
- memoised per id
34
- - `queryWindows` / `queryResolved` / `windowsOf(anchor)` — W-window identities
35
- - `reachMemo` — `sharedReachMemo(ctx)` (ancestor reach, § below)
33
+ A mechanism that never asks pays nothing, and two that ask the same question pay
34
+ once. A `floor` checks `worthRunning` before it first touches an expensive
35
+ analysis (`mechanism-market.md`). Each shared analysis is charged to its own
36
+ meter phase through `Precomputed.shared`, never to whichever mechanism touched
37
+ it first (`meter.md`).
36
38
 
37
- A mechanism that never asks pays nothing; two mechanisms asking the same
38
- question pay once. `floor()` must gate on `worthRunning` before first-touching
39
- an expensive analysis.
39
+ ## Mind memos — `beginResponse` → `endResponse` (`mind/mind.ts`)
40
40
 
41
- ## Mind memos — `beginResponse` → `endResponse`
41
+ `respond` takes fresh maps. `respondTurn` reuses the conversation's maps, which
42
+ are content-keyed across turns.
42
43
 
43
- `Mind` (`src/mind/mind.ts:beginResponse`/`endResponse`) swaps per-response state
44
- for each inference call. `respond` takes fresh maps; `respondTurn` reuses the
45
- conversation's persistent ones (content-keyed, cross-turn).
44
+ | Memo | Key | Lifetime |
45
+ | ---------------------------------------------------- | --------------------- | ----------------------------------- |
46
+ | `perceiveMemo` | bytes + boundary set | response or conversation |
47
+ | `recogniseMemo`, `climbMemo` | bytes | response or conversation |
48
+ | `canonMemo` | bytes | response, when a `canon` is set |
49
+ | `_resolvedSubtrees` | tree node (`WeakMap`) | response or conversation |
50
+ | `_edgeChoice` (the pick memo), `_edgeAsked` | node / question | response; cleared at the end |
51
+ | `sharedReachMemo`, structural probes (`traverse.ts`) | node | cleared on write or at 100K entries |
46
52
 
47
- | Memo | Key | Scope |
48
- | ------------------- | ----------------------------------------- | -------------------------------------------- |
49
- | `perceiveMemo` | `perceiveKey(bytes)` (latin1) | response / conversation |
50
- | `recogniseMemo` | `latin1(bytes)` | response / conversation |
51
- | `climbMemo` | `latin1(bytes)` | response / conversation |
52
- | `canonMemo` | `latin1(bytes)` | response (when `canon` set) |
53
- | `_resolvedSubtrees` | `WeakMap<Sema, {id,len}>` (node identity) | response / conversation |
54
- | `_edgeChoice` | `Map<nodeId, pick>` | response only — **cleared** in `endResponse` |
55
- | `_gistCache` | `BoundedMap<nodeId, Vec>` 32 MB | **session-lifetime** (not per-response) |
53
+ `foldTree` takes the `_resolvedSubtrees` fast path only when no visitor is
54
+ passed. A walk that emits sites always descends in full, and the cache only
55
+ elides store probes.
56
56
 
57
- `_gistCache` (≈ 8K gists at D=1024) survives across responses; all others are
58
- dropped or cleared at `endResponse`. `_resolvedSubtrees` elides store probes
59
- when `visit` is absent; with a visitor it still walks in full (see
60
- `src/mind/primitives.ts:foldTree`).
57
+ ## The trace boundary
61
58
 
62
- ## Trace boundary — what is bypassed
59
+ A traced response must emit every step and still give the same answer.
63
60
 
64
- Traced responses must emit every step, but must not change the answer.
65
-
66
- - **Bypassed:** `_edgeChoice` via `guidedNext`
67
- (`src/mind/traverse.ts:guidedNext`) and `sharedReachMemo`
68
- (`src/mind/traverse.ts:sharedReachMemo`). Both return fresh empty maps when
69
- `ctx.trace !== null`; `chooseNext` recomputes identically (pure over store +
70
- guide).
71
- - **Always consulted:** `perceiveMemo`, `recogniseMemo`, `climbMemo` (and their
72
- underlying `perceive`/`recognise`/`climbAttention` caches). Bypassing breaks
73
- idempotence.
74
-
75
- `foldTree`'s subtree fast path is taken only when no `visit` is supplied. With a
76
- visitor (recognition, attention) the walk still descends; the cache elides only
77
- probes. Bypassing `recogniseMemo` under trace re-ran `recogniseImpl` with a warm
78
- `_resolvedSubtrees` and emitted fewer sites (observed 31 → 5) — a correctness
79
- change, not just a slowdown.
80
-
81
- ## Meter — charge work to itself
82
-
83
- Shared analyses are charged to their own phase (`meter.time(phase, fn)` in
84
- `Precomputed.shared`), not to the mechanism that first touched them
85
- (`src/meter.ts:PhaseCost`). Without this, the profile reads "cast.floor costs 2
86
- s" when the cost was the consensus climb cast paid for on everyone's behalf.
61
+ - **Bypassed under trace:** the pick memo (`guidedNext`) and `sharedReachMemo`.
62
+ Both return fresh maps, and `chooseNext` recomputes the same pick from the
63
+ store and the question.
64
+ - **Always consulted:** `perceiveMemo`, `recogniseMemo` and `climbMemo`.
65
+ Bypassing `recogniseMemo` once re-ran recognition over a warm subtree cache
66
+ and emitted 5 sites instead of 31. That was a change in the answer, not just a
67
+ slowdown.
68
+ - **The pick memo is cleared when the climb publishes its points.** A pick made
69
+ earlier read less evidence, and keeping it made traced and untraced responses
70
+ disagree.
87
71
 
88
72
  ## Adding a shared analysis
89
73
 
90
- Add one lazy method to `Precomputed`. No new memo map elsewhere. Gate it behind
91
- `worthRunning` in `floor()`.
74
+ Add one lazy method to `Precomputed`, never a memo map elsewhere, and guard it
75
+ behind `worthRunning` in `floor`.
92
76
 
93
77
  ## Pins
94
78
 
95
- - `test/42` — recognition idempotence under trace: traced and untraced
96
- `recognise` return the same site count and cached object.
79
+ - `test/42` — recognition is idempotent under trace: same site count, same
80
+ cached object.
81
+ - `test/155.4` — traced and untraced responses agree once the climb publishes
82
+ its points.
@@ -1,55 +1,45 @@
1
- # Meter — Work Accounting
1
+ # Meter — What an Answer Cost
2
2
 
3
- `src/meter.ts` is the one computational-usage accounting surface. It counts what
4
- inference _cost_ so a slow response can be attributed instead of guessed at. The
5
- rationale says why an answer was chosen; the meter says what it cost to choose
6
- it. Harness: the public path — `new Mind({ profile: true })`, then
7
- `mind.lastCost` (`formatReport` / `sumReports`).
3
+ > **Law:** `src/meter.ts` is the one surface that accounts for work. Inference
4
+ > writes to it and never reads it. Its counters are exact. Its milliseconds are
5
+ > hints.
8
6
 
9
- ## Five contracts
10
-
11
- 1. **Write-only from inference.** No counter reaches a decision, a threshold, or
12
- an ordering. Determinism survives only because the meter is observed, never
13
- consulted. Every call site is `meter?.x++` on a nullable field.
14
-
15
- 2. **Counters vs hints.** Counters are exact and diffable: a regression shows in
16
- a diff of two COLD runs; a repeated query meters less, as memos warm.
17
- Millisecond fields (`elapsedMs`, per-phase `ms`) are non-deterministic hints
18
- reported separately — never use them to gate behaviour.
19
-
20
- 3. **Phases nest, they do not partition.** Each phase is charged by the layer
21
- doing the work (`recognise`, the climb's two, the bridge), and a mechanism's
22
- `floor` contains whatever shared analysis it first-touched. Read a phase as
23
- inclusive wall-clock; never sum phases. `CostReport.elapsedMs` is the only
24
- whole.
7
+ The rationale says why an answer was chosen; the meter says what choosing it
8
+ cost. Together they are Sema's development instrumentation, read only through
9
+ the public path: `new Mind({ profile: true })`, then `mind.lastCost`
10
+ (`formatReport`, `sumReports`).
25
11
 
26
- 4. **Count once.** Off by default and free when off
27
- (`new Mind({ profile:
28
- true })` to attach). A layer that wants to be
29
- visible bumps a field in `meter.ts` — it does not grow a private counter.
30
- (Legacy `danglingReads` / `compactFailures` in `store.ts` are health
31
- counters, not per-response work.)
32
-
33
- 5. **Shared analyses charged to themselves.** The first toucher pays the wall
34
- clock, but every later consumer gets the result free. Attribution follows the
35
- analysis, not the mechanism that first triggered it — otherwise the profile
36
- misreads which work is expensive (e.g. the consensus climb billed through
37
- whichever mechanism happened to need it first).
38
-
39
- ## Where counters live
40
-
41
- `src/meter.ts:Meter` is the only definition of a counter name. Phases are
42
- charged via `meter.time(phase, fn)` / `meter.timeSync(phase, fn)`, which
43
- snapshot counters on entry and attribute the delta to the phase. The sync/async
44
- seam is load-bearing: synchronous layers (perception, recognition, graph search)
45
- must use `timeSync` so the profiled path does not await where the unprofiled
46
- path does not.
12
+ ## Five contracts
47
13
 
48
- `CostReport` is plain JSON (`version`, `elapsedMs`, `queryBytes`, `counters`,
49
- `phases`). Zero-valued counters are dropped; `formatReport` renders the three
50
- heaviest counters per phase.
14
+ 1. **Write-only.** No counter reaches a decision, a threshold or an ordering.
15
+ Every call site is `meter?.x++` on a nullable field, so determinism survives
16
+ because the meter is observed, never consulted.
17
+ 2. **Counters decide; milliseconds hint.** Counters are deterministic and can be
18
+ diffed. Compare two cold runs, because a repeated query meters less as memos
19
+ warm. `elapsedMs` and per-phase `ms` depend on the machine: on a busy
20
+ workstation, back-to-back runs of one build differ by up to ±15%. Judge a
21
+ change by its counter deltas, never by time alone.
22
+ 3. **Phases nest; they do not partition.** A phase is charged by the layer doing
23
+ the work, and a mechanism's `floor` includes whatever shared analysis it
24
+ touched first. Read a phase as inclusive wall-clock time and never sum
25
+ phases. `CostReport.elapsedMs` is the only whole.
26
+ 4. **One home for every counter name.** The meter is off by default and free
27
+ when off. A layer that wants to be visible adds a field to `Meter`. It never
28
+ grows a private counter, a log or a timing probe (`AGENTS.md` §6). The
29
+ store's `danglingReads` and `compactFailures` are session health counters,
30
+ not per-response work.
31
+ 5. **A shared analysis has its own phase.** Phases are charged with
32
+ `meter.time(phase, fn)`, or with `timeSync` for synchronous layers, so the
33
+ profiled path never awaits where the unprofiled path does not.
34
+ `Precomputed.shared` gives each shared analysis its own phase. Otherwise the
35
+ profile would read "`cast.floor` costs 2 s" when the cost was the consensus
36
+ climb, run on everyone's behalf.
37
+
38
+ `CostReport` is plain JSON: `version`, `elapsedMs`, `queryBytes`, `counters` and
39
+ `phases`. Zero counters are dropped, and `formatReport` shows the three heaviest
40
+ counters in each phase.
51
41
 
52
42
  ## Pins
53
43
 
54
- - `test/55` — `Meter`, `CostReport`, `searchPops` / `searchPushes`, phase
44
+ - `test/55` — `Meter`, `CostReport`, `searchPops`/`searchPushes`, and phase
55
45
  nesting.