@hviana/sema 0.9.3 → 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.
- package/AGENTS.md +16 -7
- package/dist/src/mind/learning.js +11 -12
- package/dist/src/mind/mind.js +4 -4
- package/dist/src/mind/types.d.ts +10 -15
- package/dist/src/store.d.ts +10 -10
- package/dist/src/store.js +5 -5
- package/docs/INDEX.md +62 -59
- package/docs/INVARIANTS.md +36 -18
- package/docs/PHILOSOPHY.md +317 -0
- package/docs/architecture/bounded-reads.md +31 -71
- package/docs/architecture/caches.md +61 -81
- package/docs/architecture/closure.md +88 -107
- package/docs/architecture/commonality.md +47 -38
- package/docs/architecture/cost-model.md +57 -79
- package/docs/architecture/determinism.md +43 -55
- package/docs/architecture/evidence.md +158 -235
- package/docs/architecture/exact-vs-approximate.md +41 -35
- package/docs/architecture/factored-machinery.md +34 -20
- package/docs/architecture/fold-contract.md +110 -118
- package/docs/architecture/halo-sketch.md +105 -96
- package/docs/architecture/match-project.md +51 -42
- package/docs/architecture/mechanism-market.md +87 -91
- package/docs/architecture/memoization.md +60 -74
- package/docs/architecture/meter.md +37 -47
- package/docs/architecture/saturation.md +75 -101
- package/docs/architecture/store.md +118 -99
- package/docs/architecture/thresholds.md +66 -73
- package/docs/failures/tempting-but-wrong.md +139 -165
- package/docs/harness/gates.md +27 -32
- package/docs/mechanisms/alu.md +22 -69
- package/docs/mechanisms/cast.md +76 -71
- package/docs/mechanisms/confluence.md +22 -29
- package/docs/mechanisms/cover.md +58 -66
- package/docs/mechanisms/extraction.md +33 -37
- package/docs/mechanisms/prefix-completion.md +36 -39
- package/docs/mechanisms/recall.md +60 -53
- package/docs/mechanisms/reference.md +63 -49
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alu/README.md +90 -298
- package/src/derive/README.md +94 -256
- package/src/mind/learning.ts +11 -12
- package/src/mind/mind.ts +4 -4
- package/src/mind/types.ts +10 -15
- package/src/rabitq-ivf/README.md +11 -8
- package/src/store.ts +5 -5
|
@@ -1,61 +1,70 @@
|
|
|
1
1
|
# Match → Project → Gate
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
`src/mind/match.ts`.
|
|
5
|
-
|
|
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
|
|
8
|
+
## The family
|
|
8
9
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
31
|
+
- `isSpanShaped` — the open reading: a sparse subsequence.
|
|
32
|
+
- `containsSpan` — the strict reading: a contiguous run, or a resolved node.
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
60
|
-
- `test/50` —
|
|
61
|
-
- `test/24
|
|
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 —
|
|
1
|
+
# Mechanism Market — Many Ways of Thinking, One Price
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
interface (`mind/pipeline-mechanism.ts`). The decider
|
|
5
|
-
|
|
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
|
-
|
|
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
|
|
12
|
-
floor(ctx, query, pre, worthRunning): Promise<number | null>; // bound or null
|
|
13
|
-
run(ctx, query, pre): Promise<MechanismResult[]>;
|
|
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
|
|
17
|
-
accounted: Array<[number, number]>;
|
|
18
|
-
moves: number;
|
|
19
|
-
used
|
|
20
|
-
scaffolding
|
|
21
|
-
provenance
|
|
22
|
-
complete
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
##
|
|
35
|
+
## The decider
|
|
33
36
|
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
prefix-completion] + ALU (`aluToMechanism`) + extensions
|
|
44
|
+
weight = moves + PASS · unaccountedBytes grade = ⌊weight / STEP⌋
|
|
39
45
|
```
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
48
|
-
|
|
49
|
-
2. **Declared competence
|
|
50
|
-
length, anchor shape, weave
|
|
51
|
-
|
|
52
|
-
3. **Visible budget
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`complete
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- **
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
114
|
-
- `test/04
|
|
115
|
-
- `test/153` — run-ahead
|
|
116
|
-
|
|
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
|
|
1
|
+
# Memoization — Shared Evidence, Computed Once
|
|
2
2
|
|
|
3
|
-
> **Law:** asking never writes, so
|
|
4
|
-
>
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
`
|
|
13
|
-
shared evidence lives.
|
|
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
|
-
|
|
15
|
+
**Eager**, populated before any `floor` or `run`:
|
|
17
16
|
|
|
18
|
-
- `rec
|
|
19
|
-
- `computed
|
|
20
|
-
- `guide
|
|
21
|
-
- `k
|
|
17
|
+
- `rec`, the recognition;
|
|
18
|
+
- `computed`, every mechanism's `parse` spans;
|
|
19
|
+
- `guide`, the query gist;
|
|
20
|
+
- `k = 2·recallQueryK`.
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
**Lazy**, cached by promise, so the first caller starts the work and every later
|
|
23
|
+
caller awaits it:
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
+
`respond` takes fresh maps. `respondTurn` reuses the conversation's maps, which
|
|
42
|
+
are content-keyed across turns.
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
A traced response must emit every step and still give the same answer.
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
- **
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
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
|
|
96
|
-
|
|
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 —
|
|
1
|
+
# Meter — What an Answer Cost
|
|
2
2
|
|
|
3
|
-
`src/meter.ts` is the one
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
-
`
|
|
50
|
-
|
|
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
|
|
44
|
+
- `test/55` — `Meter`, `CostReport`, `searchPops`/`searchPushes`, and phase
|
|
55
45
|
nesting.
|