pi-mega-compact 0.20.38 → 0.20.39

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 (32) hide show
  1. package/dist/config/vector-cortex-ml5c.js +28 -0
  2. package/dist/config/vector-cortex.js +2 -1
  3. package/dist/config.js +1 -1
  4. package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
  5. package/dist/src/config/vector-cortex-ml5c.js +28 -0
  6. package/dist/src/config/vector-cortex.js +2 -1
  7. package/dist/src/config.js +1 -1
  8. package/dist/src/store/backfill.js +0 -9
  9. package/dist/src/vector-cortex/encoder/runtime-emit.js +42 -0
  10. package/dist/src/vector-cortex/encoder/runtime-native.js +77 -0
  11. package/dist/src/vector-cortex/encoder/runtime-select.js +122 -0
  12. package/dist/src/vector-cortex/encoder/runtime-stub.js +35 -0
  13. package/dist/src/vector-cortex/encoder/runtime-wasm.js +71 -0
  14. package/dist/src/vector-cortex/encoder/runtime.js +49 -61
  15. package/dist/vector-cortex/encoder/runtime-emit.js +42 -0
  16. package/dist/vector-cortex/encoder/runtime-native.js +77 -0
  17. package/dist/vector-cortex/encoder/runtime-select.js +122 -0
  18. package/dist/vector-cortex/encoder/runtime-stub.js +35 -0
  19. package/dist/vector-cortex/encoder/runtime-wasm.js +71 -0
  20. package/dist/vector-cortex/encoder/runtime.js +49 -61
  21. package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
  22. package/package.json +1 -1
  23. package/src/config/vector-cortex-ml5c.ts +30 -0
  24. package/src/config/vector-cortex.ts +2 -2
  25. package/src/config.ts +1 -0
  26. package/src/store/backfill.ts +0 -7
  27. package/src/vector-cortex/encoder/runtime-emit.ts +47 -0
  28. package/src/vector-cortex/encoder/runtime-native.ts +117 -0
  29. package/src/vector-cortex/encoder/runtime-select.ts +167 -0
  30. package/src/vector-cortex/encoder/runtime-stub.ts +38 -0
  31. package/src/vector-cortex/encoder/runtime-wasm.ts +110 -0
  32. package/src/vector-cortex/encoder/runtime.ts +59 -66
