pi-mega-compact 0.20.0 → 0.20.2
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/config/vector-cortex.js +25 -0
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +2 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-reconstruct.js +33 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-residual.js +51 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex.js +2 -0
- package/dist/extensions/dashboard-server/routes.js +1 -1
- package/dist/extensions/dashboard-server/server.js +5 -1
- package/dist/src/config/vector-cortex.js +25 -0
- package/dist/src/config.js +1 -1
- package/dist/src/vector-cortex/reconstruct/_acceptance-helpers.js +365 -0
- package/dist/src/vector-cortex/reconstruct/assemble.js +137 -0
- package/dist/src/vector-cortex/reconstruct/closure.js +268 -0
- package/dist/src/vector-cortex/reconstruct/types.js +46 -0
- package/dist/src/vector-cortex/reconstruct/validate.js +152 -0
- package/dist/src/vector-cortex/residual/codec.js +224 -0
- package/dist/src/vector-cortex/residual/dct.js +158 -0
- package/dist/src/vector-cortex/residual/fixture-payload.js +77 -0
- package/dist/src/vector-cortex/residual/gf256.js +182 -0
- package/dist/src/vector-cortex/residual/parity.js +245 -0
- package/dist/src/vector-cortex/residual/quantize.js +200 -0
- package/dist/src/vector-cortex/residual/stream.js +124 -0
- package/dist/src/vector-cortex/residual/types.js +55 -0
- package/dist/vector-cortex/reconstruct/_acceptance-helpers.js +365 -0
- package/dist/vector-cortex/reconstruct/assemble.js +137 -0
- package/dist/vector-cortex/reconstruct/closure.js +268 -0
- package/dist/vector-cortex/reconstruct/types.js +46 -0
- package/dist/vector-cortex/reconstruct/validate.js +152 -0
- package/dist/vector-cortex/residual/codec.js +224 -0
- package/dist/vector-cortex/residual/dct.js +158 -0
- package/dist/vector-cortex/residual/fixture-payload.js +77 -0
- package/dist/vector-cortex/residual/gf256.js +182 -0
- package/dist/vector-cortex/residual/parity.js +245 -0
- package/dist/vector-cortex/residual/quantize.js +200 -0
- package/dist/vector-cortex/residual/stream.js +124 -0
- package/dist/vector-cortex/residual/types.js +55 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-n4z5w9Ox.js → AreaChart-XF3jPpvn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-n4z5w9Ox.js.map → AreaChart-XF3jPpvn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-BLGjWWBr.js → BarChart-DnYYn0GF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-BLGjWWBr.js.map → BarChart-DnYYn0GF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-CAnrKKVK.js → CacheTab-D3bVskCL.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-CAnrKKVK.js.map → CacheTab-D3bVskCL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-D-GL7Ngq.js → EventsTab-CX_8myfc.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-D-GL7Ngq.js.map → EventsTab-CX_8myfc.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-C4x5qQ8s.js → HealthTab-CnVzBQvN.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-C4x5qQ8s.js.map → HealthTab-CnVzBQvN.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-BSPs0eq5.js → MaintenanceTab-Cao--dtV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-BSPs0eq5.js.map → MaintenanceTab-Cao--dtV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-BWEUtTSu.js → MemoryMapTab-DZdI-9ig.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-BWEUtTSu.js.map → MemoryMapTab-DZdI-9ig.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Dhm1c3Cx.js → MetricsTab-SlpSmNU4.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Dhm1c3Cx.js.map → MetricsTab-SlpSmNU4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-Cm6cySiQ.js → OverviewTab-C_-_4au9.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-Cm6cySiQ.js.map → OverviewTab-C_-_4au9.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DxLN1N4K.js → ReposTab-CyWedWZC.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DxLN1N4K.js.map → ReposTab-CyWedWZC.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-CZuI4ISW.js → SessionsTab-wqPBMIJZ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-CZuI4ISW.js.map → SessionsTab-wqPBMIJZ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-C_0VOomm.js → SetupTab-MojXmuUf.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-C_0VOomm.js.map → SetupTab-MojXmuUf.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BhbaRLmi.js → TimeSavedCard-BxcplEOJ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BhbaRLmi.js.map → TimeSavedCard-BxcplEOJ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CXJmbjje.js → TurnsTab-D8_qWbjY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CXJmbjje.js.map → TurnsTab-D8_qWbjY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-B3yisAyB.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-B3yisAyB.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-C5uvMCzp.js → WikiTab-Cfd-QZYz.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-C5uvMCzp.js.map → WikiTab-Cfd-QZYz.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-Cl_fxJcg.js → button-tX5ht0bb.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-Cl_fxJcg.js.map → button-tX5ht0bb.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-BH8Le5YM.js → card-BT3Z5dDg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-BH8Le5YM.js.map → card-BT3Z5dDg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-sZ6ZNtBi.js → generateCategoricalChart-B6p2WnkY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-sZ6ZNtBi.js.map → generateCategoricalChart-B6p2WnkY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-CX9zv8cJ.js → index-CFLekP12.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-CX9zv8cJ.js.map → index-CFLekP12.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-CXJhcjhM.js → switch-B52oAMSw.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-CXJhcjhM.js.map → switch-B52oAMSw.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-CoLtIc6y.js → toggle-VxxVc5Tp.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-CoLtIc6y.js.map → toggle-VxxVc5Tp.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-fSXho606.js → useSSE-DAVdZnG7.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-fSXho606.js.map → useSSE-DAVdZnG7.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/api/vector-cortex.ts +9 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +37 -0
- package/extensions/dashboard-client/src/types/vector-cortex.ts +16 -0
- package/extensions/dashboard-server/api-contracts/vector-cortex.ts +57 -0
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +12 -0
- package/extensions/dashboard-server/routes-vector-cortex-reconstruct.ts +41 -0
- package/extensions/dashboard-server/routes-vector-cortex-residual.ts +60 -0
- package/extensions/dashboard-server/routes-vector-cortex.ts +2 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +4 -0
- package/package.json +1 -1
- package/src/config/vector-cortex.ts +27 -0
- package/src/config.ts +2 -0
- package/src/vector-cortex/reconstruct/_acceptance-helpers.ts +455 -0
- package/src/vector-cortex/reconstruct/assemble.ts +170 -0
- package/src/vector-cortex/reconstruct/closure.ts +299 -0
- package/src/vector-cortex/reconstruct/types.ts +330 -0
- package/src/vector-cortex/reconstruct/validate.ts +184 -0
- package/src/vector-cortex/residual/codec.ts +292 -0
- package/src/vector-cortex/residual/dct.ts +166 -0
- package/src/vector-cortex/residual/fixture-payload.ts +90 -0
- package/src/vector-cortex/residual/gf256.ts +196 -0
- package/src/vector-cortex/residual/parity.ts +283 -0
- package/src/vector-cortex/residual/quantize.ts +230 -0
- package/src/vector-cortex/residual/stream.ts +135 -0
- package/src/vector-cortex/residual/types.ts +236 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BGe9hu3r.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BGe9hu3r.js.map +0 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/residual/parity.ts — Reed–Solomon (9,6) erasure parity over the
|
|
3
|
+
* protected stream (VC4B).
|
|
4
|
+
*
|
|
5
|
+
* Construction (RESIDUAL_CODEC §erasure parity): the protected stream is split
|
|
6
|
+
* into `k=6` equal, zero-padded data shards (the unpadded stream length is
|
|
7
|
+
* stored on every shard). A `9x6` Vandermonde matrix `V[r][c] = alpha_r^c` with
|
|
8
|
+
* evaluation points `alpha_r = r+1` is converted to a SYSTEMATIC generator
|
|
9
|
+
* `G = V x inverse(V[0..5, 0..5])`; rows 0..5 are then the identity (data shards
|
|
10
|
+
* pass through unchanged) and rows 6..8 produce the three parity shards in that
|
|
11
|
+
* order.
|
|
12
|
+
*
|
|
13
|
+
* Recovery: any 6 of the 9 shards reconstruct the stream. Every shard carries
|
|
14
|
+
* its own SHA-256, so an UNKNOWN corruption is DETECTED and can be promoted to a
|
|
15
|
+
* known erasure — it is never blindly error-corrected. More than 3 erasures
|
|
16
|
+
* fails closed (`RES_TOO_MANY_ERASURES`) to the exact source / mode C.
|
|
17
|
+
*
|
|
18
|
+
* Guardrails: local hashing only, no storage, no console, no network
|
|
19
|
+
* (PREVENT-PI-004 / PREVENT-011).
|
|
20
|
+
*/
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
import { gfAdd, gfAt, gfInvert, gfMatMul, gfMatrix, gfMul, gfPickRows, gfSet, gfSubRows, vandermonde, } from "./gf256.js";
|
|
23
|
+
import { RS_DATA_SHARDS, RS_PARITY_SHARDS, RS_TOTAL_SHARDS, } from "./types.js";
|
|
24
|
+
/** SHA-256 of a byte slice, lowercase hex. */
|
|
25
|
+
export function sha256Hex(bytes) {
|
|
26
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The systematic `9x6` generator `G = V x inverse(V[0..5,0..5])`. Built once:
|
|
30
|
+
* the matrix depends only on the fixed geometry and the fixed field.
|
|
31
|
+
*/
|
|
32
|
+
let cachedGenerator = null;
|
|
33
|
+
export function systematicGenerator() {
|
|
34
|
+
if (cachedGenerator)
|
|
35
|
+
return cachedGenerator;
|
|
36
|
+
const v = vandermonde(RS_TOTAL_SHARDS, RS_DATA_SHARDS);
|
|
37
|
+
const top = gfSubRows(v, 0, RS_DATA_SHARDS);
|
|
38
|
+
const topInv = gfInvert(top);
|
|
39
|
+
if (!topInv) {
|
|
40
|
+
// The fixed Vandermonde top square over GF(2^8) with distinct points 1..6
|
|
41
|
+
// is always invertible; this guard exists so a future geometry change fails
|
|
42
|
+
// loudly rather than silently producing a broken generator.
|
|
43
|
+
throw new Error("residual parity: singular Vandermonde top square");
|
|
44
|
+
}
|
|
45
|
+
cachedGenerator = gfMatMul(v, topInv);
|
|
46
|
+
return cachedGenerator;
|
|
47
|
+
}
|
|
48
|
+
/** Per-shard payload length for a stream of `streamLength` bytes. */
|
|
49
|
+
export function shardLength(streamLength) {
|
|
50
|
+
return Math.ceil(streamLength / RS_DATA_SHARDS);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Split the protected stream into 9 shards: 6 systematic data shards (equal
|
|
54
|
+
* length, final one zero-padded) followed by 3 parity shards.
|
|
55
|
+
*/
|
|
56
|
+
export function encodeShards(stream) {
|
|
57
|
+
const len = shardLength(stream.length);
|
|
58
|
+
const g = systematicGenerator();
|
|
59
|
+
const data = [];
|
|
60
|
+
for (let i = 0; i < RS_DATA_SHARDS; i++) {
|
|
61
|
+
const shard = new Uint8Array(len);
|
|
62
|
+
shard.set(stream.subarray(i * len, Math.min((i + 1) * len, stream.length)));
|
|
63
|
+
data.push(shard);
|
|
64
|
+
}
|
|
65
|
+
const shards = data.map((bytes, index) => ({
|
|
66
|
+
schema: "parity-shard-v1",
|
|
67
|
+
index,
|
|
68
|
+
kind: "data",
|
|
69
|
+
bytes,
|
|
70
|
+
digest: sha256Hex(bytes),
|
|
71
|
+
streamLength: stream.length,
|
|
72
|
+
}));
|
|
73
|
+
for (let p = 0; p < RS_PARITY_SHARDS; p++) {
|
|
74
|
+
const row = RS_DATA_SHARDS + p;
|
|
75
|
+
const bytes = new Uint8Array(len);
|
|
76
|
+
for (let c = 0; c < RS_DATA_SHARDS; c++) {
|
|
77
|
+
const coefficient = gfAt(g, row, c);
|
|
78
|
+
if (coefficient === 0)
|
|
79
|
+
continue;
|
|
80
|
+
const src = data[c];
|
|
81
|
+
for (let b = 0; b < len; b++) {
|
|
82
|
+
bytes[b] = gfAdd(bytes[b], gfMul(coefficient, src[b]));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
shards.push({
|
|
86
|
+
schema: "parity-shard-v1",
|
|
87
|
+
index: row,
|
|
88
|
+
kind: "parity",
|
|
89
|
+
bytes,
|
|
90
|
+
digest: sha256Hex(bytes),
|
|
91
|
+
streamLength: stream.length,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return shards;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Verify every supplied shard's SHA-256 and return the indices whose digest
|
|
98
|
+
* does NOT match. A corrupt shard is DETECTED here; the caller promotes it to a
|
|
99
|
+
* known erasure rather than attempting unknown-error correction.
|
|
100
|
+
*/
|
|
101
|
+
export function detectCorruptShards(shards) {
|
|
102
|
+
const corrupt = [];
|
|
103
|
+
for (const s of shards) {
|
|
104
|
+
if (sha256Hex(s.bytes) !== s.digest)
|
|
105
|
+
corrupt.push(s.index);
|
|
106
|
+
}
|
|
107
|
+
return corrupt.sort((a, b) => a - b);
|
|
108
|
+
}
|
|
109
|
+
/** Structural validation shared by recovery paths. */
|
|
110
|
+
function validateShards(shards) {
|
|
111
|
+
if (shards.length === 0)
|
|
112
|
+
return { ok: false, code: "RES_TOO_MANY_ERASURES" };
|
|
113
|
+
const seen = new Set();
|
|
114
|
+
const length = shards[0].bytes.length;
|
|
115
|
+
const streamLength = shards[0].streamLength;
|
|
116
|
+
for (const s of shards) {
|
|
117
|
+
if (s.index < 0 || s.index >= RS_TOTAL_SHARDS || !Number.isInteger(s.index)) {
|
|
118
|
+
return { ok: false, code: "RES_DUPLICATE_SHARD_INDEX" };
|
|
119
|
+
}
|
|
120
|
+
if (seen.has(s.index))
|
|
121
|
+
return { ok: false, code: "RES_DUPLICATE_SHARD_INDEX" };
|
|
122
|
+
seen.add(s.index);
|
|
123
|
+
if (s.bytes.length !== length) {
|
|
124
|
+
return { ok: false, code: "RES_SHARD_LENGTH_MISMATCH" };
|
|
125
|
+
}
|
|
126
|
+
if (s.streamLength !== streamLength) {
|
|
127
|
+
return { ok: false, code: "RES_SHARD_LENGTH_MISMATCH" };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (shardLength(streamLength) !== length) {
|
|
131
|
+
return { ok: false, code: "RES_SHARD_LENGTH_MISMATCH" };
|
|
132
|
+
}
|
|
133
|
+
return { ok: true, length, streamLength };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Reconstruct the protected stream from AT LEAST 6 surviving shards.
|
|
137
|
+
*
|
|
138
|
+
* Every supplied shard is digest-checked first: a shard whose SHA-256 does not
|
|
139
|
+
* match its recorded digest is treated as a KNOWN erasure (it is dropped from
|
|
140
|
+
* the surviving set), which is exactly how a corrupt parity shard becomes the
|
|
141
|
+
* third erasure in the sprint's failure-injection case. If fewer than 6 shards
|
|
142
|
+
* survive that check, recovery fails closed with `RES_TOO_MANY_ERASURES` and
|
|
143
|
+
* never attempts unknown-error correction.
|
|
144
|
+
*/
|
|
145
|
+
export function recoverStream(shards) {
|
|
146
|
+
const structural = validateShards(shards);
|
|
147
|
+
if (!structural.ok)
|
|
148
|
+
return structural;
|
|
149
|
+
const { length, streamLength } = structural;
|
|
150
|
+
// Digest check: promote every detected corruption to a known erasure.
|
|
151
|
+
const corrupt = new Set(detectCorruptShards(shards));
|
|
152
|
+
const survivors = shards
|
|
153
|
+
.filter((s) => !corrupt.has(s.index))
|
|
154
|
+
.slice()
|
|
155
|
+
.sort((a, b) => a.index - b.index);
|
|
156
|
+
if (survivors.length < RS_DATA_SHARDS) {
|
|
157
|
+
return { ok: false, code: "RES_TOO_MANY_ERASURES" };
|
|
158
|
+
}
|
|
159
|
+
// Deterministic selection: the six lowest surviving indices.
|
|
160
|
+
const chosen = survivors.slice(0, RS_DATA_SHARDS);
|
|
161
|
+
const g = systematicGenerator();
|
|
162
|
+
const sub = gfPickRows(g, chosen.map((s) => s.index));
|
|
163
|
+
const subInv = gfInvert(sub);
|
|
164
|
+
if (!subInv)
|
|
165
|
+
return { ok: false, code: "RES_SINGULAR_MATRIX" };
|
|
166
|
+
// Recover the six data shards: D = inverse(G_chosen) x S_chosen.
|
|
167
|
+
const recoveredData = [];
|
|
168
|
+
for (let r = 0; r < RS_DATA_SHARDS; r++) {
|
|
169
|
+
const out = new Uint8Array(length);
|
|
170
|
+
for (let c = 0; c < RS_DATA_SHARDS; c++) {
|
|
171
|
+
const coefficient = gfAt(subInv, r, c);
|
|
172
|
+
if (coefficient === 0)
|
|
173
|
+
continue;
|
|
174
|
+
const src = chosen[c].bytes;
|
|
175
|
+
for (let b = 0; b < length; b++) {
|
|
176
|
+
out[b] = gfAdd(out[b], gfMul(coefficient, src[b]));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
recoveredData.push(out);
|
|
180
|
+
}
|
|
181
|
+
const stream = new Uint8Array(streamLength);
|
|
182
|
+
for (let i = 0; i < RS_DATA_SHARDS; i++) {
|
|
183
|
+
const start = i * length;
|
|
184
|
+
if (start >= streamLength)
|
|
185
|
+
break;
|
|
186
|
+
const take = Math.min(length, streamLength - start);
|
|
187
|
+
stream.set(recoveredData[i].subarray(0, take), start);
|
|
188
|
+
}
|
|
189
|
+
const present = new Set(chosen.map((s) => s.index));
|
|
190
|
+
const recoveredIndices = [];
|
|
191
|
+
for (let i = 0; i < RS_TOTAL_SHARDS; i++)
|
|
192
|
+
if (!present.has(i))
|
|
193
|
+
recoveredIndices.push(i);
|
|
194
|
+
return { ok: true, stream, recoveredIndices };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Recover with an EXPLICIT erasure set: the named indices are treated as lost
|
|
198
|
+
* regardless of their digest. More than `m=3` marked erasures fails closed with
|
|
199
|
+
* `RES_TOO_MANY_ERASURES` before any matrix work — including the case where
|
|
200
|
+
* three data shards are marked AND a fourth (parity) shard is corrupt.
|
|
201
|
+
*/
|
|
202
|
+
export function recoverWithErasures(shards, erasedIndices) {
|
|
203
|
+
const erased = new Set(erasedIndices);
|
|
204
|
+
if (erased.size > RS_PARITY_SHARDS) {
|
|
205
|
+
return { ok: false, code: "RES_TOO_MANY_ERASURES" };
|
|
206
|
+
}
|
|
207
|
+
const kept = shards.filter((s) => !erased.has(s.index));
|
|
208
|
+
// A corruption among the KEPT shards is an additional known erasure; if that
|
|
209
|
+
// pushes the total past m=3 the recovery fails closed rather than attempting
|
|
210
|
+
// unknown-error correction.
|
|
211
|
+
const corrupt = detectCorruptShards(kept);
|
|
212
|
+
if (erased.size + corrupt.length > RS_PARITY_SHARDS) {
|
|
213
|
+
return { ok: false, code: "RES_TOO_MANY_ERASURES" };
|
|
214
|
+
}
|
|
215
|
+
return recoverStream(kept);
|
|
216
|
+
}
|
|
217
|
+
/** Rebuild the full 9-shard set from a recovered stream (repair path). */
|
|
218
|
+
export function repairShards(stream) {
|
|
219
|
+
return encodeShards(stream);
|
|
220
|
+
}
|
|
221
|
+
/** Test/diagnostic helper: the systematic generator's parity rows. */
|
|
222
|
+
export function parityRows() {
|
|
223
|
+
const g = systematicGenerator();
|
|
224
|
+
const rows = [];
|
|
225
|
+
for (let r = RS_DATA_SHARDS; r < RS_TOTAL_SHARDS; r++) {
|
|
226
|
+
const row = [];
|
|
227
|
+
for (let c = 0; c < RS_DATA_SHARDS; c++)
|
|
228
|
+
row.push(gfAt(g, r, c));
|
|
229
|
+
rows.push(row);
|
|
230
|
+
}
|
|
231
|
+
return rows;
|
|
232
|
+
}
|
|
233
|
+
/** Test helper: assert the generator's top square really is the identity. */
|
|
234
|
+
export function generatorIsSystematic() {
|
|
235
|
+
const g = systematicGenerator();
|
|
236
|
+
for (let r = 0; r < RS_DATA_SHARDS; r++) {
|
|
237
|
+
for (let c = 0; c < RS_DATA_SHARDS; c++) {
|
|
238
|
+
const expected = r === c ? 1 : 0;
|
|
239
|
+
if (gfAt(g, r, c) !== expected)
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
export { gfMatrix, gfSet };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/residual/quantize.ts — int16 coefficient quantization + the
|
|
3
|
+
* block-scoped exact correction stream (VC4B).
|
|
4
|
+
*
|
|
5
|
+
* Quantization (RESIDUAL_CODEC §byte scope): signed coefficients are quantized
|
|
6
|
+
* to int16 with a per-block float32 LE scale `max(abs(c))/32767` (an all-zero
|
|
7
|
+
* block has scale 0). Ties round to nearest-even; SATURATION REJECTS the
|
|
8
|
+
* encoding rather than clipping — a value that would land outside
|
|
9
|
+
* [-32767, 32767] returns `RES_QUANTIZE_RANGE`, as does any non-finite input.
|
|
10
|
+
* Dequantization multiplies back by the (float32-rounded) scale.
|
|
11
|
+
*
|
|
12
|
+
* Because quantization may not reproduce the bytes exactly, the encoder appends
|
|
13
|
+
* a block-scoped EXACT correction stream: for each block in ascending u32 LE
|
|
14
|
+
* `blockIndex`, a varint correction count followed by sorted
|
|
15
|
+
* `(u16 offsetWithinBlock, u8 original)` entries. Offsets are 0..4095, duplicate
|
|
16
|
+
* offsets are rejected, and omitted blocks have count zero. Applying the
|
|
17
|
+
* corrections makes post-quantization byte error exactly ZERO.
|
|
18
|
+
*
|
|
19
|
+
* Pure numeric/serialization logic: no storage, no console, no network
|
|
20
|
+
* (PREVENT-PI-004 / PREVENT-011).
|
|
21
|
+
*/
|
|
22
|
+
import { roundHalfToEven } from "./dct.js";
|
|
23
|
+
import { RESIDUAL_BLOCK_SIZE, } from "./types.js";
|
|
24
|
+
/** Int16 quantization limit (symmetric; -32768 is never produced). */
|
|
25
|
+
export const INT16_LIMIT = 32767;
|
|
26
|
+
/**
|
|
27
|
+
* Quantize one block's coefficients to int16 with a per-block float32 scale.
|
|
28
|
+
* Rejects non-finite inputs and any value that would saturate int16 with
|
|
29
|
+
* `RES_QUANTIZE_RANGE` (RESIDUAL_CODEC: saturation rejects encoding).
|
|
30
|
+
*/
|
|
31
|
+
export function quantizeBlock(coefficients) {
|
|
32
|
+
let peak = 0;
|
|
33
|
+
for (let i = 0; i < coefficients.length; i++) {
|
|
34
|
+
const c = coefficients[i];
|
|
35
|
+
if (!Number.isFinite(c))
|
|
36
|
+
return { ok: false, code: "RES_QUANTIZE_RANGE" };
|
|
37
|
+
const a = Math.abs(c);
|
|
38
|
+
if (a > peak)
|
|
39
|
+
peak = a;
|
|
40
|
+
}
|
|
41
|
+
const out = new Int16Array(coefficients.length);
|
|
42
|
+
if (peak === 0) {
|
|
43
|
+
// All-zero block: scale is exactly 0 and every coefficient stays 0.
|
|
44
|
+
return { ok: true, block: { scale: 0, coefficients: out } };
|
|
45
|
+
}
|
|
46
|
+
// The persisted scale is a float32 — quantize/dequantize with the SAME
|
|
47
|
+
// float32 value the artifact stores so encode and decode agree bit-for-bit.
|
|
48
|
+
const scale = Math.fround(peak / INT16_LIMIT);
|
|
49
|
+
if (!Number.isFinite(scale) || scale <= 0) {
|
|
50
|
+
return { ok: false, code: "RES_QUANTIZE_RANGE" };
|
|
51
|
+
}
|
|
52
|
+
for (let i = 0; i < coefficients.length; i++) {
|
|
53
|
+
const q = roundHalfToEven(coefficients[i] / scale);
|
|
54
|
+
if (q > INT16_LIMIT || q < -INT16_LIMIT) {
|
|
55
|
+
return { ok: false, code: "RES_QUANTIZE_RANGE" };
|
|
56
|
+
}
|
|
57
|
+
out[i] = q;
|
|
58
|
+
}
|
|
59
|
+
return { ok: true, block: { scale, coefficients: out } };
|
|
60
|
+
}
|
|
61
|
+
/** Dequantize a block back to float coefficients (`coefficient * scale`). */
|
|
62
|
+
export function dequantizeBlock(block) {
|
|
63
|
+
const out = new Float64Array(block.coefficients.length);
|
|
64
|
+
for (let i = 0; i < block.coefficients.length; i++) {
|
|
65
|
+
out[i] = block.coefficients[i] * block.scale;
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Diff a reconstructed block against the original, producing the sorted
|
|
71
|
+
* correction entries for that block (empty when the reconstruction is exact).
|
|
72
|
+
*/
|
|
73
|
+
export function diffBlock(original, reconstructed) {
|
|
74
|
+
const out = [];
|
|
75
|
+
for (let i = 0; i < original.length; i++) {
|
|
76
|
+
if (original[i] !== reconstructed[i]) {
|
|
77
|
+
out.push({ offset: i, original: original[i] });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/** Apply a block's corrections in place over a reconstructed block. */
|
|
83
|
+
export function applyCorrections(reconstructed, corrections) {
|
|
84
|
+
let previous = -1;
|
|
85
|
+
for (const c of corrections) {
|
|
86
|
+
if (!Number.isInteger(c.offset) || c.offset < 0 || c.offset >= reconstructed.length) {
|
|
87
|
+
return { ok: false, code: "RES_CORRECTION_RANGE" };
|
|
88
|
+
}
|
|
89
|
+
if (!Number.isInteger(c.original) || c.original < 0 || c.original > 255) {
|
|
90
|
+
return { ok: false, code: "RES_CORRECTION_RANGE" };
|
|
91
|
+
}
|
|
92
|
+
// Entries are sorted ascending with no duplicate offset.
|
|
93
|
+
if (c.offset === previous) {
|
|
94
|
+
return { ok: false, code: "RES_CORRECTION_DUPLICATE_OFFSET" };
|
|
95
|
+
}
|
|
96
|
+
if (c.offset < previous)
|
|
97
|
+
return { ok: false, code: "RES_CORRECTION_RANGE" };
|
|
98
|
+
previous = c.offset;
|
|
99
|
+
reconstructed[c.offset] = c.original;
|
|
100
|
+
}
|
|
101
|
+
return { ok: true };
|
|
102
|
+
}
|
|
103
|
+
// ── varint (LEB128, unsigned) ───────────────────────────────────────────────
|
|
104
|
+
/** Encode an unsigned integer as LEB128 varint bytes. */
|
|
105
|
+
export function encodeVarint(value) {
|
|
106
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
107
|
+
throw new RangeError("varint requires a non-negative integer");
|
|
108
|
+
}
|
|
109
|
+
const out = [];
|
|
110
|
+
let v = value;
|
|
111
|
+
do {
|
|
112
|
+
let byte = v & 0x7f;
|
|
113
|
+
v = Math.floor(v / 128);
|
|
114
|
+
if (v > 0)
|
|
115
|
+
byte |= 0x80;
|
|
116
|
+
out.push(byte);
|
|
117
|
+
} while (v > 0);
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
/** Decode an LEB128 varint at `offset`; returns the value and the next offset. */
|
|
121
|
+
export function decodeVarint(bytes, offset) {
|
|
122
|
+
let value = 0;
|
|
123
|
+
let shift = 1;
|
|
124
|
+
let i = offset;
|
|
125
|
+
for (; i < bytes.length; i++) {
|
|
126
|
+
const byte = bytes[i];
|
|
127
|
+
value += (byte & 0x7f) * shift;
|
|
128
|
+
if ((byte & 0x80) === 0)
|
|
129
|
+
return { value, next: i + 1 };
|
|
130
|
+
shift *= 128;
|
|
131
|
+
if (shift > Number.MAX_SAFE_INTEGER)
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
// ── correction stream serialization ─────────────────────────────────────────
|
|
137
|
+
/**
|
|
138
|
+
* Serialize the block-scoped correction stream: a varint block count, then for
|
|
139
|
+
* each NON-EMPTY block in ascending order a u32 LE blockIndex, a varint
|
|
140
|
+
* correction count, and the sorted `(u16 LE offset, u8 original)` entries.
|
|
141
|
+
* Omitted blocks have an implicit count of zero.
|
|
142
|
+
*/
|
|
143
|
+
export function serializeCorrections(blocks) {
|
|
144
|
+
const present = blocks
|
|
145
|
+
.filter((b) => b.corrections.length > 0)
|
|
146
|
+
.slice()
|
|
147
|
+
.sort((a, b) => a.blockIndex - b.blockIndex);
|
|
148
|
+
const out = [];
|
|
149
|
+
out.push(...encodeVarint(present.length));
|
|
150
|
+
for (const b of present) {
|
|
151
|
+
out.push(b.blockIndex & 0xff, (b.blockIndex >>> 8) & 0xff, (b.blockIndex >>> 16) & 0xff, (b.blockIndex >>> 24) & 0xff);
|
|
152
|
+
out.push(...encodeVarint(b.corrections.length));
|
|
153
|
+
for (const c of b.corrections) {
|
|
154
|
+
out.push(c.offset & 0xff, (c.offset >>> 8) & 0xff, c.original & 0xff);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return Uint8Array.from(out);
|
|
158
|
+
}
|
|
159
|
+
/** Parse a serialized correction stream. Returns null on any malformed input. */
|
|
160
|
+
export function parseCorrections(bytes, offset) {
|
|
161
|
+
const head = decodeVarint(bytes, offset);
|
|
162
|
+
if (!head)
|
|
163
|
+
return null;
|
|
164
|
+
let pos = head.next;
|
|
165
|
+
const blocks = [];
|
|
166
|
+
let previousIndex = -1;
|
|
167
|
+
for (let b = 0; b < head.value; b++) {
|
|
168
|
+
if (pos + 4 > bytes.length)
|
|
169
|
+
return null;
|
|
170
|
+
const blockIndex = bytes[pos] |
|
|
171
|
+
(bytes[pos + 1] << 8) |
|
|
172
|
+
(bytes[pos + 2] << 16) |
|
|
173
|
+
(bytes[pos + 3] << 24);
|
|
174
|
+
pos += 4;
|
|
175
|
+
if (blockIndex < 0 || blockIndex <= previousIndex)
|
|
176
|
+
return null;
|
|
177
|
+
previousIndex = blockIndex;
|
|
178
|
+
const count = decodeVarint(bytes, pos);
|
|
179
|
+
if (!count)
|
|
180
|
+
return null;
|
|
181
|
+
pos = count.next;
|
|
182
|
+
const corrections = [];
|
|
183
|
+
let previousOffset = -1;
|
|
184
|
+
for (let i = 0; i < count.value; i++) {
|
|
185
|
+
if (pos + 3 > bytes.length)
|
|
186
|
+
return null;
|
|
187
|
+
const off = bytes[pos] | (bytes[pos + 1] << 8);
|
|
188
|
+
const original = bytes[pos + 2];
|
|
189
|
+
pos += 3;
|
|
190
|
+
if (off >= RESIDUAL_BLOCK_SIZE)
|
|
191
|
+
return null;
|
|
192
|
+
if (off <= previousOffset)
|
|
193
|
+
return null; // duplicate or unsorted
|
|
194
|
+
previousOffset = off;
|
|
195
|
+
corrections.push({ offset: off, original });
|
|
196
|
+
}
|
|
197
|
+
blocks.push({ blockIndex, corrections });
|
|
198
|
+
}
|
|
199
|
+
return { blocks, next: pos };
|
|
200
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/residual/stream.ts — canonical PROTECTED STREAM serialization
|
|
3
|
+
* for the VC4B residual codec.
|
|
4
|
+
*
|
|
5
|
+
* The protected stream is `header + all block scales/coefficient arrays +
|
|
6
|
+
* corrections` (RESIDUAL_CODEC §erasure parity), serialized in exactly this
|
|
7
|
+
* canonical order so encode and decode are byte-symmetric:
|
|
8
|
+
*
|
|
9
|
+
* header (46 bytes) magic `VCR1` | u32 LE originalLength | 32-byte SHA-256
|
|
10
|
+
* | u16 LE blockSize | u16 LE k | u16 LE m
|
|
11
|
+
* blocks for each block in ascending index:
|
|
12
|
+
* float32 LE scale | 4096 * int16 LE coefficient
|
|
13
|
+
* corrections varint blockCount, then per non-empty block
|
|
14
|
+
* u32 LE blockIndex | varint count
|
|
15
|
+
* | count * (u16 LE offset, u8 original)
|
|
16
|
+
*
|
|
17
|
+
* Pure serialization: no storage, no console, no network (PREVENT-PI-004 /
|
|
18
|
+
* PREVENT-011).
|
|
19
|
+
*/
|
|
20
|
+
import { parseCorrections, serializeCorrections, } from "./quantize.js";
|
|
21
|
+
import { RESIDUAL_BLOCK_SIZE, RESIDUAL_HEADER_BYTES, RESIDUAL_MAGIC, RS_DATA_SHARDS, RS_PARITY_SHARDS, } from "./types.js";
|
|
22
|
+
/** Per-block serialized size: float32 scale + n int16 coefficients. */
|
|
23
|
+
export function blockBytes(blockSize = RESIDUAL_BLOCK_SIZE) {
|
|
24
|
+
return 4 + blockSize * 2;
|
|
25
|
+
}
|
|
26
|
+
/** Serialize the canonical 46-byte header. */
|
|
27
|
+
export function serializeHeader(header) {
|
|
28
|
+
const out = new Uint8Array(RESIDUAL_HEADER_BYTES);
|
|
29
|
+
const view = new DataView(out.buffer);
|
|
30
|
+
for (let i = 0; i < 4; i++)
|
|
31
|
+
out[i] = RESIDUAL_MAGIC.charCodeAt(i);
|
|
32
|
+
view.setUint32(4, header.originalLength, true);
|
|
33
|
+
const digest = Buffer.from(header.payloadDigest, "hex");
|
|
34
|
+
out.set(digest.subarray(0, 32), 8);
|
|
35
|
+
view.setUint16(40, header.blockSize, true);
|
|
36
|
+
view.setUint16(42, header.dataShards, true);
|
|
37
|
+
view.setUint16(44, header.parityShards, true);
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/** Parse the canonical header. Returns null on magic/geometry mismatch. */
|
|
41
|
+
export function parseHeader(bytes) {
|
|
42
|
+
if (bytes.length < RESIDUAL_HEADER_BYTES)
|
|
43
|
+
return null;
|
|
44
|
+
for (let i = 0; i < 4; i++) {
|
|
45
|
+
if (bytes[i] !== RESIDUAL_MAGIC.charCodeAt(i))
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
49
|
+
const originalLength = view.getUint32(4, true);
|
|
50
|
+
const payloadDigest = Buffer.from(bytes.subarray(8, 40)).toString("hex");
|
|
51
|
+
const blockSize = view.getUint16(40, true);
|
|
52
|
+
const dataShards = view.getUint16(42, true);
|
|
53
|
+
const parityShards = view.getUint16(44, true);
|
|
54
|
+
if (blockSize !== RESIDUAL_BLOCK_SIZE)
|
|
55
|
+
return null;
|
|
56
|
+
if (dataShards !== RS_DATA_SHARDS || parityShards !== RS_PARITY_SHARDS)
|
|
57
|
+
return null;
|
|
58
|
+
return {
|
|
59
|
+
magic: RESIDUAL_MAGIC,
|
|
60
|
+
originalLength,
|
|
61
|
+
payloadDigest,
|
|
62
|
+
blockSize: RESIDUAL_BLOCK_SIZE,
|
|
63
|
+
dataShards: RS_DATA_SHARDS,
|
|
64
|
+
parityShards: RS_PARITY_SHARDS,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Serialize the full protected stream for one encoded artifact. */
|
|
68
|
+
export function serializeStream(codec) {
|
|
69
|
+
const head = serializeHeader(codec.header);
|
|
70
|
+
const perBlock = blockBytes(codec.header.blockSize);
|
|
71
|
+
const corrections = serializeCorrections(codec.corrections);
|
|
72
|
+
const out = new Uint8Array(head.length + codec.blocks.length * perBlock + corrections.length);
|
|
73
|
+
out.set(head, 0);
|
|
74
|
+
const view = new DataView(out.buffer);
|
|
75
|
+
let pos = head.length;
|
|
76
|
+
for (const block of codec.blocks) {
|
|
77
|
+
view.setFloat32(pos, block.scale, true);
|
|
78
|
+
pos += 4;
|
|
79
|
+
for (let i = 0; i < block.coefficients.length; i++) {
|
|
80
|
+
view.setInt16(pos, block.coefficients[i], true);
|
|
81
|
+
pos += 2;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
out.set(corrections, pos);
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
/** Parse a protected stream back into the codec artifact. Null on malformed. */
|
|
88
|
+
export function parseStream(stream) {
|
|
89
|
+
const header = parseHeader(stream);
|
|
90
|
+
if (!header)
|
|
91
|
+
return null;
|
|
92
|
+
const perBlock = blockBytes(header.blockSize);
|
|
93
|
+
const blockCount = Math.ceil(header.originalLength / header.blockSize);
|
|
94
|
+
const bodyEnd = RESIDUAL_HEADER_BYTES + blockCount * perBlock;
|
|
95
|
+
if (stream.length < bodyEnd)
|
|
96
|
+
return null;
|
|
97
|
+
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
98
|
+
const blocks = [];
|
|
99
|
+
let pos = RESIDUAL_HEADER_BYTES;
|
|
100
|
+
for (let b = 0; b < blockCount; b++) {
|
|
101
|
+
const scale = view.getFloat32(pos, true);
|
|
102
|
+
pos += 4;
|
|
103
|
+
const coefficients = new Int16Array(header.blockSize);
|
|
104
|
+
for (let i = 0; i < header.blockSize; i++) {
|
|
105
|
+
coefficients[i] = view.getInt16(pos, true);
|
|
106
|
+
pos += 2;
|
|
107
|
+
}
|
|
108
|
+
blocks.push({ scale, coefficients });
|
|
109
|
+
}
|
|
110
|
+
const parsed = parseCorrections(stream, pos);
|
|
111
|
+
if (!parsed)
|
|
112
|
+
return null;
|
|
113
|
+
// Every correction must name a block that exists.
|
|
114
|
+
for (const block of parsed.blocks) {
|
|
115
|
+
if (block.blockIndex >= blockCount)
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
schema: "residual-codec-v1",
|
|
120
|
+
header,
|
|
121
|
+
blocks,
|
|
122
|
+
corrections: parsed.blocks,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/residual/types.ts — reversible residual payload codec (VC4B).
|
|
3
|
+
*
|
|
4
|
+
* Owns `ResidualCodecV1` / `ParityShardV1` — the contract of the sprint failure
|
|
5
|
+
* triad:
|
|
6
|
+
*
|
|
7
|
+
* A = admitted residual (DCT + int16 + exact corrections) + RS(9,6) parity;
|
|
8
|
+
* B = exact compressed bytes (forced when the >95% accounting rejects A);
|
|
9
|
+
* C = ledger bytes (forced when A/B decode fails).
|
|
10
|
+
*
|
|
11
|
+
* Semantic vectors NEVER claim to recover exact text. Exact bytes come only from
|
|
12
|
+
* exact payload shards (VC4A `ExactShardV1`) or this REVERSIBLE codec: a block
|
|
13
|
+
* DCT-II analysis, int16 quantization, and a block-scoped exact correction
|
|
14
|
+
* stream that makes post-decode byte error exactly zero for admitted artifacts.
|
|
15
|
+
* Numeric erasure parity protects the codec bytes; it is not a substitute for
|
|
16
|
+
* the exact payload (RESIDUAL_CODEC.md).
|
|
17
|
+
*
|
|
18
|
+
* Consumes only reviewer-accepted predecessor contracts (VC1A EventV2 bytes via
|
|
19
|
+
* VC4A shard ranges) and the common contracts. Pure types + registered
|
|
20
|
+
* conformance IDs: no storage, no console, no network (PREVENT-PI-004 /
|
|
21
|
+
* PREVENT-011).
|
|
22
|
+
*/
|
|
23
|
+
/** Fixed transform block length (RESIDUAL_CODEC §byte scope: 4096). */
|
|
24
|
+
export const RESIDUAL_BLOCK_SIZE = 4096;
|
|
25
|
+
/** Reed–Solomon shard geometry: k=6 data shards, m=3 parity shards. */
|
|
26
|
+
export const RS_DATA_SHARDS = 6;
|
|
27
|
+
export const RS_PARITY_SHARDS = 3;
|
|
28
|
+
export const RS_TOTAL_SHARDS = RS_DATA_SHARDS + RS_PARITY_SHARDS;
|
|
29
|
+
/** GF(2^8) primitive polynomial for the parity field (0x11d). */
|
|
30
|
+
export const GF_PRIMITIVE_POLYNOMIAL = 0x11d;
|
|
31
|
+
/** Header magic bytes `VCR1`. */
|
|
32
|
+
export const RESIDUAL_MAGIC = "VCR1";
|
|
33
|
+
/**
|
|
34
|
+
* Admission ratio: residual is admitted only when its FULL encoded size is at
|
|
35
|
+
* most `floor(0.95 * exactCompressedSize)` (RESIDUAL_CODEC §admission). The
|
|
36
|
+
* accounting counts every persisted byte — header, scales, coefficients,
|
|
37
|
+
* corrections, shard metadata, all 9 shards, and digests.
|
|
38
|
+
*/
|
|
39
|
+
export const ADMISSION_NUMERATOR = 95;
|
|
40
|
+
export const ADMISSION_DENOMINATOR = 100;
|
|
41
|
+
/** Serialized header byte length: 4 + 4 + 32 + 2 + 2 + 2. */
|
|
42
|
+
export const RESIDUAL_HEADER_BYTES = 46;
|
|
43
|
+
/**
|
|
44
|
+
* Registered RES conformance ID range (RES-001..050). The acceptance test reads
|
|
45
|
+
* these rows from the v2 manifest and asserts each returns its manifest
|
|
46
|
+
* `ok`/`code`. The three named assertions (RES-DCT-001 / RES-RS-002 /
|
|
47
|
+
* RES-ADMIT-003) live alongside them.
|
|
48
|
+
*/
|
|
49
|
+
export const RES_IDS = Array.from({ length: 50 }, (_v, i) => `RES-${String(i + 1).padStart(3, "0")}`);
|
|
50
|
+
/** Named RES conformance assertions (the sprint's headline rows). */
|
|
51
|
+
export const RES_NAMED_IDS = [
|
|
52
|
+
"RES-DCT-001",
|
|
53
|
+
"RES-RS-002",
|
|
54
|
+
"RES-ADMIT-003",
|
|
55
|
+
];
|