pi-mega-compact 0.20.70 → 0.20.71
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/dist/extensions/dashboard-server/routes-vector-cortex-crystals.js +45 -30
- package/dist/extensions/dashboard-server/routes-vector-cortex-diagnostics.js +50 -29
- package/dist/extensions/dashboard-server/routes-vector-cortex-economics.js +33 -20
- package/dist/extensions/dashboard-server/routes-vector-cortex-helpers.js +54 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-policy.js +33 -16
- package/dist/src/vector-cortex/cache/store.js +25 -2
- package/dist/src/vector-cortex/encoder/asset.js +34 -2
- package/dist/src/vector-cortex/encoder/router.js +31 -3
- package/dist/src/vector-cortex/livewire/livewire-live.js +163 -0
- package/dist/src/vector-cortex/livewire/livewire-registry.js +73 -0
- package/dist/src/vector-cortex/livewire/livewire-runtime.js +107 -0
- package/dist/src/vector-cortex/livewire/livewire-snapshot.js +101 -0
- package/dist/src/vector-cortex/livewire/livewire-types.js +19 -0
- package/dist/src/vector-cortex/provider/registry.js +44 -12
- package/dist/vector-cortex/cache/store.js +25 -2
- package/dist/vector-cortex/encoder/asset.js +34 -2
- package/dist/vector-cortex/encoder/router.js +31 -3
- package/dist/vector-cortex/provider/registry.js +44 -12
- package/extensions/dashboard-server/routes-vector-cortex-crystals.ts +46 -30
- package/extensions/dashboard-server/routes-vector-cortex-diagnostics.ts +52 -29
- package/extensions/dashboard-server/routes-vector-cortex-economics.ts +35 -20
- package/extensions/dashboard-server/routes-vector-cortex-helpers.ts +84 -0
- package/extensions/dashboard-server/routes-vector-cortex-policy.ts +35 -16
- package/package.json +1 -1
- package/src/vector-cortex/cache/store.ts +26 -2
- package/src/vector-cortex/encoder/asset.ts +46 -3
- package/src/vector-cortex/encoder/router.ts +56 -2
- package/src/vector-cortex/encoder/types.ts +3 -0
- package/src/vector-cortex/livewire/livewire-live.ts +223 -0
- package/src/vector-cortex/livewire/livewire-registry.ts +91 -0
- package/src/vector-cortex/livewire/livewire-runtime.ts +117 -0
- package/src/vector-cortex/livewire/livewire-snapshot.ts +108 -0
- package/src/vector-cortex/livewire/livewire-types.ts +84 -0
- package/src/vector-cortex/provider/economics.ts +9 -31
- package/src/vector-cortex/provider/registry.ts +82 -11
- package/src/vector-cortex/provider/types.ts +37 -0
|
@@ -26,7 +26,19 @@ import {
|
|
|
26
26
|
} from "./types.js";
|
|
27
27
|
|
|
28
28
|
export type AssetVerifyResult =
|
|
29
|
-
| {
|
|
29
|
+
| {
|
|
30
|
+
ok: true;
|
|
31
|
+
embeddedBytes: number;
|
|
32
|
+
maxTokens: number;
|
|
33
|
+
onnxDigest: string;
|
|
34
|
+
tokenizerDigest: string;
|
|
35
|
+
/**
|
|
36
|
+
* ML5-A (VC2B-2): digest of the manifest-pinned `trained-heads.json`
|
|
37
|
+
* sibling when the manifest declares `headWeights`; null when the
|
|
38
|
+
* manifest ships no head weights (the committed placeholder bundle).
|
|
39
|
+
*/
|
|
40
|
+
headWeightsDigest: string | null;
|
|
41
|
+
}
|
|
30
42
|
| { ok: false; code: string };
|
|
31
43
|
|
|
32
44
|
/** True when `p` is a single basename: non-empty, no path separators, no "..",
|
|
@@ -80,7 +92,14 @@ function isManifest(m: unknown): m is ModelManifestV1 {
|
|
|
80
92
|
typeof o.onnx.path === "string" &&
|
|
81
93
|
typeof o.onnx.sha256 === "string" &&
|
|
82
94
|
typeof o.tokenizer.path === "string" &&
|
|
83
|
-
typeof o.tokenizer.sha256 === "string"
|
|
95
|
+
typeof o.tokenizer.sha256 === "string" &&
|
|
96
|
+
// ML5-A (VC2B-2): optional headWeights must, when present, be a valid
|
|
97
|
+
// ManifestAssetFile (path basename + sha256 + bytes).
|
|
98
|
+
(o.headWeights === undefined ||
|
|
99
|
+
(!!o.headWeights &&
|
|
100
|
+
typeof o.headWeights.path === "string" &&
|
|
101
|
+
typeof o.headWeights.sha256 === "string" &&
|
|
102
|
+
typeof o.headWeights.bytes === "number"))
|
|
84
103
|
);
|
|
85
104
|
}
|
|
86
105
|
|
|
@@ -122,6 +141,11 @@ export function verifyEncoderAsset(
|
|
|
122
141
|
if (!isBasename(manifest.onnx.path) || !isBasename(manifest.tokenizer.path)) {
|
|
123
142
|
return { ok: false, code: ENC_FAIL.MANIFEST_INVALID };
|
|
124
143
|
}
|
|
144
|
+
// ML5-A (VC2B-2): a manifest-declared headWeights path must also be a bare
|
|
145
|
+
// basename (no traversal into arbitrary paths off the asset dir).
|
|
146
|
+
if (manifest.headWeights !== undefined && !isBasename(manifest.headWeights.path)) {
|
|
147
|
+
return { ok: false, code: ENC_FAIL.MANIFEST_INVALID };
|
|
148
|
+
}
|
|
125
149
|
|
|
126
150
|
const onnxPath = join(assetDir, manifest.onnx.path);
|
|
127
151
|
const onnxDigest = digestFile(onnxPath);
|
|
@@ -133,13 +157,32 @@ export function verifyEncoderAsset(
|
|
|
133
157
|
if (tokDigest === null) return { ok: false, code: ENC_FAIL.ASSET_UNREADABLE };
|
|
134
158
|
if (tokDigest !== manifest.tokenizer.sha256) return { ok: false, code: ENC_FAIL.DIGEST_MISMATCH };
|
|
135
159
|
|
|
160
|
+
// ML5-A (VC2B-2): verify the manifest-pinned trained-heads sibling when the
|
|
161
|
+
// manifest declares it. Absent declaration -> headWeightsDigest: null and no
|
|
162
|
+
// failure (committed placeholder bundle ships no trained weights).
|
|
163
|
+
let headWeightsDigest: string | null = null;
|
|
164
|
+
if (manifest.headWeights !== undefined) {
|
|
165
|
+
const hwPath = join(assetDir, manifest.headWeights.path);
|
|
166
|
+
const hwDigest = digestFile(hwPath);
|
|
167
|
+
if (hwDigest === null) return { ok: false, code: ENC_FAIL.ASSET_UNREADABLE };
|
|
168
|
+
if (hwDigest !== manifest.headWeights.sha256) return { ok: false, code: ENC_FAIL.DIGEST_MISMATCH };
|
|
169
|
+
headWeightsDigest = hwDigest;
|
|
170
|
+
}
|
|
171
|
+
|
|
136
172
|
let embeddedBytes = 0;
|
|
137
173
|
try {
|
|
138
174
|
embeddedBytes = statSync(onnxPath).size + statSync(tokPath).size;
|
|
139
175
|
} catch {
|
|
140
176
|
return { ok: false, code: ENC_FAIL.ASSET_UNREADABLE };
|
|
141
177
|
}
|
|
142
|
-
return {
|
|
178
|
+
return {
|
|
179
|
+
ok: true,
|
|
180
|
+
embeddedBytes,
|
|
181
|
+
maxTokens: manifest.maxTokens,
|
|
182
|
+
onnxDigest,
|
|
183
|
+
tokenizerDigest: tokDigest,
|
|
184
|
+
headWeightsDigest,
|
|
185
|
+
};
|
|
143
186
|
}
|
|
144
187
|
|
|
145
188
|
/**
|
|
@@ -26,15 +26,24 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { createEncoderRuntime } from "./runtime.js";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
encodeVectorSet,
|
|
31
|
+
loadHeadProjections,
|
|
32
|
+
projectHeadFromTrunk,
|
|
33
|
+
type HeadProjectionOptions,
|
|
34
|
+
type HeadProjectionTable,
|
|
35
|
+
} from "./heads.js";
|
|
30
36
|
import { embedTrigram512, selectTrigramBFallback } from "./trigram.js";
|
|
31
37
|
import { embedLexical, selectLexicalC } from "./lexical.js";
|
|
32
38
|
import { createEncoderHeadsReporter, type EncoderHeadsReporter } from "./emit-vc2b.js";
|
|
39
|
+
import { ML5A_ENABLED } from "../../config/vector-cortex.js";
|
|
33
40
|
import {
|
|
34
41
|
ENC_FAIL,
|
|
42
|
+
ENCODER_HEAD_ORDER,
|
|
35
43
|
type EncoderInput,
|
|
36
44
|
type EncoderLoadResult,
|
|
37
45
|
type EncoderRuntime,
|
|
46
|
+
type HeadVector,
|
|
38
47
|
type VectorSetV1,
|
|
39
48
|
} from "./types.js";
|
|
40
49
|
|
|
@@ -61,6 +70,15 @@ export interface RouterOptions extends HeadProjectionOptions {
|
|
|
61
70
|
/** Forced fallback: skips the A load and selects the named VC2B fallback
|
|
62
71
|
* (used to exercise C when A and B are both disabled). Optional. */
|
|
63
72
|
readonly forceFallback?: "B" | "C";
|
|
73
|
+
/**
|
|
74
|
+
* ML5-A (VC2B-2): path to the `trained-heads-v1` artifact. When supplied AND
|
|
75
|
+
* the MEGACOMPACT_ML5_A gate is on AND the artifact loads (correct seed +
|
|
76
|
+
* shape), the mode-A VectorSet is produced by projecting the real trained head
|
|
77
|
+
* matrices over the trunk embedding (`projectHeadFromTrunk`). When absent /
|
|
78
|
+
* flag-off / unloadable, the mode-A producer falls back to the deterministic
|
|
79
|
+
* LCG placeholder `encodeVectorSet` (byte-identical predecessor).
|
|
80
|
+
*/
|
|
81
|
+
readonly trainedHeadsPath?: string;
|
|
64
82
|
}
|
|
65
83
|
|
|
66
84
|
/** Deterministic text derived from an int token sequence so the asset-free
|
|
@@ -133,10 +151,46 @@ export function encodeOrFallback(
|
|
|
133
151
|
if (!inferred.ok) {
|
|
134
152
|
return fallbackFromLoad({ ok: false, mode: "B", code: inferred.code }, reporter, tokens);
|
|
135
153
|
}
|
|
136
|
-
const vectorSet =
|
|
154
|
+
const vectorSet = produceVectorSet(inferred.semantic, tokens, options, reporter);
|
|
137
155
|
return { ok: true, mode: "A", vectorSet, code: null };
|
|
138
156
|
}
|
|
139
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Produce a mode-A `VectorSetV1` from the [1,384] trunk embedding (the
|
|
160
|
+
* `runtime.infer` result). VC2B-2 ML5-A: when real trained heads are loaded
|
|
161
|
+
* (`MEGACOMPACT_ML5_A` on + a `trainedHeadsPath` that loads), the multi-head
|
|
162
|
+
* output is projected through the real trained projection matrices via
|
|
163
|
+
* `projectHeadFromTrunk`. Otherwise (flag-off / absent / unloadable artifact)
|
|
164
|
+
* the deterministic LCG placeholder `encodeVectorSet` serves mode A —
|
|
165
|
+
* byte-identical to the VC2B predecessor. Non-fatal: an unloadable artifact
|
|
166
|
+
* degrades to the placeholder, never a throw.
|
|
167
|
+
*/
|
|
168
|
+
function produceVectorSet(
|
|
169
|
+
trunkEmbedding: Float32Array,
|
|
170
|
+
tokens: readonly number[],
|
|
171
|
+
options: RouterOptions,
|
|
172
|
+
reporter: EncoderHeadsReporter,
|
|
173
|
+
): VectorSetV1 {
|
|
174
|
+
const table: HeadProjectionTable | null =
|
|
175
|
+
ML5A_ENABLED() && options.trainedHeadsPath !== undefined
|
|
176
|
+
? loadHeadProjections(options.trainedHeadsPath)
|
|
177
|
+
: null;
|
|
178
|
+
if (table !== null) {
|
|
179
|
+
const heads: HeadVector[] = ENCODER_HEAD_ORDER.map((h) =>
|
|
180
|
+
projectHeadFromTrunk(h, trunkEmbedding, table),
|
|
181
|
+
);
|
|
182
|
+
reporter.headsEmitted({
|
|
183
|
+
heads: heads.length,
|
|
184
|
+
dims: heads.map((h) => h.dim).join("/"),
|
|
185
|
+
normalized: true,
|
|
186
|
+
tokens: tokens.length,
|
|
187
|
+
});
|
|
188
|
+
return { schema: "vector-set-v1", inputTokens: [...tokens], heads, normalized: true };
|
|
189
|
+
}
|
|
190
|
+
// ML5-A placeholder fallback (byte-identical predecessor).
|
|
191
|
+
return encodeVectorSet(tokens, { reporter, seed: options.seed });
|
|
192
|
+
}
|
|
193
|
+
|
|
140
194
|
/**
|
|
141
195
|
* Catch a (real or forced) A load failure (ok === false only) and select the
|
|
142
196
|
* B/C fallback that emits `vector_cortex_encoder_fallback_selected` from the
|
|
@@ -92,6 +92,9 @@ export interface ModelManifestV1 {
|
|
|
92
92
|
readonly heads: EncoderHeads;
|
|
93
93
|
readonly onnx: ManifestAssetFile;
|
|
94
94
|
readonly tokenizer: ManifestAssetFile;
|
|
95
|
+
/** ML5-A (VC2B-2): optional sibling `trained-heads.json` (`trained-heads-v1`) shipping
|
|
96
|
+
* REAL trained head weights; absent in the placeholder bundle, non-fatal when missing. */
|
|
97
|
+
readonly headWeights?: ManifestAssetFile;
|
|
95
98
|
readonly totalBytes: number;
|
|
96
99
|
readonly trainingManifestDigest: string;
|
|
97
100
|
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/livewire/livewire-live.ts — LIVEWIRE in-process live state.
|
|
3
|
+
*
|
|
4
|
+
* Holds the LIVE subsystem objects for ONE stateDir: the VC7A `CrystalStore`,
|
|
5
|
+
* the VC7C cache breaker, the per-miss-class tallies, the VC8B shadow metrics,
|
|
6
|
+
* and the VC7B economics computed bit. This is the object the RUNTIME accumulates
|
|
7
|
+
* into (`recordMiss`, `recordServeBlocked`, `recordShadowRun`, ...) and that the
|
|
8
|
+
* reader-only dashboard routes snapshot out of. Kept separate from the registry
|
|
9
|
+
* (which owns the per-stateDir `Map` + persistence) so neither file crosses the
|
|
10
|
+
* 300-line soft-as-hard gate.
|
|
11
|
+
*
|
|
12
|
+
* AGGREGATE-FIRST, COUNTS ONLY. `snapshotOf` projects the live objects down to
|
|
13
|
+
* the SECURITY_PRIVACY-safe `LivewireSnapshot` (counts + codes + triad mode) that
|
|
14
|
+
* gets persisted and rendered. No session id, digest, range, profile id, bytes,
|
|
15
|
+
* or prompt text can pass through ANY of these fields.
|
|
16
|
+
*
|
|
17
|
+
* PREVENT-PI-004: no network. PREVENT-011: no `any`. Non-fatal: every derive is
|
|
18
|
+
* a pure read.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { CrystalStore } from "../cache/store.js";
|
|
22
|
+
import { createCacheBreaker } from "../cache/breaker.js";
|
|
23
|
+
import type { MissClass } from "../cache/diagnostics-types.js";
|
|
24
|
+
import { validateProfileEconomics } from "../provider/economics.js";
|
|
25
|
+
import { BASE_PROVIDER_PROFILES } from "../provider/registry.js";
|
|
26
|
+
import type {
|
|
27
|
+
LivewireDiagnosticsAggregate,
|
|
28
|
+
LivewireEconomicsAggregate,
|
|
29
|
+
LivewirePolicyAggregate,
|
|
30
|
+
LivewireSnapshot,
|
|
31
|
+
} from "./livewire-types.js";
|
|
32
|
+
|
|
33
|
+
/** One VC7C miss classification tally (in-process, counts only). */
|
|
34
|
+
export interface LivewireDiagnosticsRecord {
|
|
35
|
+
readonly tallies: { [K in MissClass]: number };
|
|
36
|
+
serveBlocked: number;
|
|
37
|
+
/**
|
|
38
|
+
* Observable breaker state (CLOSED_A / OPEN_B / ... / MANUAL_HALT). Tracked as
|
|
39
|
+
* a field so it survives process restart: the runtime syncs it from the live
|
|
40
|
+
* breaker when persisting, and a reader process restores it from the snapshot
|
|
41
|
+
* (a fresh in-process breaker is always CLOSED_A, which would be misleading).
|
|
42
|
+
*/
|
|
43
|
+
breakerState: string;
|
|
44
|
+
lastFailure: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One VC7B economics record (computed bit + profile tallies + last failure). */
|
|
48
|
+
export interface LivewireEconomicsRecord {
|
|
49
|
+
computed: boolean;
|
|
50
|
+
/** Provider profiles that declare cache economics (static from BASE_PROFILES). */
|
|
51
|
+
profileCount: number;
|
|
52
|
+
/** Exclusions that carry a proving fixture id (static from BASE_PROFILES). */
|
|
53
|
+
provenExclusions: number;
|
|
54
|
+
/** Exclusions rejected for lacking a fixture id (static from BASE_PROFILES). */
|
|
55
|
+
unprovenExclusions: number;
|
|
56
|
+
lastFailure: string | null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Static VC7B economics profile tallies derived from the base provider registry.
|
|
61
|
+
* `profileCount` counts profiles that declare economics; `provenExclusions` /
|
|
62
|
+
* `unprovenExclusions` tally the exclusion sets validated against the fixture
|
|
63
|
+
* rule. This is PURE (no storage/clock/network) and identical regardless of the
|
|
64
|
+
* `MEGACOMPACT_VC7B` flag — only the emission/route seam is flag-gated.
|
|
65
|
+
*/
|
|
66
|
+
function baseEconomicsTallies(): {
|
|
67
|
+
profileCount: number;
|
|
68
|
+
provenExclusions: number;
|
|
69
|
+
unprovenExclusions: number;
|
|
70
|
+
} {
|
|
71
|
+
let profileCount = 0;
|
|
72
|
+
let provenExclusions = 0;
|
|
73
|
+
let unprovenExclusions = 0;
|
|
74
|
+
for (const bundle of BASE_PROVIDER_PROFILES) {
|
|
75
|
+
const econ = bundle.profile.economics;
|
|
76
|
+
if (econ === null) continue;
|
|
77
|
+
profileCount += 1;
|
|
78
|
+
const codes = validateProfileEconomics(bundle.profile, econ);
|
|
79
|
+
const failed = new Set(codes);
|
|
80
|
+
for (const _ex of bundle.profile.excludedJsonPointers) {
|
|
81
|
+
if (failed.has("ECON_EXCLUSION_UNPROVEN")) {
|
|
82
|
+
unprovenExclusions += 1;
|
|
83
|
+
} else {
|
|
84
|
+
provenExclusions += 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { profileCount, provenExclusions, unprovenExclusions };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** One VC8B shadow-policy record (metrics + active pressure version). */
|
|
92
|
+
export interface LivewireShadowRecord {
|
|
93
|
+
shadowDecisions: number;
|
|
94
|
+
clampedDecisions: number;
|
|
95
|
+
rejectedInputs: number;
|
|
96
|
+
liveMutations: number;
|
|
97
|
+
pressureVersion: 1 | 2;
|
|
98
|
+
lastFailure: string | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The live, in-process state for one stateDir. */
|
|
102
|
+
export interface LivewireLiveState {
|
|
103
|
+
readonly crystalStore: CrystalStore;
|
|
104
|
+
readonly breaker: ReturnType<typeof createCacheBreaker>;
|
|
105
|
+
readonly diagnostics: LivewireDiagnosticsRecord;
|
|
106
|
+
readonly economics: LivewireEconomicsRecord;
|
|
107
|
+
readonly shadow: LivewireShadowRecord;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A zeroed per-class tally map. */
|
|
111
|
+
function zeroTallies(): LivewireDiagnosticsRecord["tallies"] {
|
|
112
|
+
return {
|
|
113
|
+
profile: 0,
|
|
114
|
+
range: 0,
|
|
115
|
+
dependency: 0,
|
|
116
|
+
request: 0,
|
|
117
|
+
generation: 0,
|
|
118
|
+
unknown: 0,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Build a fresh (empty) live state with real subsystem objects. */
|
|
123
|
+
export function createLiveState(): LivewireLiveState {
|
|
124
|
+
const econTallies = baseEconomicsTallies();
|
|
125
|
+
return {
|
|
126
|
+
crystalStore: new CrystalStore(),
|
|
127
|
+
breaker: createCacheBreaker(),
|
|
128
|
+
diagnostics: {
|
|
129
|
+
tallies: zeroTallies(),
|
|
130
|
+
serveBlocked: 0,
|
|
131
|
+
breakerState: "CLOSED_A",
|
|
132
|
+
lastFailure: null,
|
|
133
|
+
},
|
|
134
|
+
economics: {
|
|
135
|
+
computed: false,
|
|
136
|
+
profileCount: econTallies.profileCount,
|
|
137
|
+
provenExclusions: econTallies.provenExclusions,
|
|
138
|
+
unprovenExclusions: econTallies.unprovenExclusions,
|
|
139
|
+
lastFailure: null,
|
|
140
|
+
},
|
|
141
|
+
shadow: {
|
|
142
|
+
shadowDecisions: 0,
|
|
143
|
+
clampedDecisions: 0,
|
|
144
|
+
rejectedInputs: 0,
|
|
145
|
+
liveMutations: 0,
|
|
146
|
+
pressureVersion: 1,
|
|
147
|
+
lastFailure: null,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Project the live state down to the persisted, reader-only aggregate. */
|
|
153
|
+
export function snapshotOf(state: LivewireLiveState): LivewireSnapshot {
|
|
154
|
+
const crystals = state.crystalStore.stats();
|
|
155
|
+
const diag: LivewireDiagnosticsAggregate = {
|
|
156
|
+
profileMisses: state.diagnostics.tallies.profile,
|
|
157
|
+
rangeMisses: state.diagnostics.tallies.range,
|
|
158
|
+
dependencyMisses: state.diagnostics.tallies.dependency,
|
|
159
|
+
requestMisses: state.diagnostics.tallies.request,
|
|
160
|
+
generationMisses: state.diagnostics.tallies.generation,
|
|
161
|
+
unknownMisses: state.diagnostics.tallies.unknown,
|
|
162
|
+
serveBlocked: state.diagnostics.serveBlocked,
|
|
163
|
+
breakerState: state.diagnostics.breakerState,
|
|
164
|
+
lastFailure: state.diagnostics.lastFailure,
|
|
165
|
+
};
|
|
166
|
+
const econ: LivewireEconomicsAggregate = {
|
|
167
|
+
profileCount: state.economics.profileCount,
|
|
168
|
+
provenExclusions: state.economics.provenExclusions,
|
|
169
|
+
unprovenExclusions: state.economics.unprovenExclusions,
|
|
170
|
+
computed: state.economics.computed,
|
|
171
|
+
lastFailure: state.economics.lastFailure,
|
|
172
|
+
};
|
|
173
|
+
const policy: LivewirePolicyAggregate = {
|
|
174
|
+
shadowDecisions: state.shadow.shadowDecisions,
|
|
175
|
+
clampedDecisions: state.shadow.clampedDecisions,
|
|
176
|
+
rejectedInputs: state.shadow.rejectedInputs,
|
|
177
|
+
liveMutations: state.shadow.liveMutations,
|
|
178
|
+
pressureVersion: state.shadow.pressureVersion,
|
|
179
|
+
lastFailure: state.shadow.lastFailure,
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
schema: "vector-cortex-livewire-v1",
|
|
183
|
+
crystals: {
|
|
184
|
+
mode: crystals.mode,
|
|
185
|
+
crystalCount: crystals.crystalCount,
|
|
186
|
+
totalBytes: crystals.totalBytes,
|
|
187
|
+
hits: crystals.hits,
|
|
188
|
+
misses: crystals.misses,
|
|
189
|
+
hitBytes: crystals.hitBytes,
|
|
190
|
+
writes: crystals.writes,
|
|
191
|
+
duplicateWrites: crystals.duplicateWrites,
|
|
192
|
+
collisions: crystals.collisions,
|
|
193
|
+
},
|
|
194
|
+
diagnostics: diag,
|
|
195
|
+
economics: econ,
|
|
196
|
+
policy,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Seed a fresh live state's CUMULATIVE counters from a persisted snapshot. */
|
|
201
|
+
export function rehydrateLive(state: LivewireLiveState, snap: LivewireSnapshot): void {
|
|
202
|
+
state.crystalStore.rehydrate(snap.crystals);
|
|
203
|
+
state.diagnostics.tallies.profile = snap.diagnostics.profileMisses;
|
|
204
|
+
state.diagnostics.tallies.range = snap.diagnostics.rangeMisses;
|
|
205
|
+
state.diagnostics.tallies.dependency = snap.diagnostics.dependencyMisses;
|
|
206
|
+
state.diagnostics.tallies.request = snap.diagnostics.requestMisses;
|
|
207
|
+
state.diagnostics.tallies.generation = snap.diagnostics.generationMisses;
|
|
208
|
+
state.diagnostics.tallies.unknown = snap.diagnostics.unknownMisses;
|
|
209
|
+
state.diagnostics.serveBlocked = snap.diagnostics.serveBlocked;
|
|
210
|
+
state.diagnostics.breakerState = snap.diagnostics.breakerState;
|
|
211
|
+
state.diagnostics.lastFailure = snap.diagnostics.lastFailure;
|
|
212
|
+
state.economics.computed = snap.economics.computed;
|
|
213
|
+
state.economics.profileCount = snap.economics.profileCount;
|
|
214
|
+
state.economics.provenExclusions = snap.economics.provenExclusions;
|
|
215
|
+
state.economics.unprovenExclusions = snap.economics.unprovenExclusions;
|
|
216
|
+
state.economics.lastFailure = snap.economics.lastFailure;
|
|
217
|
+
state.shadow.shadowDecisions = snap.policy.shadowDecisions;
|
|
218
|
+
state.shadow.clampedDecisions = snap.policy.clampedDecisions;
|
|
219
|
+
state.shadow.rejectedInputs = snap.policy.rejectedInputs;
|
|
220
|
+
state.shadow.liveMutations = snap.policy.liveMutations;
|
|
221
|
+
state.shadow.pressureVersion = snap.policy.pressureVersion;
|
|
222
|
+
state.shadow.lastFailure = snap.policy.lastFailure;
|
|
223
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/livewire/livewire-registry.ts — per-stateDir LIWIRE registry.
|
|
3
|
+
*
|
|
4
|
+
* The single seam the runtime and the reader-only dashboard routes BOTH access.
|
|
5
|
+
* It owns a `Map<stateDir, LivewireLiveState>` (one live subsystem cluster per
|
|
6
|
+
* repo) and lazily opens a state on first access, rehydrating its cumulative
|
|
7
|
+
* counters from the persisted aggregate snapshot (`livewire-snapshot.ts`) so a
|
|
8
|
+
* freshly-spawned process — e.g. the dashboard server — reports the same counts
|
|
9
|
+
* the runtime has already accumulated.
|
|
10
|
+
*
|
|
11
|
+
* The runtime WRITES through this registry (see `livewire-runtime.ts`): each
|
|
12
|
+
* mutation persists a reduced, count-only snapshot. The routes READ through
|
|
13
|
+
* `livewireOf(stateDir)` and call the pure `snapshotOf`. There is no other path.
|
|
14
|
+
*
|
|
15
|
+
* BEST-EFFORT + NON-FATAL. `openLivewire` never throws: persistence failures are
|
|
16
|
+
* swallowed by the snapshot layer and state is always returned. PREVENT-PI-004
|
|
17
|
+
* (local in-process Map + filesystem only), PREVENT-011 (no `any`).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { LivewireSnapshot } from "./livewire-types.js";
|
|
21
|
+
import {
|
|
22
|
+
createLiveState,
|
|
23
|
+
rehydrateLive,
|
|
24
|
+
snapshotOf,
|
|
25
|
+
type LivewireLiveState,
|
|
26
|
+
} from "./livewire-live.js";
|
|
27
|
+
import {
|
|
28
|
+
loadLivewireSnapshot,
|
|
29
|
+
saveLivewireSnapshot,
|
|
30
|
+
} from "./livewire-snapshot.js";
|
|
31
|
+
|
|
32
|
+
/** The per-stateDir registry (process-local; a fresh process starts empty). */
|
|
33
|
+
const REGISTRY = new Map<string, LivewireLiveState>();
|
|
34
|
+
|
|
35
|
+
/** Optional structured logger for best-effort failure events. */
|
|
36
|
+
export type LivewireLogger = (line: unknown) => void;
|
|
37
|
+
|
|
38
|
+
let activeLogger: LivewireLogger | undefined;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Bind the structured logger the snapshot layer uses for its non-fatal write
|
|
42
|
+
* failures. The runtime calls this once at startup with its JSON logger.
|
|
43
|
+
*/
|
|
44
|
+
export function setLivewireLogger(logger: LivewireLogger | undefined): void {
|
|
45
|
+
activeLogger = logger;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Open (or return the cached) live state for a stateDir. Lazy: on first access
|
|
50
|
+
* it rehydrates from the persisted aggregate so a separate dashboard process
|
|
51
|
+
* reflects prior runtime work. Never throws.
|
|
52
|
+
*/
|
|
53
|
+
export function livewireOf(stateDir: string): LivewireLiveState {
|
|
54
|
+
const cached = REGISTRY.get(stateDir);
|
|
55
|
+
if (cached !== undefined) return cached;
|
|
56
|
+
const state = createLiveState();
|
|
57
|
+
const snap = loadLivewireSnapshot(stateDir);
|
|
58
|
+
if (snap !== null) rehydrateLive(state, snap);
|
|
59
|
+
REGISTRY.set(stateDir, state);
|
|
60
|
+
return state;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Persist a state's reduced aggregate (counts + codes only). Best-effort and
|
|
65
|
+
* non-fatal. Called by the runtime after every mutation so the snapshot stays
|
|
66
|
+
* fresh for any reader process.
|
|
67
|
+
*/
|
|
68
|
+
export function persistLivewire(state: LivewireLiveState, stateDir: string): void {
|
|
69
|
+
saveLivewireSnapshot(stateDir, snapshotOf(state), activeLogger);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Persist the live state for a stateDir (convenience over open + persist). */
|
|
73
|
+
export function flushLivewire(stateDir: string): void {
|
|
74
|
+
const state = REGISTRY.get(stateDir);
|
|
75
|
+
if (state === undefined) return;
|
|
76
|
+
persistLivewire(state, stateDir);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Build the reader aggregate for one stateDir WITHOUT persisting — the reader
|
|
81
|
+
* seam the dashboard routes call. Reads the live state (rehydrated from disk on
|
|
82
|
+
* first access) and projects it to the count-only snapshot.
|
|
83
|
+
*/
|
|
84
|
+
export function readLivewireSnapshot(stateDir: string): LivewireSnapshot {
|
|
85
|
+
return snapshotOf(livewireOf(stateDir));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** For tests: drop the registry so a fresh stateDir is fully rehydrated. */
|
|
89
|
+
export function _resetLivewireRegistryForTests(): void {
|
|
90
|
+
REGISTRY.clear();
|
|
91
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/livewire/livewire-runtime.ts — LIVEWIRE runtime accumulation seam.
|
|
3
|
+
*
|
|
4
|
+
* The WRITE side of LIVEWIRE: the runtime calls these methods wherever the
|
|
5
|
+
* corresponding subsystem path actually runs, and each call accumulates into the
|
|
6
|
+
* per-stateDir live state and persists a reduced count-only snapshot (best-effort,
|
|
7
|
+
* non-fatal). This is the seam that turns the pure VC7A/VC7B/VC7C/VC8B arithmetic
|
|
8
|
+
* into LIVE dashboard state.
|
|
9
|
+
*
|
|
10
|
+
* FLAG SEMANTICS (mirrors every other VC writer): these methods run REGARDLESS of
|
|
11
|
+
* the corresponding `MEGACOMPACT_VC*` flag — the arithmetic is never skipped, and
|
|
12
|
+
* a miss is still classified / a write still stored exactly as before. Only the
|
|
13
|
+
* dashboard REPORT seam (`routes-vector-cortex-*.ts`) is flag-gated: with the
|
|
14
|
+
* flag off a route returns the legacy `deferredReason` (byte-identical to the
|
|
15
|
+
* predecessor). The reporter emit seams in `cache/*-emit.ts` / `controller/
|
|
16
|
+
* policy-emit.ts` remain the event-announcement gate.
|
|
17
|
+
*
|
|
18
|
+
* PREVENT-PI-004: no network. PREVENT-011: no `any`. Every write is best-effort.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { MissClass, MissObservation } from "../cache/diagnostics-types.js";
|
|
22
|
+
import { classifyMiss } from "../cache/diagnostics.js";
|
|
23
|
+
import { shouldBlockServe } from "../cache/breaker.js";
|
|
24
|
+
import type { CrystalV1, CrystalWriteResult } from "../cache/types.js";
|
|
25
|
+
import type { ShadowResult } from "../controller/types.js";
|
|
26
|
+
import { livewireOf, persistLivewire } from "./livewire-registry.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Record a VC7A crystal WRITE attempt (write-once + collision arithmetic). The
|
|
30
|
+
* caller passes the fully-formed `CrystalV1`; the store returns the write result
|
|
31
|
+
* (first-write / idempotent / collision). The dashboard's `stats()` reflects it.
|
|
32
|
+
*
|
|
33
|
+
* @returns the store's write verdict, forwarded so the runtime can act on a
|
|
34
|
+
* collision without re-deriving it.
|
|
35
|
+
*/
|
|
36
|
+
export function recordCrystalWrite(stateDir: string, crystal: CrystalV1): CrystalWriteResult {
|
|
37
|
+
const state = livewireOf(stateDir);
|
|
38
|
+
const result = state.crystalStore.write(crystal);
|
|
39
|
+
persistLivewire(state, stateDir);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Record a VC7A crystal READ attempt. Returns the stored crystal (or undefined
|
|
45
|
+
* on a miss / mode C), mirroring `CrystalStore.read` so the cache-serve path can
|
|
46
|
+
* use this seam entirely.
|
|
47
|
+
*/
|
|
48
|
+
export function readCrystal(stateDir: string, keyDigest: string): CrystalV1 | undefined {
|
|
49
|
+
const state = livewireOf(stateDir);
|
|
50
|
+
const found = state.crystalStore.read(keyDigest);
|
|
51
|
+
persistLivewire(state, stateDir);
|
|
52
|
+
return found;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Record a VC7C miss observation: classify it into its exclusive class and tally
|
|
57
|
+
* it. When the class demands the cache serve be demoted BEFORE answering, the
|
|
58
|
+
* `serveBlocked` counter is also incremented. The live breaker state itself is
|
|
59
|
+
* driven by the real cache-serve path (through `breaker.execute`), not here; this
|
|
60
|
+
* seam only observes and tallies.
|
|
61
|
+
*
|
|
62
|
+
* @returns the exclusive class the observation was tallied under.
|
|
63
|
+
*/
|
|
64
|
+
export function observeCacheMiss(stateDir: string, observation: MissObservation): MissClass {
|
|
65
|
+
const state = livewireOf(stateDir);
|
|
66
|
+
const missClass = classifyMiss(observation).missClass;
|
|
67
|
+
state.diagnostics.tallies[missClass] += 1;
|
|
68
|
+
if (shouldBlockServe(missClass)) state.diagnostics.serveBlocked += 1;
|
|
69
|
+
persistLivewire(state, stateDir);
|
|
70
|
+
return missClass;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Record a VC7C cache serve that the breaker demoted BEFORE answering. Talls the
|
|
75
|
+
* `serveBlocked` counter the diagnostics card surfaces.
|
|
76
|
+
*/
|
|
77
|
+
export function recordServeBlocked(stateDir: string): void {
|
|
78
|
+
const state = livewireOf(stateDir);
|
|
79
|
+
state.diagnostics.serveBlocked += 1;
|
|
80
|
+
persistLivewire(state, stateDir);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Record a VC8B shadow evaluation run: accumulate its decision metrics so the
|
|
85
|
+
* policy card reports how many shadow decisions were evaluated / clamped /
|
|
86
|
+
* rejected and how many live mutations the shadow proved (structurally 0).
|
|
87
|
+
*/
|
|
88
|
+
export function recordShadowRun(stateDir: string, result: ShadowResult): void {
|
|
89
|
+
const state = livewireOf(stateDir);
|
|
90
|
+
state.shadow.shadowDecisions += result.metrics.evaluated;
|
|
91
|
+
state.shadow.clampedDecisions += result.metrics.clamped;
|
|
92
|
+
state.shadow.rejectedInputs += result.metrics.rejected;
|
|
93
|
+
state.shadow.liveMutations += result.metrics.liveMutations;
|
|
94
|
+
persistLivewire(state, stateDir);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set the active M7 pressure version (1 = legacy, 2 = migrated). The runtime
|
|
99
|
+
* calls this after a successful `migratePressureV2` so the policy card reflects
|
|
100
|
+
* the live migration state rather than a hardcoded 1.
|
|
101
|
+
*/
|
|
102
|
+
export function setPressureVersion(stateDir: string, version: 1 | 2): void {
|
|
103
|
+
const state = livewireOf(stateDir);
|
|
104
|
+
state.shadow.pressureVersion = version;
|
|
105
|
+
persistLivewire(state, stateDir);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Mark that VC7B cache economics have actually been computed at runtime (the
|
|
110
|
+
* `computed` bit drives the economics card's `hasData`). The runtime calls this
|
|
111
|
+
* after the first real `computeEconomics` over observed usage.
|
|
112
|
+
*/
|
|
113
|
+
export function markEconomicsComputed(stateDir: string): void {
|
|
114
|
+
const state = livewireOf(stateDir);
|
|
115
|
+
state.economics.computed = true;
|
|
116
|
+
persistLivewire(state, stateDir);
|
|
117
|
+
}
|