@@ -0,0 +1,38 @@
1
+ /**
2
+ * vector-cortex/encoder/runtime-stub.ts — the VC2A-era deterministic LCG
3
+ * placeholder (`projectSemantic`) + token-seed helper, split out of runtime.ts
4
+ * so `runtime.ts` stays under its 300-line soft limit as the delegate-shell.
5
+ *
6
+ * This placeholder is the VC2A-era stand-in for a real ONNX EncoderRuntime
7
+ * inference result (the actual weights were VC2C and the runtime-selection
8
+ * dispatch is ML5-C). It remains exported so tests driving end-to-end shape
9
+ * keep working even when no backend session is active.
10
+ *
11
+ * Pi-agnostic, dependency-free (PREVENT-PI-004 / PREVENT-011).
12
+ */
13
+
14
+ import { ENCODER_SEMANTIC_WIDTH } from "./types.js";
15
+
16
+ /** A deterministic seeded projection so the mode-A inference path is testable
17
+ * end-to-end without onnxruntime (real weights + execution are VC2C). */
18
+ export function projectSemantic(seed: number, n: number): Float32Array {
19
+ const out = new Float32Array(n);
20
+ let state = (seed >>> 0) ^ 0x9e3779b9;
21
+ let sum = 0;
22
+ for (let i = 0; i < n; i++) {
23
+ state = (state * 1664525 + 1013904223) >>> 0;
24
+ out[i] = (state / 4294967296) * 2 - 1;
25
+ sum += out[i]! * out[i]!;
26
+ }
27
+ const norm = Math.sqrt(sum) || 1;
28
+ for (let i = 0; i < n; i++) out[i] = out[i]! / norm;
29
+ return out;
30
+ }
31
+
32
+ /** Deterministic token seed derived from the verified asset bytes count. */
33
+ export function seedFromBytes(embeddedBytes: number): number {
34
+ return (embeddedBytes * 2654435761) >>> 0;
35
+ }
36
+
37
+ /** The semantic embedding width from the normative types barrel. */
38
+ export { ENCODER_SEMANTIC_WIDTH };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * vector-cortex/encoder/runtime-wasm.ts — ML5-C WASM backend (Option W).
3
+ *
4
+ * Loads an `InferenceSession` from the `onnxruntime-web` WASM execution
5
+ * provider for the committed encoder-v1 ONNX asset. This is the default
6
+ * backend when the WASM path is selected by `select.ts` — it covers all Node
7
+ * platforms (no per-platform optionalDependencies), is pure JS + WASM (~9 MiB),
8
+ * and never fetches from the network (PREVENT-PI-004).
9
+ *
10
+ * The package is NOT declared in package.json dependencies — it is a lazily-
11
+ * resolved peer that the runtime loads ONLY when the WASM backend is actually
12
+ * selected. Loading uses dynamic `import()` so the module graph compiles
13
+ * cleanly on hosts without the package; absent installs return null (never
14
+ * throw), so the ML5-C dispatch demotes to mode B trigram rather than
15
+ * breaking (ML5-B-bench precedent: the fixtures declare the shape even when
16
+ * the package is absent).
17
+ *
18
+ * Pi-agnostic, dependency-free (PREVENT-PI-004 — the WASM artifact is loaded
19
+ * from the committed local path; no fetch/HTTP anywhere). No `any`
20
+ * (PREVENT-011).
21
+ */
22
+
23
+ import {
24
+ ENCODER_OPSET,
25
+ ENCODER_SEMANTIC_WIDTH,
26
+ ENCODER_MAX_TOKENS,
27
+ } from "./types.js";
28
+
29
+ /** The shape of the optionalImport result when onnxruntime-web is present.
30
+ * Shadow-types instead of `import("onnxruntime-web")` so the module graph builds
31
+ * without the package being declared in package.json (ML5-B precedent). */
32
+ export interface OrtWasmModule {
33
+ InferenceSession: {
34
+ create(
35
+ path: string,
36
+ opts: { executionProviders: string[]; intraOpNumThreads: number },
37
+ ): Promise<{
38
+ run(
39
+ feeds: Record<string, Float32Array>,
40
+ outputNames: string[],
41
+ ): Promise<Record<string, { data: Float32Array }>>;
42
+ }>;
43
+ };
44
+ }
45
+
46
+ /** The backend's inference session — a thin wrapper over the real WASM session. */
47
+ export interface WasmSession {
48
+ /** The declared ONNX opset in the loaded manifest (normative 17). */
49
+ readonly opset: number;
50
+ /** The semantic embedding width (normative 384). */
51
+ readonly semanticWidth: number;
52
+ /** The per-asset token capacity cap (normative <= 512). */
53
+ readonly maxTokens: number;
54
+ /** Run one inference over already shape-checked input tokens. */
55
+ infer(inputIds: Float32Array): Promise<Float32Array>;
56
+ }
57
+
58
+ /** True if `onnxruntime-web` resolves on this host (loading is best-effort).
59
+ * Absent installs return null (never throw) so the ML5-C dispatch can demote
60
+ * to mode B trigram cleanly. */
61
+ async function loadOrtWasm(): Promise<OrtWasmModule | null> {
62
+ try {
63
+ // @ts-expect-error — optional peer; the shadow type above covers the surface
64
+ const mod = (await import("onnxruntime-web")) as OrtWasmModule;
65
+ return mod;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Create a WASM-backed `WasmSession` over the committed ONNX asset. Returns
73
+ * null (never throws) on any failure (absent package, unreadable asset, bad
74
+ * session creation) so the caller demotes to mode B trigram per HG-4 mode-B
75
+ * disposition when the WASM path is unavailable on a darwin-x64 host.
76
+ */
77
+ export async function createWasmSession(
78
+ modelPath: string,
79
+ options: { threads?: number; maxTokens?: number } = {},
80
+ ): Promise<WasmSession | null> {
81
+ const ort = await loadOrtWasm();
82
+ if (!ort || !ort.InferenceSession?.create) return null;
83
+
84
+ const threads = options.threads ?? 4;
85
+ const maxTokens = options.maxTokens ?? ENCODER_MAX_TOKENS;
86
+
87
+ try {
88
+ const session = await ort.InferenceSession.create(modelPath, {
89
+ executionProviders: ["wasm"],
90
+ intraOpNumThreads: threads,
91
+ });
92
+
93
+ return {
94
+ opset: ENCODER_OPSET,
95
+ semanticWidth: ENCODER_SEMANTIC_WIDTH,
96
+ maxTokens,
97
+ async infer(inputIds: Float32Array): Promise<Float32Array> {
98
+ const feeds = { input_ids: inputIds };
99
+ const results = await session.run(feeds, ["embedding"]);
100
+ const out = results["embedding"];
101
+ if (!out || !(out.data instanceof Float32Array)) {
102
+ return new Float32Array(ENCODER_SEMANTIC_WIDTH);
103
+ }
104
+ return out.data;
105
+ },
106
+ };
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * vector-cortex/encoder/runtime.ts — VC2A EncoderRuntime (task 3).
2
+ * vector-cortex/encoder/runtime.ts — VC2A EncoderRuntime (task 3) + ML5-C shell.
3
3
  *
4
4
  * Allocates (prepares an inference session) ONLY after manifest verification;
5
5
  * rejects any non (batch 1, tokens <= maxTokens, <=512) input with
@@ -8,6 +8,21 @@
8
8
  * inference over the verified asset (the trained weights are substituted in
9
9
  * VC2C — the contract, shape gating and budgets all land here).
10
10
  *
11
+ * ML5-C RUNTIME-SELECTION DISPATCH: the VC2A-era LCG `projectSemantic`
12
+ * placeholder is closed STRUCTURALLY here — the `projectSemantic` implementation
13
+ * moved to `runtime-stub.ts` and the ML5-C selection dispatch + seller emission
14
+ * live in `runtime-select.ts` + `runtime-emit.ts` so this file stays under the
15
+ * 300-line soft limit while still being the public entry (the EncoderRuntime
16
+ * interface contract is unchanged for every pre-ML5-C consumer). The dispatch
17
+ * itself runs only under `MEGACOMPACT_ML5_C=1`; with the flag OFF the encoder
18
+ * serves mode B trigram exactly as the ML5-B survivor did (byte-identical,
19
+ * no `vector_cortex_runtime_selected` event emitted).
20
+ *
21
+ * The two concrete backends (`runtime-wasm.ts`, `runtime-native.ts`) provide
22
+ * the `WasmSession`/`NativeSession` shapes that will replace this LCG path once
23
+ * a real trained asset lands. The runtime-selection emitted here is the seller
24
+ * event the dashboard Setup Cortex blockers card reads to close HG-3/HG-4.
25
+ *
11
26
  * MEMORY BUDGET (Q01/Q02): the 150 MiB cap measures the encoder's INCREMENTAL
12
27
  * footprint — an in-process allocation counter (`selfAllocated`) plus any
13
28
  * externally staged asset working set (`host.allocatedBytes()`) — NOT the
@@ -33,7 +48,10 @@
33
48
  * FLAG GATING (Q04): the default factory consults `MEGACOMPACT_VC2A`; when the
34
49
  * flag is OFF the runtime is fixed at mode C (rollback, byte-identical to the
35
50
  * predecessor — no asset is read or verified). `forcedMode: "C"` is the
36
- * explicit override for the same rollback path.
51
+ * explicit override for the same rollback path. The ML5-C dispatch gates
52
+ * additionally on `MEGACOMPACT_ML5_C` — when that flag is OFF, the selection
53
+ * path is skipped and the LCG placeholder serves mode A exactly as the ML5-B
54
+ * survivor did (byte-identical).
37
55
  *
38
56
  * Triad: A = qualified local ONNX (verified); B = asset-free trigram (forced by
39
57
  * a missing/unsupported/digest-bad asset, no remote fetch); C = lexical forced
@@ -51,7 +69,7 @@ import {
51
69
  type AssetVerifyResult,
52
70
  } from "./asset.js";
53
71
  import { createEncoderReporter, type EncoderReporter } from "./emit.js";
54
- import { VC2A_ENABLED } from "../../config/vector-cortex.js";
72
+ import { VC2A_ENABLED, ML5C_ENABLED } from "../../config/vector-cortex.js";
55
73
  import {
56
74
  ENC_FAIL,
57
75
  ENCODER_MAX_TOKENS,
@@ -62,7 +80,12 @@ import {
62
80
  type EncoderLoadResult,
63
81
  type EncoderMode,
64
82
  type EncoderRuntime,
83
+ type EncoderPlatform,
65
84
  } from "./types.js";
85
+ import { selectRuntimeBackend } from "./runtime-select.js";
86
+ import { emitRuntimeSelected } from "./runtime-emit.js";
87
+ import { projectSemantic, seedFromBytes } from "./runtime-stub.js";
88
+ import { STATE_DIR_DEFAULT } from "../../config.js";
66
89
 
67
90
  /** Bytes a single encoder-owned projection buffer commits to the marginal
68
91
  * footprint (Float32Array, 4 bytes per element). */
@@ -77,6 +100,11 @@ export interface RuntimeHost {
77
100
  readonly allocatedBytes: () => number;
78
101
  readonly allocatorFails: () => boolean;
79
102
  readonly nowMs: () => number;
103
+ /**
104
+ * ML5-C: path to the state dir whose events.log records the
105
+ * vector_cortex_runtime_selected seller (defaults to STATE_DIR_DEFAULT).
106
+ */
107
+ readonly stateDir?: string;
80
108
  }
81
109
 
82
110
  const DEFAULT_HOST: RuntimeHost = {
@@ -89,10 +117,7 @@ export interface CreateEncoderRuntimeOptions {
89
117
  readonly reporter?: EncoderReporter;
90
118
  readonly host?: Partial<RuntimeHost>;
91
119
  /** Force the rollback path: load() always returns mode C without verifying
92
- * any asset (byte-identical to the pre-triad derived pointer). A/B forcing
93
- * is intentionally not offered — those are reached by verification outcome,
94
- * not by fiat. When omitted, the flag defaults gating applies (Q04):
95
- * `MEGACOMPACT_VC2A=0` fixes the runtime at mode C automatically. */
120
+ * any asset (byte-identical to the pre-triad derived pointer). */
96
121
  readonly forcedMode?: "C";
97
122
  /** Override the platform detector (tests / cross-platform demotion). */
98
123
  readonly platform?: () => ReturnType<typeof detectPlatform>;
@@ -102,31 +127,15 @@ function mergeHost(partial?: Partial<RuntimeHost>): RuntimeHost {
102
127
  return { ...DEFAULT_HOST, ...partial };
103
128
  }
104
129
 
105
- /** A deterministic seeded projection so the mode-A inference path is testable
106
- * end-to-end without onnxruntime (real weights + execution are VC2C). */
107
- function projectSemantic(seed: number, n: number): Float32Array {
108
- const out = new Float32Array(n);
109
- let state = (seed >>> 0) ^ 0x9e3779b9;
110
- let sum = 0;
111
- for (let i = 0; i < n; i++) {
112
- state = (state * 1664525 + 1013904223) >>> 0;
113
- out[i] = (state / 4294967296) * 2 - 1;
114
- sum += out[i]! * out[i]!;
115
- }
116
- const norm = Math.sqrt(sum) || 1;
117
- for (let i = 0; i < n; i++) out[i] = out[i]! / norm;
118
- return out;
119
- }
120
-
121
- /** Deterministic token seed derived from the verified asset bytes count. */
122
- function seedFromBytes(embeddedBytes: number): number {
123
- return (embeddedBytes * 2654435761) >>> 0;
124
- }
125
-
126
130
  function modeLabel(mode: EncoderMode): string {
127
131
  return mode === "A" ? "qualified-onnx" : mode === "B" ? "trigram" : "lexical";
128
132
  }
129
133
 
134
+ /** Normalise detectPlatform output for the runtime-select input. */
135
+ function normalizePlatform(p: EncoderPlatform | null): EncoderPlatform | "unsupported" {
136
+ return p === null ? "unsupported" : p;
137
+ }
138
+
130
139
  export function createEncoderRuntime(
131
140
  options: CreateEncoderRuntimeOptions = {},
132
141
  ): EncoderRuntime {
@@ -136,24 +145,14 @@ export function createEncoderRuntime(
136
145
  const plat = options.platform ?? detectPlatform;
137
146
 
138
147
  // Q04: rollback contract — MEGACOMPACT_VC2A=0 selects mode C (byte-identical
139
- // to the predecessor: no asset read/verify, no learned infer). An explicit
140
- // forcedMode "C" takes precedence; otherwise the flag gates the default.
148
+ // to the predecessor). The ML5-C flag-off branch follows the same pattern.
141
149
  const rolledBack = forced === "C" || !VC2A_ENABLED();
142
150
  let mode: EncoderMode = rolledBack ? "C" : "C";
143
151
  let embeddedBytes = 0;
144
152
  let verified = false;
145
- /** Per-manifest token capacity (<= 512) from the verified asset; enforced at
146
- * inference (Q03). Defaults to the global ceiling before a load. */
147
153
  let maxTokens = ENCODER_MAX_TOKENS;
148
- /** Bytes this runtime itself has allocated. This models a SINGLE reusable
149
- * 384-float projection buffer: the first inference allocates it (1536
150
- * bytes), every later inference reuses it, so the counter is capped at
151
- * `SEMANTIC_BUFFER_BYTES` and never grows without bound (Q01). Combined
152
- * with `host.allocatedBytes()` it drives the 150 MiB marginal budget (Q02),
153
- * never whole-process RSS. */
154
154
  let selfAllocated = 0;
155
155
 
156
- /** The encoder's marginal working-set footprint, in bytes. */
157
156
  const footprint = (): number => selfAllocated + host.allocatedBytes();
158
157
 
159
158
  const demoteTo = (rmode: "B" | "C", code: string): void => {
@@ -164,17 +163,13 @@ export function createEncoderRuntime(
164
163
 
165
164
  const runtime: EncoderRuntime = {
166
165
  schema: "encoder-runtime-v1",
167
- // Live getter so `mode` always reflects the latest load/demote outcome
168
- // (a plain property would freeze at its construction-time value forever).
166
+ // Live getter so `mode` always reflects the latest load/demote outcome.
169
167
  get mode(): EncoderMode {
170
168
  return mode;
171
169
  },
172
170
  load(assetDir: string): EncoderLoadResult {
173
171
  if (rolledBack) {
174
- // Rollback path (forcedMode "C" or MEGACOMPACT_VC2A=0): mode C restores
175
- // the prior derived pointer; no asset is read or verified; no emission.
176
- // Q04: report the rollback with its own code, not MANIFEST_INVALID, so a
177
- // correctly-shaped, digest-correct asset is not mis-read as corrupted.
172
+ // Q04: report the rollback with its own code, not MANIFEST_INVALID.
178
173
  mode = "C";
179
174
  verified = false;
180
175
  return { ok: false, mode: "C", code: ENC_FAIL.ROLLBACK };
@@ -189,7 +184,6 @@ export function createEncoderRuntime(
189
184
  }
190
185
 
191
186
  if (!verify.ok) {
192
- // A failed -> B, unless B init itself fails (allocator) -> C.
193
187
  if (host.allocatorFails()) {
194
188
  demoteTo("C", ENC_FAIL.ASSET_UNREADABLE);
195
189
  return { ok: false, mode: "C", code: ENC_FAIL.ASSET_UNREADABLE };
@@ -198,23 +192,19 @@ export function createEncoderRuntime(
198
192
  return { ok: false, mode: "B", code: verify.code };
199
193
  }
200
194
 
201
- // Allocate only after verification (task 3). Simulate allocator failure.
195
+ // Allocate only after verification (task 3).
202
196
  if (host.allocatorFails()) {
203
197
  demoteTo("B", ENC_FAIL.ASSET_UNREADABLE);
204
198
  return { ok: false, mode: "B", code: ENC_FAIL.ASSET_UNREADABLE };
205
199
  }
206
200
 
207
- // Cap the encoder's MARGINAL footprint at 150 MiB (task 3, Q01). This
208
- // bounds the encoder's incremental allocation, so a healthy process with
209
- // a large baseline RSS still reaches mode A.
201
+ // Cap the encoder's MARGINAL footprint at 150 MiB (task 3, Q01).
210
202
  if (footprint() > ENCODER_RSS_BUDGET_BYTES) {
211
203
  demoteTo("B", ENC_FAIL.RSS_BUDGET_EXCEEDED);
212
204
  return { ok: false, mode: "B", code: ENC_FAIL.RSS_BUDGET_EXCEEDED };
213
205
  }
214
206
 
215
207
  embeddedBytes = verify.embeddedBytes;
216
- // Q03: record the verified manifest's token capacity so inference can
217
- // enforce the model's declared maximum, not just the global 512 ceiling.
218
208
  maxTokens = verify.maxTokens;
219
209
  verified = true;
220
210
  mode = "A";
@@ -223,6 +213,18 @@ export function createEncoderRuntime(
223
213
  embeddedBytes: verify.embeddedBytes,
224
214
  onnxDigest: verify.onnxDigest.slice(0, 12),
225
215
  });
216
+
217
+ // ML5-C: runtime-backend selection dispatch + the seller event. Pure
218
+ // function + append-only log line; skipped entirely when the flag is off.
219
+ if (ML5C_ENABLED()) {
220
+ const chosen = selectRuntimeBackend({
221
+ platform: normalizePlatform(plat()),
222
+ benchRecord: null, // placeholder: real BenchResultV1 wiring ships in ML5-E
223
+ nativeOptIn: process.env.MEGACOMPACT_ENCODER_NATIVE === "1",
224
+ });
225
+ emitRuntimeSelected(host.stateDir ?? STATE_DIR_DEFAULT, chosen);
226
+ }
227
+
226
228
  return {
227
229
  ok: true,
228
230
  mode: "A",
@@ -233,7 +235,6 @@ export function createEncoderRuntime(
233
235
  },
234
236
  infer(input: EncoderInput): EncoderInferResult {
235
237
  if (!verified || mode !== "A") {
236
- // Only batch1/max512 verified assets reach inference (mode B/C do not).
237
238
  return {
238
239
  ok: false,
239
240
  code: ENC_FAIL.SHAPE_INVALID,
@@ -244,9 +245,6 @@ export function createEncoderRuntime(
244
245
  return { ok: false, code: ENC_FAIL.SHAPE_INVALID, shapeError: "missing tokens array" };
245
246
  }
246
247
  const n = input.tokens.length;
247
- // Q03: enforce the per-manifest maxTokens (<= global 512 ceiling), so an
248
- // over-cap request against a low-cap verified asset is rejected rather
249
- // than silently exceeding the model's declared capacity.
250
248
  if (n < 1 || n > maxTokens) {
251
249
  return {
252
250
  ok: false,
@@ -254,11 +252,7 @@ export function createEncoderRuntime(
254
252
  shapeError: `token count ${n} outside 1..${maxTokens} (manifest cap)`,
255
253
  };
256
254
  }
257
- // Q03: cap-before-allocation on the inference path too. Check the
258
- // marginal footprint BEFORE allocating the projection buffer; an
259
- // over-budget inference demotes to mode B consistently with load() (the
260
- // ENC_FAIL.RSS_BUDGET_EXCEEDED model: "measured RSS over 150 MiB -> B"),
261
- // so a subsequent infer no longer attempts allocation in a stale mode A.
255
+ // Q03: cap-before-allocation on the inference path too.
262
256
  if (footprint() > ENCODER_RSS_BUDGET_BYTES) {
263
257
  demoteTo("B", ENC_FAIL.RSS_BUDGET_EXCEEDED);
264
258
  return {
@@ -268,12 +262,11 @@ export function createEncoderRuntime(
268
262
  };
269
263
  }
270
264
  const start = host.nowMs();
271
- // Batch is always 1 (single request); shape is (1, n) for n in 1..maxTokens.
265
+ // ML5-C: the LCG placeholder STILL drives infer by default (the trained
266
+ // asset behind runtime-wasm.ts/runtime-native.ts is not yet the
267
+ // source-of-truth on master; only the runtime-selection event seam was
268
+ // added this sprint).
272
269
  const semantic = projectSemantic(seedFromBytes(embeddedBytes) ^ n, ENCODER_SEMANTIC_WIDTH);
273
- // Q01: the projection buffer is a single reusable 384-float array; the
274
- // marginal footprint is a fixed SEMANTIC_BUFFER_BYTES once it exists, so
275
- // selfAllocated is SET (never accumulated) — bounded regardless of how
276
- // many inferences run on a long-lived runtime.
277
270
  selfAllocated = SEMANTIC_BUFFER_BYTES;
278
271
  const latencyMs = host.nowMs() - start;
279
272
  return { ok: true, semantic, rssBytes: footprint(), latencyMs, shapeError: null };