pi-mega-compact 0.20.42 → 0.20.44
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/assets/vector-cortex/encoder-v1/manifest.json +1 -1
- package/assets/vector-cortex/encoder-v1/model-card.json +1 -1
- package/assets/vector-cortex/encoder-v1/model.onnx +0 -0
- package/assets/vector-cortex/encoder-v1/tokenizer.json +1 -1
- package/dist/config/vector-cortex-enc0a.js +32 -0
- package/dist/config/vector-cortex-enc0b.js +34 -0
- package/dist/config/vector-cortex.js +3 -3
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +2 -0
- package/dist/src/config/vector-cortex-enc0a.js +32 -0
- package/dist/src/config/vector-cortex-enc0b.js +34 -0
- package/dist/src/config/vector-cortex.js +3 -3
- package/dist/src/config.js +1 -1
- package/dist/src/vector-cortex/_acceptance-enc0a-contract.js +60 -0
- package/dist/src/vector-cortex/_acceptance-vc2a-conformance.js +90 -0
- package/dist/src/vector-cortex/_acceptance-vc2a-runtime.js +285 -0
- package/dist/src/vector-cortex/_acceptance-vc2b-conformance.js +92 -0
- package/dist/src/vector-cortex/_acceptance-vc2b-heads.js +221 -0
- package/dist/src/vector-cortex/encoder/asset.js +3 -2
- package/dist/src/vector-cortex/encoder/decision.js +75 -0
- package/dist/src/vector-cortex/encoder/emit.js +2 -0
- package/dist/src/vector-cortex/encoder/encoder-onnx-dispatch.js +58 -0
- package/dist/src/vector-cortex/encoder/onnx.js +147 -0
- package/dist/src/vector-cortex/encoder/runtime-wasm.js +17 -4
- package/dist/src/vector-cortex/encoder/runtime.js +17 -7
- package/dist/src/vector-cortex/encoder/types-vc2c.js +50 -0
- package/dist/src/vector-cortex/encoder/types.js +13 -34
- package/dist/vector-cortex/_acceptance-enc0a-contract.js +60 -0
- package/dist/vector-cortex/_acceptance-vc2a-conformance.js +90 -0
- package/dist/vector-cortex/_acceptance-vc2a-runtime.js +285 -0
- package/dist/vector-cortex/_acceptance-vc2b-conformance.js +92 -0
- package/dist/vector-cortex/_acceptance-vc2b-heads.js +221 -0
- package/dist/vector-cortex/encoder/asset.js +3 -2
- package/dist/vector-cortex/encoder/decision.js +75 -0
- package/dist/vector-cortex/encoder/emit.js +2 -0
- package/dist/vector-cortex/encoder/encoder-onnx-dispatch.js +58 -0
- package/dist/vector-cortex/encoder/onnx.js +147 -0
- package/dist/vector-cortex/encoder/runtime-wasm.js +17 -4
- package/dist/vector-cortex/encoder/runtime.js +17 -7
- package/dist/vector-cortex/encoder/types-vc2c.js +50 -0
- package/dist/vector-cortex/encoder/types.js +13 -34
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +12 -0
- package/package.json +3 -2
- package/src/config/vector-cortex-enc0a.ts +34 -0
- package/src/config/vector-cortex-enc0b.ts +36 -0
- package/src/config/vector-cortex.ts +3 -3
- package/src/config.ts +2 -0
- package/src/vector-cortex/_acceptance-enc0a-contract.ts +71 -0
- package/src/vector-cortex/_acceptance-vc2a-conformance.ts +119 -0
- package/src/vector-cortex/_acceptance-vc2a-runtime.ts +300 -0
- package/src/vector-cortex/_acceptance-vc2b-conformance.ts +121 -0
- package/src/vector-cortex/_acceptance-vc2b-heads.ts +234 -0
- package/src/vector-cortex/encoder/asset.ts +3 -2
- package/src/vector-cortex/encoder/bench-export.ts +2 -2
- package/src/vector-cortex/encoder/decision.ts +125 -0
- package/src/vector-cortex/encoder/emit.ts +5 -1
- package/src/vector-cortex/encoder/encoder-onnx-dispatch.ts +76 -0
- package/src/vector-cortex/encoder/onnx.ts +198 -0
- package/src/vector-cortex/encoder/runtime-native.ts +1 -1
- package/src/vector-cortex/encoder/runtime-wasm.ts +21 -8
- package/src/vector-cortex/encoder/runtime.ts +27 -9
- package/src/vector-cortex/encoder/types-vc2c.ts +134 -0
- package/src/vector-cortex/encoder/types.ts +19 -115
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/** VC2A runtime invariant + injection + triad + budget acceptance suites.
|
|
2
|
+
* Extracted from vc2a-acceptance.test.ts (soft-limit compliance). Context injected. */
|
|
3
|
+
import { test, describe } from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
export function registerVc2aRuntime(ctx) {
|
|
8
|
+
const { ENC_FAIL, ENCODER_RSS_BUDGET_BYTES, ENCODER_LATENCY_P95_MS, tmpAsset, baseManifest, buildDir, rmBuilt, sha256, percentile, createEncoderRuntime, verifyEncoderAsset, readEncoderManifest, } = ctx;
|
|
9
|
+
// -------------------------------------------------------------------------
|
|
10
|
+
// Invariant + unique failure injection + forced triad
|
|
11
|
+
// -------------------------------------------------------------------------
|
|
12
|
+
describe("encoder runtime invariant + injection + triad", () => {
|
|
13
|
+
test("invariant: only batch1/max512 verified assets reach inference", () => {
|
|
14
|
+
const built = buildDir("valid");
|
|
15
|
+
try {
|
|
16
|
+
const rt = createEncoderRuntime();
|
|
17
|
+
const load = rt.load(built.dir);
|
|
18
|
+
assert.equal(load.ok, true);
|
|
19
|
+
if (load.ok)
|
|
20
|
+
assert.equal(load.mode, "A");
|
|
21
|
+
// dims 1..512 infer; 0 and 513+ are shape-rejected.
|
|
22
|
+
for (const n of [1, 64, 512]) {
|
|
23
|
+
const inf = rt.infer({ tokens: Array.from({ length: n }, (_, i) => i % 500) });
|
|
24
|
+
assert.equal(inf.ok, true, `dim ${n} infers`);
|
|
25
|
+
}
|
|
26
|
+
for (const n of [0, 513, 1000]) {
|
|
27
|
+
const inf = rt.infer({ tokens: Array.from({ length: n }) });
|
|
28
|
+
assert.equal(inf.ok, false, `dim ${n} shape-rejected`);
|
|
29
|
+
if (!inf.ok)
|
|
30
|
+
assert.equal(inf.code, ENC_FAIL.SHAPE_INVALID);
|
|
31
|
+
}
|
|
32
|
+
// A batch>1 manifest never reaches inference: verification demotes to B.
|
|
33
|
+
const bad = buildDir("batch-2");
|
|
34
|
+
try {
|
|
35
|
+
const checker = createEncoderRuntime();
|
|
36
|
+
const check = checker.load(bad.dir);
|
|
37
|
+
assert.equal(check.ok, false);
|
|
38
|
+
if (!check.ok)
|
|
39
|
+
assert.equal(check.code, ENC_FAIL.BATCH_INVALID);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
rmBuilt(bad);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
rmBuilt(built);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
test("1..513 maxTokens manifests: 1..512 verify+infer, 513 demotes TOKENS_EXCEEDED", () => {
|
|
50
|
+
for (const maxTok of [1, 64, 512]) {
|
|
51
|
+
const dir = tmpAsset("vc2a-dim");
|
|
52
|
+
mkdirSync(dir, { recursive: true });
|
|
53
|
+
const onnx = Buffer.from(`dim-${maxTok}`, "binary");
|
|
54
|
+
const tok = Buffer.from('{"vocab":[]}', "utf8");
|
|
55
|
+
writeFileSync(join(dir, "model.onnx"), onnx);
|
|
56
|
+
writeFileSync(join(dir, "tokenizer.json"), tok);
|
|
57
|
+
const m = baseManifest({
|
|
58
|
+
maxTokens: maxTok,
|
|
59
|
+
onnx: { path: "model.onnx", sha256: sha256(onnx), bytes: onnx.length },
|
|
60
|
+
tokenizer: { path: "tokenizer.json", sha256: sha256(tok), bytes: tok.length },
|
|
61
|
+
});
|
|
62
|
+
writeFileSync(join(dir, "manifest.json"), JSON.stringify(m));
|
|
63
|
+
assert.equal(verifyEncoderAsset(dir, readEncoderManifest(dir)).ok, true, `maxTokens ${maxTok} verifies`);
|
|
64
|
+
const rt = createEncoderRuntime();
|
|
65
|
+
assert.equal(rt.load(dir).ok, true, `maxTokens ${maxTok} loads`);
|
|
66
|
+
const inf = rt.infer({ tokens: Array.from({ length: maxTok }) });
|
|
67
|
+
assert.equal(inf.ok, true, `cap ${maxTok} infers`);
|
|
68
|
+
rmSync(dir, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
// 513 demotes.
|
|
71
|
+
const dir513 = tmpAsset("vc2a-dim-513");
|
|
72
|
+
mkdirSync(dir513, { recursive: true });
|
|
73
|
+
const onnx513 = Buffer.from("dim-513", "binary");
|
|
74
|
+
const tok513 = Buffer.from('{"vocab":[]}', "utf8");
|
|
75
|
+
writeFileSync(join(dir513, "model.onnx"), onnx513);
|
|
76
|
+
writeFileSync(join(dir513, "tokenizer.json"), tok513);
|
|
77
|
+
const m513 = baseManifest({
|
|
78
|
+
maxTokens: 513,
|
|
79
|
+
onnx: { path: "model.onnx", sha256: sha256(onnx513), bytes: onnx513.length },
|
|
80
|
+
tokenizer: { path: "tokenizer.json", sha256: sha256(tok513), bytes: tok513.length },
|
|
81
|
+
});
|
|
82
|
+
writeFileSync(join(dir513, "manifest.json"), JSON.stringify(m513));
|
|
83
|
+
const res = verifyEncoderAsset(dir513, readEncoderManifest(dir513));
|
|
84
|
+
assert.equal(res.ok, false);
|
|
85
|
+
if (!res.ok)
|
|
86
|
+
assert.equal(res.code, ENC_FAIL.TOKENS_EXCEEDED);
|
|
87
|
+
rmSync(dir513, { recursive: true, force: true });
|
|
88
|
+
});
|
|
89
|
+
test("truncated ONNX during digest read demotes ENC_ASSET_UNREADABLE", () => {
|
|
90
|
+
// LIVE-platform manifest with absent model.onnx -> unreadable -> ASSET_UNREADABLE.
|
|
91
|
+
const built = buildDir("missing-onnx");
|
|
92
|
+
try {
|
|
93
|
+
const load = createEncoderRuntime().load(built.dir);
|
|
94
|
+
assert.equal(load.ok, false);
|
|
95
|
+
if (!load.ok) {
|
|
96
|
+
assert.equal(load.code, ENC_FAIL.ASSET_UNREADABLE);
|
|
97
|
+
assert.equal(load.mode, "B");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
rmBuilt(built);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
test("allocator failure after verification demotes ENC_ASSET_UNREADABLE", () => {
|
|
105
|
+
const built = buildDir("valid");
|
|
106
|
+
try {
|
|
107
|
+
const load = createEncoderRuntime({ host: { allocatorFails: () => true } }).load(built.dir);
|
|
108
|
+
assert.equal(load.ok, false);
|
|
109
|
+
if (!load.ok)
|
|
110
|
+
assert.equal(load.code, ENC_FAIL.ASSET_UNREADABLE);
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
rmBuilt(built);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
test("forced triad A / B / C", () => {
|
|
117
|
+
const dirA = buildDir("valid");
|
|
118
|
+
try {
|
|
119
|
+
assert.equal(createEncoderRuntime().load(dirA.dir).ok, true);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
rmBuilt(dirA);
|
|
123
|
+
}
|
|
124
|
+
// B: missing asset => asset-free trigram.
|
|
125
|
+
const dirB = buildDir("missing-onnx");
|
|
126
|
+
try {
|
|
127
|
+
const load = createEncoderRuntime().load(dirB.dir);
|
|
128
|
+
assert.equal(load.ok, false);
|
|
129
|
+
if (!load.ok)
|
|
130
|
+
assert.equal(load.mode, "B");
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
rmBuilt(dirB);
|
|
134
|
+
}
|
|
135
|
+
// C: lexical forced by mode C (rollback path).
|
|
136
|
+
const dirC = tmpAsset("vc2a-C");
|
|
137
|
+
mkdirSync(dirC, { recursive: true });
|
|
138
|
+
try {
|
|
139
|
+
const load = createEncoderRuntime({ forcedMode: "C" }).load(dirC);
|
|
140
|
+
assert.equal(load.ok, false);
|
|
141
|
+
if (!load.ok)
|
|
142
|
+
assert.equal(load.mode, "C");
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
rmSync(dirC, { recursive: true, force: true });
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
test("acceptance budgets: infer p95 <=40ms and encoder marginal footprint <=150MiB", () => {
|
|
149
|
+
// Q02: rssBytes = encoder MARGINAL footprint, never whole-process RSS.
|
|
150
|
+
// p95 via linear interpolation — not max sample.
|
|
151
|
+
const built = buildDir("valid");
|
|
152
|
+
try {
|
|
153
|
+
const rt = createEncoderRuntime();
|
|
154
|
+
assert.equal(rt.load(built.dir).ok, true);
|
|
155
|
+
const latencies = [];
|
|
156
|
+
for (let i = 0; i < 200; i++) {
|
|
157
|
+
const inf = rt.infer({ tokens: Array.from({ length: 128 }, (_, k) => k) });
|
|
158
|
+
assert.equal(inf.ok, true);
|
|
159
|
+
if (inf.ok) {
|
|
160
|
+
latencies.push(inf.latencyMs);
|
|
161
|
+
assert.ok(inf.rssBytes <= ENCODER_RSS_BUDGET_BYTES, "encoder marginal footprint <=150MiB");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const p95 = percentile(latencies, 0.95);
|
|
165
|
+
assert.ok(p95 <= ENCODER_LATENCY_P95_MS, `p95 ${p95} <= 40ms`);
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
rmBuilt(built);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
test("Q01: a long-lived runtime cannot drift over the 150MiB marginal budget", () => {
|
|
172
|
+
// selfAllocated models a REUSABLE projection buffer — must not accumulate.
|
|
173
|
+
// 100k inferences prove no irreversible budget demotion.
|
|
174
|
+
const built = buildDir("valid");
|
|
175
|
+
try {
|
|
176
|
+
const rt = createEncoderRuntime();
|
|
177
|
+
assert.equal(rt.load(built.dir).ok, true);
|
|
178
|
+
let firstRss = 0;
|
|
179
|
+
for (let i = 0; i < 100_000; i++) {
|
|
180
|
+
const inf = rt.infer({ tokens: Array.from({ length: 4 }, () => i % 500) });
|
|
181
|
+
assert.equal(inf.ok, true, `infer #${i} still ok`);
|
|
182
|
+
if (inf.ok) {
|
|
183
|
+
if (i === 0)
|
|
184
|
+
firstRss = inf.rssBytes;
|
|
185
|
+
assert.equal(inf.rssBytes, firstRss, `marginal footprint flat at infer #${i}`);
|
|
186
|
+
assert.ok(inf.rssBytes <= ENCODER_RSS_BUDGET_BYTES, `still within budget at infer #${i}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
assert.equal(rt.mode, "A", "mode A survives 100k inferences (no irreversible budget demotion)");
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
rmBuilt(built);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
test("Q03: per-manifest maxTokens is enforced at inference (over-cap rejected)", () => {
|
|
196
|
+
// maxTokens=64 manifest: 65..512 tokens must be SHAPE_INVALID.
|
|
197
|
+
const dir = tmpAsset("vc2a-q03");
|
|
198
|
+
mkdirSync(dir, { recursive: true });
|
|
199
|
+
const onnx = Buffer.from("q03-lowcap", "binary");
|
|
200
|
+
const tok = Buffer.from('{"vocab":[]}', "utf8");
|
|
201
|
+
writeFileSync(join(dir, "model.onnx"), onnx);
|
|
202
|
+
writeFileSync(join(dir, "tokenizer.json"), tok);
|
|
203
|
+
const m = baseManifest({
|
|
204
|
+
maxTokens: 64,
|
|
205
|
+
onnx: { path: "model.onnx", sha256: sha256(onnx), bytes: onnx.length },
|
|
206
|
+
tokenizer: { path: "tokenizer.json", sha256: sha256(tok), bytes: tok.length },
|
|
207
|
+
});
|
|
208
|
+
writeFileSync(join(dir, "manifest.json"), JSON.stringify(m));
|
|
209
|
+
try {
|
|
210
|
+
const rt = createEncoderRuntime();
|
|
211
|
+
assert.equal(rt.load(dir).ok, true, "maxTokens=64 manifest loads (mode A)");
|
|
212
|
+
assert.equal(rt.infer({ tokens: Array.from({ length: 64 }, (_, k) => k) }).ok, true, "cap 64 infers");
|
|
213
|
+
for (const over of [65, 128, 512]) {
|
|
214
|
+
const inf = rt.infer({ tokens: Array.from({ length: over }) });
|
|
215
|
+
assert.equal(inf.ok, false, `${over} tokens rejected against a 64-cap manifest`);
|
|
216
|
+
if (!inf.ok)
|
|
217
|
+
assert.equal(inf.code, ENC_FAIL.SHAPE_INVALID);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
rmSync(dir, { recursive: true, force: true });
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
test("Q04: MEGACOMPACT_VC2A=0 makes the default factory select mode C (rollback)", () => {
|
|
225
|
+
// Flag OFF => default factory is fixed at mode C (byte-identical predecessor).
|
|
226
|
+
const saved = process.env.MEGACOMPACT_VC2A;
|
|
227
|
+
process.env.MEGACOMPACT_VC2A = "0";
|
|
228
|
+
try {
|
|
229
|
+
// An explicitly forced "C" also reports ROLLBACK (existing contract).
|
|
230
|
+
const forced = createEncoderRuntime({ forcedMode: "C" });
|
|
231
|
+
// The default factory under flag-off must match that exactly.
|
|
232
|
+
const byFlag = createEncoderRuntime();
|
|
233
|
+
for (const rt of [forced, byFlag]) {
|
|
234
|
+
assert.equal(rt.mode, "C", "flag-off / forced runtime starts in mode C");
|
|
235
|
+
const built = buildDir("valid");
|
|
236
|
+
try {
|
|
237
|
+
const load = rt.load(built.dir);
|
|
238
|
+
assert.equal(load.ok, false);
|
|
239
|
+
if (!load.ok) {
|
|
240
|
+
assert.equal(load.mode, "C", "flag-off default factory reports mode C");
|
|
241
|
+
assert.equal(load.code, ENC_FAIL.ROLLBACK, "ROLLBACK, not MANIFEST_INVALID");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
rmBuilt(built);
|
|
246
|
+
}
|
|
247
|
+
const inf = rt.infer({ tokens: [1, 2, 3] });
|
|
248
|
+
assert.equal(inf.ok, false, "no learned infer on the flag-off path");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
if (saved === undefined)
|
|
253
|
+
delete process.env.MEGACOMPACT_VC2A;
|
|
254
|
+
else
|
|
255
|
+
process.env.MEGACOMPACT_VC2A = saved;
|
|
256
|
+
}
|
|
257
|
+
// Restore the module-scope pin (flag ON) for subsequent scenarios.
|
|
258
|
+
process.env.MEGACOMPACT_VC2A = "1";
|
|
259
|
+
});
|
|
260
|
+
test("all digest corruptions demote before load", () => {
|
|
261
|
+
for (const which of ["onnx", "tokenizer", "both"]) {
|
|
262
|
+
const dir = tmpAsset("vc2a-corr");
|
|
263
|
+
mkdirSync(dir, { recursive: true });
|
|
264
|
+
const onnx = Buffer.from("corrupt-me-onnx", "binary");
|
|
265
|
+
const tok = Buffer.from('{"vocab":[]}', "utf8");
|
|
266
|
+
writeFileSync(join(dir, "model.onnx"), onnx);
|
|
267
|
+
writeFileSync(join(dir, "tokenizer.json"), tok);
|
|
268
|
+
const m = baseManifest({
|
|
269
|
+
onnx: { path: "model.onnx", sha256: sha256(onnx), bytes: onnx.length },
|
|
270
|
+
tokenizer: { path: "tokenizer.json", sha256: sha256(tok), bytes: tok.length },
|
|
271
|
+
});
|
|
272
|
+
if (which === "onnx" || which === "both")
|
|
273
|
+
writeFileSync(join(dir, "model.onnx"), Buffer.concat([onnx, Buffer.from("X")]));
|
|
274
|
+
if (which === "tokenizer" || which === "both")
|
|
275
|
+
writeFileSync(join(dir, "tokenizer.json"), Buffer.concat([tok, Buffer.from("X")]));
|
|
276
|
+
writeFileSync(join(dir, "manifest.json"), JSON.stringify(m));
|
|
277
|
+
const res = verifyEncoderAsset(dir, readEncoderManifest(dir));
|
|
278
|
+
assert.equal(res.ok, false, `corrupt ${which} demotes`);
|
|
279
|
+
if (!res.ok)
|
|
280
|
+
assert.equal(res.code, ENC_FAIL.DIGEST_MISMATCH, `corrupt ${which} code`);
|
|
281
|
+
rmSync(dir, { recursive: true, force: true });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** VC2B acceptance — ENC-009..016 conformance rows + the ENC-HEAD-001 /
|
|
2
|
+
* ENC-ZERO-002 / ENC-FALLBACK-003 named assertions, resolved through the REAL
|
|
3
|
+
* heads/trigram producers (no mocks). Extracted from vc2b-acceptance.test.ts so
|
|
4
|
+
* the aggregator stays under the soft line limit. Context (fixture loader,
|
|
5
|
+
* shared token/order constants) is injected by the aggregator to avoid a
|
|
6
|
+
* circular import.
|
|
7
|
+
*/
|
|
8
|
+
import { test, describe } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { ENC2B_IDS, ENCODER_HEAD_DIMS, } from "./encoder/types.js";
|
|
11
|
+
import { encodeVectorSet, projectHead, l2Norm } from "./encoder/heads.js";
|
|
12
|
+
import { embedTrigram512, ENCODER_TRIGRAM_WIDTH, selectTrigramBFallback, } from "./encoder/trigram.js";
|
|
13
|
+
export function registerConformanceRows(ctx) {
|
|
14
|
+
const { fixture, SET_TOKENS, EMPTY_TOKENS, ORDERED_DIMS } = ctx;
|
|
15
|
+
// Suite 2 — ENC-009..016 conformance rows through the real producers
|
|
16
|
+
describe("ENC-009..016 conformance rows", () => {
|
|
17
|
+
test("ENC-009: full-set produces five heads with ordered dims", () => {
|
|
18
|
+
const fx = fixture("ENC-009");
|
|
19
|
+
assert.equal(fx.expected.ok, true);
|
|
20
|
+
const set = encodeVectorSet(SET_TOKENS);
|
|
21
|
+
assert.equal(set.heads.length, fx.expected.heads);
|
|
22
|
+
assert.deepEqual(set.heads.map((h) => h.dim), fx.expected.dims, "ordered dims");
|
|
23
|
+
});
|
|
24
|
+
for (const id of ENC2B_IDS.slice(1, 6)) {
|
|
25
|
+
test(`${id}: ${fixture(id).input.head} head emits its declared dim`, () => {
|
|
26
|
+
const fx = fixture(id);
|
|
27
|
+
assert.equal(fx.expected.ok, true);
|
|
28
|
+
const head = fx.input.head;
|
|
29
|
+
assert.ok(head);
|
|
30
|
+
const hv = projectHead(head, SET_TOKENS);
|
|
31
|
+
assert.equal(hv.dim, ENCODER_HEAD_DIMS[head]);
|
|
32
|
+
assert.equal(hv.dim, fx.expected.dim, "declared dim");
|
|
33
|
+
assert.equal(hv.values.length, hv.dim);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
test("ENC-015: zero-input produces finite all-zero vectors for every head", () => {
|
|
37
|
+
const fx = fixture("ENC-015");
|
|
38
|
+
assert.equal(fx.expected.ok, true);
|
|
39
|
+
assert.equal(fx.expected.zero, true);
|
|
40
|
+
const set = encodeVectorSet(EMPTY_TOKENS);
|
|
41
|
+
assert.equal(set.heads.length, fx.expected.heads);
|
|
42
|
+
for (const hv of set.heads) {
|
|
43
|
+
for (const v of hv.values)
|
|
44
|
+
assert.equal(Number.isFinite(v), true, "finite");
|
|
45
|
+
assert.equal(hv.values.every((v) => v === 0), true, `head ${hv.head} all-zero`);
|
|
46
|
+
assert.equal(l2Norm(hv.values), 0);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
test("ENC-016: asset-free trigram B emits 512 dims", () => {
|
|
50
|
+
const fx = fixture("ENC-016");
|
|
51
|
+
assert.equal(fx.expected.ok, true);
|
|
52
|
+
assert.equal(fx.expected.mode, "B");
|
|
53
|
+
assert.equal(fx.expected.width, 512);
|
|
54
|
+
const v = embedTrigram512("the model is removed but trigram B still works");
|
|
55
|
+
assert.equal(v.length, ENCODER_TRIGRAM_WIDTH);
|
|
56
|
+
assert.equal(v.length, 512);
|
|
57
|
+
const n = l2Norm(v);
|
|
58
|
+
assert.equal(n === 0 || Math.abs(n - 1) <= 1e-6, true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
// Suite 3 — named assertions
|
|
62
|
+
describe("VC2B named assertions", () => {
|
|
63
|
+
test("ENC-HEAD-001: all five output shapes match ordered dims", () => {
|
|
64
|
+
const fx = fixture("ENC-HEAD-001");
|
|
65
|
+
assert.equal(fx.expected.ok, true);
|
|
66
|
+
const set = encodeVectorSet(SET_TOKENS);
|
|
67
|
+
assert.deepEqual(set.heads.map((h) => h.dim), ORDERED_DIMS);
|
|
68
|
+
});
|
|
69
|
+
test("ENC-ZERO-002: empty input produces finite zero vectors", () => {
|
|
70
|
+
const fx = fixture("ENC-ZERO-002");
|
|
71
|
+
assert.equal(fx.expected.ok, true);
|
|
72
|
+
const set = encodeVectorSet(EMPTY_TOKENS);
|
|
73
|
+
for (const hv of set.heads) {
|
|
74
|
+
assert.equal(hv.values.every((v) => v === 0), true);
|
|
75
|
+
assert.equal(hv.values.every((v) => Number.isFinite(v)), true);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
test("ENC-FALLBACK-003: removed model still yields 512d trigram B", () => {
|
|
79
|
+
const fx = fixture("ENC-FALLBACK-003");
|
|
80
|
+
assert.equal(fx.expected.ok, true);
|
|
81
|
+
assert.equal(fx.expected.mode, "B");
|
|
82
|
+
assert.equal(fx.expected.width, 512);
|
|
83
|
+
// The learned asset is gone (asset dir absent); the asset-free trigram B
|
|
84
|
+
// still produces a full 512-dim vector — no import of the learned asset.
|
|
85
|
+
const v = embedTrigram512("no learned model present, trigram independent");
|
|
86
|
+
assert.equal(v.length, 512);
|
|
87
|
+
const sel = selectTrigramBFallback();
|
|
88
|
+
assert.equal(sel.mode, "B");
|
|
89
|
+
assert.equal(sel.dim, 512);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/** VC2B acceptance — multi-head invariant + independence + forced triad through
|
|
2
|
+
* the REAL encode-or-fallback router (no mocks, including unique failure
|
|
3
|
+
* injection where the learned asset is deleted after A selection but before
|
|
4
|
+
* inference). Extracted from vc2b-acceptance.test.ts so the aggregator stays
|
|
5
|
+
* under the soft line limit. Shared context (withFlagsOn, stageVerifyingAssetDir,
|
|
6
|
+
* token/order constants) is injected by the aggregator to avoid a circular import.
|
|
7
|
+
*/
|
|
8
|
+
import { test, describe } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { rmSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { ENC_FAIL, ENCODER_HEAD_DIMS } from "./encoder/types.js";
|
|
13
|
+
import { encodeVectorSet, l2Norm } from "./encoder/heads.js";
|
|
14
|
+
import { ENCODER_TRIGRAM_WIDTH } from "./encoder/trigram.js";
|
|
15
|
+
import { embedLexical, ENCODER_LEXICAL_WIDTH } from "./encoder/lexical.js";
|
|
16
|
+
import { encodeOrFallback } from "./encoder/router.js";
|
|
17
|
+
import { createEncoderRuntime } from "./encoder/runtime.js";
|
|
18
|
+
import { createEncoderHeadsReporter } from "./encoder/emit-vc2b.js";
|
|
19
|
+
export function registerHeads(ctx) {
|
|
20
|
+
const { withFlagsOn, stageVerifyingAssetDir, SET_TOKENS, EMPTY_TOKENS, ORDERED_DIMS } = ctx;
|
|
21
|
+
// Suite 4 — invariant + unique failure injection + forced triad
|
|
22
|
+
describe("multi-head invariant + independence + triad", () => {
|
|
23
|
+
test("invariant: every emitted norm is 0 or within 1e-6 of 1", () => {
|
|
24
|
+
for (const tokens of [SET_TOKENS, EMPTY_TOKENS, [7], Array.from({ length: 300 }, (_, i) => i)]) {
|
|
25
|
+
const set = encodeVectorSet(tokens);
|
|
26
|
+
for (const hv of set.heads) {
|
|
27
|
+
const n = l2Norm(hv.values);
|
|
28
|
+
assert.equal(n === 0 || Math.abs(n - 1) <= 1e-6, true, `${hv.head} norm ${n}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
test("repeat drift <= 1e-6 across repeated seeded exports (all five heads)", () => {
|
|
33
|
+
for (let rep = 0; rep < 3; rep++) {
|
|
34
|
+
const a = encodeVectorSet(SET_TOKENS);
|
|
35
|
+
const b = encodeVectorSet(SET_TOKENS);
|
|
36
|
+
for (let i = 0; i < a.heads.length; i++) {
|
|
37
|
+
for (let j = 0; j < a.heads[i].values.length; j++) {
|
|
38
|
+
assert.equal(Math.abs(a.heads[i].values[j] - b.heads[i].values[j]) <= 1e-6, true);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
test("unique failure injection: delete model after A selection but before inference; router catches the real load() failure and selects independently initialized B", () => {
|
|
44
|
+
// This scenario is ON-dependent: it asserts a fallback emission, which is
|
|
45
|
+
// VC2B-flag-gated, and drives the VC2A runtime into an A load — so it self-pins
|
|
46
|
+
// both flags ON and is thus valid under either the default-ON run or the
|
|
47
|
+
// MEGACOMPACT_VC2B=0 parity run.
|
|
48
|
+
withFlagsOn(() => {
|
|
49
|
+
// Stage a learned asset the VC2A runtime would VERIFY into mode A, then
|
|
50
|
+
// REMOVE model.onnx before encoding. The router's load() returns the real
|
|
51
|
+
// ENC_ASSET_UNREADABLE failure code and must hand off to the independently
|
|
52
|
+
// initialized asset-free trigram B — emitting vector_cortex_encoder_fallback_selected
|
|
53
|
+
// from the production seam (S2: a true end-to-end router test, not a
|
|
54
|
+
// simulated direct call to selectTrigramBFallback).
|
|
55
|
+
const dir = stageVerifyingAssetDir();
|
|
56
|
+
const emitted = [];
|
|
57
|
+
const reporter = createEncoderHeadsReporter((e) => emitted.push(e));
|
|
58
|
+
try {
|
|
59
|
+
// A is selectable at this point (a staging runtime verifies it).
|
|
60
|
+
const probe = createEncoderRuntime();
|
|
61
|
+
assert.equal(probe.load(dir).ok, true, "staged asset verifies into A");
|
|
62
|
+
// "After A selection but before inference": the on-disk model is gone.
|
|
63
|
+
rmSync(join(dir, "model.onnx"), { force: true });
|
|
64
|
+
const verdict = encodeOrFallback({ tokens: SET_TOKENS }, dir, { reporter });
|
|
65
|
+
assert.equal(verdict.ok, true);
|
|
66
|
+
assert.equal(verdict.mode, "B", "router handoff selects independently initialized B");
|
|
67
|
+
assert.equal(verdict.width, 512);
|
|
68
|
+
if (verdict.ok) {
|
|
69
|
+
assert.equal(verdict.vector.length, 512);
|
|
70
|
+
assert.equal(verdict.code, ENC_FAIL.ASSET_UNREADABLE, "load() reported the real failure code");
|
|
71
|
+
}
|
|
72
|
+
assert.ok(emitted.includes("vector_cortex_encoder_fallback_selected"), "fallback-selected fired from the real router seam: " + emitted.join(","));
|
|
73
|
+
// Distinct vectors for distinct inputs — the fallback is not a constant.
|
|
74
|
+
const again = encodeOrFallback({ tokens: [9, 8, 7, 6] }, dir, { reporter });
|
|
75
|
+
assert.equal(again.ok, true);
|
|
76
|
+
if (verdict.ok && again.ok && verdict.mode === "B" && again.mode === "B") {
|
|
77
|
+
let diff = 0;
|
|
78
|
+
for (let i = 0; i < verdict.vector.length; i++)
|
|
79
|
+
diff += Math.abs(verdict.vector[i] - again.vector[i]);
|
|
80
|
+
assert.ok(diff > 1e-3, "independent trigram B is input-sensitive");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
rmSync(dir, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
test("forced triad A / B / C through the encode-or-fallback router", () => {
|
|
89
|
+
// ON-dependent: asserts heads_emitted / fallback_selected emissions, so it
|
|
90
|
+
// self-pins both flags ON (valid under either the default-ON run or the
|
|
91
|
+
// MEGACOMPACT_VC2B=0 parity run).
|
|
92
|
+
withFlagsOn(() => {
|
|
93
|
+
// A = learned projections: a verifying asset dir routes to a VectorSetV1 with
|
|
94
|
+
// the five heads in ordered dims (emitting heads_emitted).
|
|
95
|
+
const dirA = stageVerifyingAssetDir();
|
|
96
|
+
const emittedA = [];
|
|
97
|
+
try {
|
|
98
|
+
const reporterA = createEncoderHeadsReporter((e) => emittedA.push(e));
|
|
99
|
+
const a = encodeOrFallback({ tokens: SET_TOKENS }, dirA, { reporter: reporterA });
|
|
100
|
+
assert.equal(a.ok, true);
|
|
101
|
+
assert.equal(a.mode, "A");
|
|
102
|
+
if (a.ok) {
|
|
103
|
+
assert.equal(a.vectorSet.heads.length, 5);
|
|
104
|
+
assert.deepEqual(a.vectorSet.heads.map((h) => h.dim), ORDERED_DIMS);
|
|
105
|
+
}
|
|
106
|
+
assert.ok(emittedA.includes("vector_cortex_encoder_heads_emitted"));
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
rmSync(dirA, { recursive: true, force: true });
|
|
110
|
+
}
|
|
111
|
+
// B = 512d trigram selected when the learned asset directory is REMOVED: the
|
|
112
|
+
// router's load() fails (no manual fetch) and hands off to B. Remove a staged
|
|
113
|
+
// asset dir so the directory is genuinely absent, proving B needs no asset.
|
|
114
|
+
const dirB = stageVerifyingAssetDir();
|
|
115
|
+
rmSync(dirB, { recursive: true, force: true }); // asset directory REMOVED
|
|
116
|
+
const emittedB = [];
|
|
117
|
+
const reporterB = createEncoderHeadsReporter((e) => emittedB.push(e));
|
|
118
|
+
const b = encodeOrFallback({ tokens: SET_TOKENS }, dirB, { reporter: reporterB });
|
|
119
|
+
assert.equal(b.ok, true);
|
|
120
|
+
assert.equal(b.mode, "B", "B works without an asset dir");
|
|
121
|
+
assert.equal(b.width, 512);
|
|
122
|
+
if (b.ok) {
|
|
123
|
+
assert.equal(b.vector.length, 512);
|
|
124
|
+
assert.equal(b.limitation, null);
|
|
125
|
+
}
|
|
126
|
+
assert.ok(emittedB.includes("vector_cortex_encoder_fallback_selected"), "B selection emits fallback-selected");
|
|
127
|
+
// C = token/phrase lexical forced when both A and B runtimes are disabled.
|
|
128
|
+
const emittedC = [];
|
|
129
|
+
const reporterC = createEncoderHeadsReporter((e) => emittedC.push(e));
|
|
130
|
+
const c = encodeOrFallback({ tokens: SET_TOKENS }, dirB, { reporter: reporterC, forceFallback: "C" });
|
|
131
|
+
assert.equal(c.ok, true);
|
|
132
|
+
assert.equal(c.mode, "C");
|
|
133
|
+
assert.equal(c.width, ENCODER_LEXICAL_WIDTH);
|
|
134
|
+
if (c.ok && c.mode === "C") {
|
|
135
|
+
assert.equal(c.vector.length, ENCODER_LEXICAL_WIDTH);
|
|
136
|
+
assert.ok((c.limitation ?? "").length > 0, "C reports its semantic-context limitation");
|
|
137
|
+
// Q04: a FORCED C is an intentional selection, not a demotion or rollback,
|
|
138
|
+
// so it must NOT be stamped with ENC_FAIL.ROLLBACK.
|
|
139
|
+
assert.equal(c.code, null, "forced C is not a rollback/demotion (code = null)");
|
|
140
|
+
}
|
|
141
|
+
assert.ok(emittedC.includes("vector_cortex_encoder_fallback_selected"), "C selection emits fallback-selected");
|
|
142
|
+
// Widths are disjoint across the triad (no shared feature space).
|
|
143
|
+
const aWidths = Object.values(ENCODER_HEAD_DIMS);
|
|
144
|
+
for (const w of [...aWidths, ENCODER_LEXICAL_WIDTH])
|
|
145
|
+
assert.notEqual(w, ENCODER_TRIGRAM_WIDTH);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
test("forced fallback (B/C) wins over the empty-input degenerate case (Q02)", () => {
|
|
149
|
+
// Q02: a caller that explicitly forces a fallback mode must get that mode
|
|
150
|
+
// even for EMPTY input — empty tokens must NOT silently short-circuit to B.
|
|
151
|
+
// Asserts fallback_selected emissions → ON-dependent, so it self-pins via
|
|
152
|
+
// withFlagsOn (valid under either external env, same as the forced-triad test).
|
|
153
|
+
withFlagsOn(() => {
|
|
154
|
+
const emittedC = [];
|
|
155
|
+
const reporterC = createEncoderHeadsReporter((e) => emittedC.push(e));
|
|
156
|
+
const c = encodeOrFallback({ tokens: EMPTY_TOKENS }, "", { reporter: reporterC, forceFallback: "C" });
|
|
157
|
+
assert.equal(c.ok, true);
|
|
158
|
+
assert.equal(c.mode, "C", "forced C must win over empty-input B selection");
|
|
159
|
+
if (c.ok && c.mode === "C") {
|
|
160
|
+
assert.equal(c.vector.length, ENCODER_LEXICAL_WIDTH);
|
|
161
|
+
assert.equal(c.code, null, "forced C carries no rollback/demotion code (Q04)");
|
|
162
|
+
}
|
|
163
|
+
assert.ok(emittedC.includes("vector_cortex_encoder_fallback_selected"));
|
|
164
|
+
// Forced B on empty input also stays B (force mode is honored, no failure
|
|
165
|
+
// code — a forced mode is not a rollback/demotion, so code is null).
|
|
166
|
+
const emittedB = [];
|
|
167
|
+
const reporterB = createEncoderHeadsReporter((e) => emittedB.push(e));
|
|
168
|
+
const b = encodeOrFallback({ tokens: EMPTY_TOKENS }, "", { reporter: reporterB, forceFallback: "B" });
|
|
169
|
+
assert.equal(b.ok, true);
|
|
170
|
+
assert.equal(b.mode, "B");
|
|
171
|
+
if (b.ok && b.mode === "B") {
|
|
172
|
+
assert.equal(b.vector.length, ENCODER_TRIGRAM_WIDTH);
|
|
173
|
+
assert.equal(b.code, null, "forced B carries no rollback/demotion code (Q04)");
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
test("A/B/C use disjoint widths and independent algorithms", () => {
|
|
178
|
+
// A head widths are 384/128/128/64/32; B is 512; C is 256 — no shared space.
|
|
179
|
+
const aWidths = Object.values(ENCODER_HEAD_DIMS);
|
|
180
|
+
const bWidth = ENCODER_TRIGRAM_WIDTH;
|
|
181
|
+
const cWidth = ENCODER_LEXICAL_WIDTH;
|
|
182
|
+
for (const w of [...aWidths, cWidth])
|
|
183
|
+
assert.notEqual(w, bWidth);
|
|
184
|
+
// B works with the asset absent; C works with both vector runtimes disabled
|
|
185
|
+
// (C does not depend on B or A — it embeds tokens directly).
|
|
186
|
+
const cTokens = embedLexical("independently computed lexical with vector runtimes disabled");
|
|
187
|
+
assert.equal(cTokens.length, ENCODER_LEXICAL_WIDTH);
|
|
188
|
+
});
|
|
189
|
+
test("router seam enforces the verified per-manifest token capacity: over-cap input routes to B with ENC_SHAPE_INVALID, never an over-cap A VectorSet", () => {
|
|
190
|
+
// Q01: the router's mode-A path must enforce the VC2A contract
|
|
191
|
+
// "only batch1/<=maxTokens verified assets reach inference" at its own seam.
|
|
192
|
+
// A verified asset declaring maxTokens=64 with an input of 100 tokens must
|
|
193
|
+
// NOT produce an ok:true mode-A VectorSetV1 whose inputTokens breach the
|
|
194
|
+
// model's declared capacity — instead the router rejects it and falls back
|
|
195
|
+
// to the asset-free trigram B, reporting the real shape failure code.
|
|
196
|
+
withFlagsOn(() => {
|
|
197
|
+
const dir = stageVerifyingAssetDir(64); // verified low-cap manifest
|
|
198
|
+
try {
|
|
199
|
+
const over = encodeOrFallback({ tokens: Array.from({ length: 100 }, (_, i) => i) }, dir);
|
|
200
|
+
assert.equal(over.ok, true, "over-cap input still yields a usable (fallback) verdict");
|
|
201
|
+
assert.equal(over.mode, "B", "over-cap input must route to the B fallback, not an A VectorSet");
|
|
202
|
+
if (over.ok) {
|
|
203
|
+
assert.equal(over.code, ENC_FAIL.SHAPE_INVALID, "reported the real shape failure code");
|
|
204
|
+
assert.equal(over.vector.length, ENCODER_TRIGRAM_WIDTH);
|
|
205
|
+
}
|
|
206
|
+
// A within-cap input against the SAME verified manifest still reaches a
|
|
207
|
+
// qualified mode-A VectorSet — the capacity rejection is input-scoped,
|
|
208
|
+
// not a blanket demotion of the verified asset.
|
|
209
|
+
const within = encodeOrFallback({ tokens: SET_TOKENS }, dir);
|
|
210
|
+
assert.equal(within.ok, true);
|
|
211
|
+
assert.equal(within.mode, "A", "within-cap input still reaches mode A");
|
|
212
|
+
if (within.ok)
|
|
213
|
+
assert.equal(within.vectorSet.heads.length, 5);
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
rmSync(dir, { recursive: true, force: true });
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* vector-cortex/encoder/asset.ts — VC2A asset verification (task 2).
|
|
3
3
|
*
|
|
4
4
|
* Verifies a ModelManifestV1 before any allocation: SHA-256 the ONNX and
|
|
5
|
-
* tokenizer against the manifest digests, require opset
|
|
5
|
+
* tokenizer against the manifest digests, require opset 21 (ENC-0a re-baseline),
|
|
6
|
+
* batch exactly 1 and
|
|
6
7
|
* maximum 512 tokens, and confirm the current platform is in the supported
|
|
7
8
|
* matrix. On ANY of these the caller demotes to mode B (asset-free trigram) —
|
|
8
9
|
* never a remote fetch (PREVENT-PI-004). A truncated/unreadable asset during
|
|
@@ -73,7 +74,7 @@ function isManifest(m) {
|
|
|
73
74
|
*
|
|
74
75
|
* - manifest parse/shape failure -> ENC_MANIFEST_INVALID -> mode B
|
|
75
76
|
* - unsupported platform -> ENC_PLATFORM_UNSUPPORTED -> mode B
|
|
76
|
-
* - opset !=
|
|
77
|
+
* - opset != 21 -> ENC_OPSET_INVALID -> mode B
|
|
77
78
|
* - batch != 1 -> ENC_BATCH_INVALID -> mode B
|
|
78
79
|
* - maxTokens > 512 -> ENC_TOKENS_EXCEEDED -> mode B
|
|
79
80
|
* - on-disk digest != manifest digest -> ENC_DIGEST_MISMATCH (one-byte mutation)
|