qlm-measure 0.2.1 → 0.2.3
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/README.md +24 -139
- package/dist/checks.d.ts +21 -0
- package/dist/checks.d.ts.map +1 -0
- package/dist/checks.js +25 -0
- package/dist/checks.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +314 -0
- package/dist/cli.js.map +1 -0
- package/dist/clients/engine-client.d.ts +1 -1
- package/dist/clients/engine-client.d.ts.map +1 -1
- package/dist/clients/engine-client.js +1 -1
- package/dist/clients/engine-client.js.map +1 -1
- package/dist/clients/index.d.ts +4 -4
- package/dist/clients/index.d.ts.map +1 -1
- package/dist/clients/index.js +2 -2
- package/dist/clients/index.js.map +1 -1
- package/dist/clients/ontology-client.d.ts +10 -0
- package/dist/clients/ontology-client.d.ts.map +1 -1
- package/dist/clients/ontology-client.js +4 -0
- package/dist/clients/ontology-client.js.map +1 -1
- package/dist/emitter/index.d.ts +2 -2
- package/dist/emitter/index.d.ts.map +1 -1
- package/dist/emitter/index.js +1 -1
- package/dist/emitter/index.js.map +1 -1
- package/dist/emitter/measurement-emitter.d.ts +1 -1
- package/dist/emitter/measurement-emitter.d.ts.map +1 -1
- package/dist/index.d.ts +23 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -8
- package/dist/index.js.map +1 -1
- package/dist/recorder.d.ts +33 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +129 -0
- package/dist/recorder.js.map +1 -0
- package/dist/rules.d.ts +10 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +46 -0
- package/dist/rules.js.map +1 -0
- package/dist/schema/evidence-record.d.ts +2 -2
- package/dist/schema/evidence-record.d.ts.map +1 -1
- package/dist/schema/index.d.ts +6 -6
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -2
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/measurement-event.d.ts +1 -1
- package/dist/schema/measurement-event.d.ts.map +1 -1
- package/dist/schema/observation-event.d.ts +1 -1
- package/dist/schema/observation-event.d.ts.map +1 -1
- package/dist/verifier/hash.d.ts +6 -7
- package/dist/verifier/hash.d.ts.map +1 -1
- package/dist/verifier/hash.js +143 -39
- package/dist/verifier/hash.js.map +1 -1
- package/dist/verifier/index.d.ts +4 -4
- package/dist/verifier/index.d.ts.map +1 -1
- package/dist/verifier/index.js +3 -3
- package/dist/verifier/index.js.map +1 -1
- package/dist/verifier/replay.d.ts +1 -1
- package/dist/verifier/replay.d.ts.map +1 -1
- package/dist/verifier/verify-record.d.ts +4 -30
- package/dist/verifier/verify-record.d.ts.map +1 -1
- package/dist/verifier/verify-record.js +44 -83
- package/dist/verifier/verify-record.js.map +1 -1
- package/dist/verifier-v03.d.ts +24 -0
- package/dist/verifier-v03.d.ts.map +1 -0
- package/dist/verifier-v03.js +205 -0
- package/dist/verifier-v03.js.map +1 -0
- package/package.json +5 -1
package/dist/verifier/hash.js
CHANGED
|
@@ -1,73 +1,177 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hash utilities for evidence record integrity.
|
|
3
3
|
*
|
|
4
|
-
* Uses SHA-256 via
|
|
4
|
+
* Uses SHA-256 via Node's crypto module (ESM import).
|
|
5
5
|
* Canonical serialization ensures deterministic hashing.
|
|
6
6
|
*/
|
|
7
|
+
import { createHash } from "crypto";
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Format a number to match Python's json.dumps output.
|
|
10
|
+
*
|
|
11
|
+
* Python's json.dumps uses different formatting for int vs float:
|
|
12
|
+
* int 9200 → "9200" (no decimal point)
|
|
13
|
+
* float 1.0 → "1.0" (always has decimal point)
|
|
14
|
+
* float 3e-05 → "3e-05" (2-digit exponent minimum)
|
|
15
|
+
*
|
|
16
|
+
* JS has no int/float distinction. We use Number.isInteger() as the
|
|
17
|
+
* heuristic: if the JSON source had no decimal point, Python stored
|
|
18
|
+
* it as int and wrote it without .0.
|
|
19
|
+
*
|
|
20
|
+
* For the entry-hash hashable subset, only evidentialWeight,
|
|
21
|
+
* priorPosterior, and updatedPosterior are Python floats. version is
|
|
22
|
+
* an int. Everything inside event follows Python's own int/float typing
|
|
23
|
+
* from the original JSON (which round-trips through JSON.parse identically).
|
|
11
24
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Format a number to match Python's json.dumps output.
|
|
27
|
+
*
|
|
28
|
+
* Python uses the shortest decimal representation that round-trips
|
|
29
|
+
* (David Gay's dtoa algorithm). JS's Number.toString() also uses dtoa
|
|
30
|
+
* but sometimes produces longer representations with floating-point noise.
|
|
31
|
+
*
|
|
32
|
+
* We match Python by finding the shortest representation that round-trips.
|
|
33
|
+
*/
|
|
34
|
+
function pyNumberFormat(n) {
|
|
35
|
+
if (Object.is(n, -0))
|
|
36
|
+
return "-0.0";
|
|
37
|
+
if (!Number.isFinite(n))
|
|
38
|
+
return String(n);
|
|
39
|
+
// Python int path
|
|
40
|
+
if (Number.isInteger(n) && Math.abs(n) < Number.MAX_SAFE_INTEGER) {
|
|
41
|
+
return n.toString();
|
|
42
|
+
}
|
|
43
|
+
// Find the shortest decimal representation that round-trips
|
|
44
|
+
// This matches Python's float.__repr__ (shortest via dtoa)
|
|
45
|
+
let best = JSON.stringify(n);
|
|
46
|
+
// Try progressively shorter representations
|
|
47
|
+
for (let prec = 17; prec >= 1; prec--) {
|
|
48
|
+
const candidate = n.toPrecision(prec);
|
|
49
|
+
if (parseFloat(candidate) === n) {
|
|
50
|
+
best = candidate;
|
|
19
51
|
}
|
|
20
|
-
|
|
52
|
+
else {
|
|
53
|
+
break; // once it stops round-tripping, shorter won't work either
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Remove unnecessary trailing zeros and decimal point
|
|
57
|
+
// but keep at least one digit after decimal for floats
|
|
58
|
+
if (best.includes(".") && !best.includes("e") && !best.includes("E")) {
|
|
59
|
+
best = best.replace(/0+$/, "");
|
|
60
|
+
if (best.endsWith("."))
|
|
61
|
+
best += "0"; // keep the .0 for e.g. "1.0"
|
|
62
|
+
}
|
|
63
|
+
// Python uses scientific notation for abs < 1e-4
|
|
64
|
+
const abs = Math.abs(n);
|
|
65
|
+
if (abs > 0 && abs < 1e-4) {
|
|
66
|
+
// Force scientific notation matching Python
|
|
67
|
+
if (!best.includes("e") && !best.includes("E")) {
|
|
68
|
+
const exp = Math.floor(Math.log10(abs));
|
|
69
|
+
const mantissa = n / Math.pow(10, exp);
|
|
70
|
+
// Use shortest mantissa
|
|
71
|
+
let mBest = String(mantissa);
|
|
72
|
+
for (let p = 17; p >= 1; p--) {
|
|
73
|
+
const mc = mantissa.toPrecision(p);
|
|
74
|
+
if (parseFloat(mc) * Math.pow(10, exp) === n) {
|
|
75
|
+
mBest = mc;
|
|
76
|
+
}
|
|
77
|
+
else
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
// Clean mantissa
|
|
81
|
+
if (mBest.includes(".")) {
|
|
82
|
+
mBest = mBest.replace(/0+$/, "").replace(/\.$/, "");
|
|
83
|
+
}
|
|
84
|
+
best = `${mBest}e-${String(-exp).padStart(2, "0")}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Fix exponent padding: e-7 → e-07, e+2 → e+02
|
|
88
|
+
best = best.replace(/e([+-])(\d)$/, "e$10$2");
|
|
89
|
+
return best;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Format a string to match Python's json.dumps output.
|
|
93
|
+
* Python escapes all non-ASCII characters to \\uXXXX by default.
|
|
94
|
+
* JS JSON.stringify passes them through as UTF-8.
|
|
95
|
+
*/
|
|
96
|
+
function pyStringFormat(s) {
|
|
97
|
+
let result = JSON.stringify(s);
|
|
98
|
+
// Replace non-ASCII characters with \\uXXXX escapes, matching Python
|
|
99
|
+
result = result.replace(/[\u0080-\uffff]/g, (ch) => {
|
|
100
|
+
return "\\u" + ch.charCodeAt(0).toString(16).padStart(4, "0");
|
|
21
101
|
});
|
|
102
|
+
return result;
|
|
22
103
|
}
|
|
23
104
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
105
|
+
* Canonical JSON serialization — sorted keys, no whitespace.
|
|
106
|
+
* Matches Python's json.dumps(sort_keys=True, separators=(",",":"), default=str)
|
|
107
|
+
* byte-for-byte, including number and string formatting.
|
|
26
108
|
*/
|
|
27
|
-
export function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return
|
|
109
|
+
export function canonicalize(obj) {
|
|
110
|
+
return _canonicalizeValue(obj);
|
|
111
|
+
}
|
|
112
|
+
function _canonicalizeValue(val) {
|
|
113
|
+
if (val === null)
|
|
114
|
+
return "null";
|
|
115
|
+
if (val === undefined)
|
|
116
|
+
return "null";
|
|
117
|
+
if (typeof val === "boolean")
|
|
118
|
+
return val ? "true" : "false";
|
|
119
|
+
if (typeof val === "number")
|
|
120
|
+
return pyNumberFormat(val);
|
|
121
|
+
if (typeof val === "string")
|
|
122
|
+
return pyStringFormat(val);
|
|
123
|
+
if (Array.isArray(val)) {
|
|
124
|
+
return "[" + val.map(_canonicalizeValue).join(",") + "]";
|
|
33
125
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
126
|
+
if (typeof val === "object") {
|
|
127
|
+
const keys = Object.keys(val).sort();
|
|
128
|
+
const pairs = keys.map(k => {
|
|
129
|
+
const v = val[k];
|
|
130
|
+
return JSON.stringify(k) + ":" + _canonicalizeValue(v);
|
|
131
|
+
});
|
|
132
|
+
return "{" + pairs.join(",") + "}";
|
|
38
133
|
}
|
|
134
|
+
return JSON.stringify(val);
|
|
39
135
|
}
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
hash = ((hash << 5) - hash + char) | 0;
|
|
46
|
-
}
|
|
47
|
-
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
136
|
+
/**
|
|
137
|
+
* Compute SHA-256 hash of a string.
|
|
138
|
+
*/
|
|
139
|
+
export function sha256(input) {
|
|
140
|
+
return createHash("sha256").update(input).digest("hex");
|
|
48
141
|
}
|
|
49
142
|
/**
|
|
50
143
|
* Compute the canonical hash of an evidence entry.
|
|
51
|
-
*
|
|
144
|
+
* Every field must be explicitly null (not undefined) to match Python.
|
|
52
145
|
*/
|
|
53
146
|
export function hashEntry(entry) {
|
|
54
147
|
const hashable = {
|
|
55
148
|
version: entry.version,
|
|
56
149
|
timestamp: entry.timestamp,
|
|
57
150
|
event: entry.event,
|
|
58
|
-
scaffoldType: entry.scaffoldType,
|
|
59
|
-
depthLevel: entry.depthLevel,
|
|
60
|
-
epistemicMode: entry.epistemicMode,
|
|
61
|
-
triageResult: entry.triageResult,
|
|
151
|
+
scaffoldType: entry.scaffoldType ?? null,
|
|
152
|
+
depthLevel: entry.depthLevel ?? null,
|
|
153
|
+
epistemicMode: entry.epistemicMode ?? null,
|
|
154
|
+
triageResult: entry.triageResult ?? null,
|
|
62
155
|
evidentialWeight: entry.evidentialWeight,
|
|
63
156
|
priorPosterior: entry.priorPosterior,
|
|
64
157
|
updatedPosterior: entry.updatedPosterior,
|
|
65
158
|
nonInterventionDecision: entry.nonInterventionDecision ?? null,
|
|
66
159
|
};
|
|
67
|
-
|
|
160
|
+
// Build canonical, then fix the three Python-float fields:
|
|
161
|
+
// evidentialWeight, priorPosterior, updatedPosterior are Python floats.
|
|
162
|
+
// Python writes 1.0 for float(1), but JS Number.isInteger(1) returns true
|
|
163
|
+
// and pyNumberFormat writes "1". Fix by post-processing these three fields.
|
|
164
|
+
let canonical = _canonicalizeValue(hashable);
|
|
165
|
+
for (const field of ["evidentialWeight", "priorPosterior", "updatedPosterior"]) {
|
|
166
|
+
const val = hashable[field];
|
|
167
|
+
if (typeof val === "number" && Number.isInteger(val)) {
|
|
168
|
+
canonical = canonical.replace(new RegExp(`"${field}":(-?\\d+)(?=[,}\\]])`), `"${field}":$1.0`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return sha256(canonical);
|
|
68
172
|
}
|
|
69
173
|
/**
|
|
70
|
-
* Compute a hash over a canonical string
|
|
174
|
+
* Compute a hash over a canonical string.
|
|
71
175
|
*/
|
|
72
176
|
export function hashCanonical(input) {
|
|
73
177
|
return sha256(input);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/verifier/hash.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/verifier/hash.ts"],"names":[],"mappings":"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"}
|
package/dist/verifier/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { verifyRecord } from "./verify-record";
|
|
2
|
-
export type { VerificationResult, Violation } from "./verify-record";
|
|
3
|
-
export { replayToVersion, posteriorAtVersion, summarizeToVersion } from "./replay";
|
|
4
|
-
export { hashEntry, hashCanonical, canonicalize, sha256 } from "./hash";
|
|
1
|
+
export { verifyRecord } from "./verify-record.js";
|
|
2
|
+
export type { VerificationResult, Violation } from "./verify-record.js";
|
|
3
|
+
export { replayToVersion, posteriorAtVersion, summarizeToVersion } from "./replay.js";
|
|
4
|
+
export { hashEntry, hashCanonical, canonicalize, sha256 } from "./hash.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/verifier/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/verifier/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/verifier/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { verifyRecord } from "./verify-record";
|
|
2
|
-
export { replayToVersion, posteriorAtVersion, summarizeToVersion } from "./replay";
|
|
3
|
-
export { hashEntry, hashCanonical, canonicalize, sha256 } from "./hash";
|
|
1
|
+
export { verifyRecord } from "./verify-record.js";
|
|
2
|
+
export { replayToVersion, posteriorAtVersion, summarizeToVersion } from "./replay.js";
|
|
3
|
+
export { hashEntry, hashCanonical, canonicalize, sha256 } from "./hash.js";
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/verifier/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/verifier/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This is pure bookkeeping: it slices the entry array to version N
|
|
5
5
|
* and returns the logged values. It does NOT recompute any posteriors.
|
|
6
6
|
*/
|
|
7
|
-
import type { EvidenceRecord, EvidenceEntry } from "../schema/evidence-record";
|
|
7
|
+
import type { EvidenceRecord, EvidenceEntry } from "../schema/evidence-record.js";
|
|
8
8
|
/**
|
|
9
9
|
* Replay an evidence record to a specific version.
|
|
10
10
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/verifier/replay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/verifier/replay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,cAAc,EACtB,aAAa,EAAE,MAAM,GACpB,aAAa,EAAE,CAEjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,EACtB,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,IAAI,CAIf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,EACtB,aAAa,EAAE,MAAM,GACpB;IACD,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C,CAkBA"}
|
|
@@ -2,47 +2,21 @@
|
|
|
2
2
|
* Evidence Record Verifier
|
|
3
3
|
*
|
|
4
4
|
* Verifies the bookkeeping integrity of an evidence record WITHOUT
|
|
5
|
-
* containing any estimation mathematics.
|
|
6
|
-
* "verify instead of trust."
|
|
5
|
+
* containing any estimation mathematics.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
* - Schema validity (required fields present, correct types)
|
|
10
|
-
* - Strictly monotonic version numbers
|
|
11
|
-
* - Timestamp monotonicity (with configurable tolerance)
|
|
12
|
-
* - Hash-chain integrity (prevHash linkage, entryHash recompute)
|
|
13
|
-
* - Posterior chain consistency (each priorPosterior = previous updatedPosterior)
|
|
14
|
-
* - Compaction-boundary integrity
|
|
15
|
-
* - Enum validity (all enum values are from the public vocabulary)
|
|
16
|
-
*
|
|
17
|
-
* What it MUST NOT do (enforced by test):
|
|
18
|
-
* - Never recompute a posterior
|
|
19
|
-
* - Never contain an update function
|
|
20
|
-
* - Never embed a weight or threshold
|
|
21
|
-
* - Never import from any estimation module
|
|
7
|
+
* verify makes no network calls. Records never leave your machine.
|
|
22
8
|
*/
|
|
23
|
-
import type { EvidenceRecord } from "../schema/evidence-record";
|
|
9
|
+
import type { EvidenceRecord } from "../schema/evidence-record.js";
|
|
24
10
|
export interface Violation {
|
|
25
|
-
/** Entry version where the violation was found, or 0 for record-level. */
|
|
26
11
|
version: number;
|
|
27
|
-
/** Category of the violation. */
|
|
28
12
|
category: "schema" | "version" | "timestamp" | "hash" | "posterior" | "compaction" | "enum";
|
|
29
|
-
/** Human-readable description. */
|
|
30
13
|
message: string;
|
|
14
|
+
checkId: string;
|
|
31
15
|
}
|
|
32
16
|
export interface VerificationResult {
|
|
33
|
-
/** Whether the record passed all checks. */
|
|
34
17
|
valid: boolean;
|
|
35
|
-
/** List of violations found. */
|
|
36
18
|
violations: Violation[];
|
|
37
|
-
/** Number of entries checked. */
|
|
38
19
|
entriesChecked: number;
|
|
39
20
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Verify the integrity of an evidence record.
|
|
42
|
-
*
|
|
43
|
-
* @param record The evidence record to verify.
|
|
44
|
-
* @param timestampToleranceMs Maximum allowed backward timestamp drift (default: 1000ms).
|
|
45
|
-
* @returns Verification result with any violations found.
|
|
46
|
-
*/
|
|
47
21
|
export declare function verifyRecord(record: EvidenceRecord, timestampToleranceMs?: number): VerificationResult;
|
|
48
22
|
//# sourceMappingURL=verify-record.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-record.d.ts","sourceRoot":"","sources":["../../src/verifier/verify-record.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verify-record.d.ts","sourceRoot":"","sources":["../../src/verifier/verify-record.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,8BAA8B,CAAC;AAIlF,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,MAAM,CAAC;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB;AAYD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,oBAAoB,GAAE,MAAa,GAClC,kBAAkB,CAwHpB"}
|
|
@@ -2,148 +2,113 @@
|
|
|
2
2
|
* Evidence Record Verifier
|
|
3
3
|
*
|
|
4
4
|
* Verifies the bookkeeping integrity of an evidence record WITHOUT
|
|
5
|
-
* containing any estimation mathematics.
|
|
6
|
-
* "verify instead of trust."
|
|
5
|
+
* containing any estimation mathematics.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
* - Schema validity (required fields present, correct types)
|
|
10
|
-
* - Strictly monotonic version numbers
|
|
11
|
-
* - Timestamp monotonicity (with configurable tolerance)
|
|
12
|
-
* - Hash-chain integrity (prevHash linkage, entryHash recompute)
|
|
13
|
-
* - Posterior chain consistency (each priorPosterior = previous updatedPosterior)
|
|
14
|
-
* - Compaction-boundary integrity
|
|
15
|
-
* - Enum validity (all enum values are from the public vocabulary)
|
|
16
|
-
*
|
|
17
|
-
* What it MUST NOT do (enforced by test):
|
|
18
|
-
* - Never recompute a posterior
|
|
19
|
-
* - Never contain an update function
|
|
20
|
-
* - Never embed a weight or threshold
|
|
21
|
-
* - Never import from any estimation module
|
|
7
|
+
* verify makes no network calls. Records never leave your machine.
|
|
22
8
|
*/
|
|
23
|
-
import { hashEntry } from "./hash";
|
|
9
|
+
import { hashEntry } from "./hash.js";
|
|
24
10
|
const VALID_SCAFFOLD_TYPES = ["none", "socratic", "probing", "metacognitive", "scaffolding", "explain", "hint", "demonstrate"];
|
|
25
11
|
const VALID_DEPTH_LEVELS = ["surface", "conceptual", "transfer", "integration"];
|
|
26
12
|
const VALID_EPISTEMIC_MODES = ["experience", "inference", "analogy", "testimony"];
|
|
27
13
|
const VALID_TRIAGE_RESULTS = ["correct", "slip", "misconception", "disengagement", "ambiguous"];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* @param timestampToleranceMs Maximum allowed backward timestamp drift (default: 1000ms).
|
|
33
|
-
* @returns Verification result with any violations found.
|
|
34
|
-
*/
|
|
14
|
+
function isValidISO8601(ts) {
|
|
15
|
+
const d = new Date(ts);
|
|
16
|
+
return !isNaN(d.getTime()) && ts.length > 0;
|
|
17
|
+
}
|
|
35
18
|
export function verifyRecord(record, timestampToleranceMs = 1000) {
|
|
36
19
|
const violations = [];
|
|
37
|
-
// Record-level schema checks
|
|
38
20
|
if (!record.studentScopeId || typeof record.studentScopeId !== "string") {
|
|
39
|
-
violations.push({ version: 0, category: "schema", message: "Missing or invalid studentScopeId" });
|
|
21
|
+
violations.push({ version: 0, category: "schema", message: "Missing or invalid studentScopeId", checkId: "SCHEMA.REQUIRED" });
|
|
40
22
|
}
|
|
41
23
|
if (!Array.isArray(record.entries)) {
|
|
42
|
-
violations.push({ version: 0, category: "schema", message: "entries must be an array" });
|
|
24
|
+
violations.push({ version: 0, category: "schema", message: "entries must be an array", checkId: "SCHEMA.REQUIRED" });
|
|
43
25
|
return { valid: false, violations, entriesChecked: 0 };
|
|
44
26
|
}
|
|
45
|
-
// Compaction boundary
|
|
27
|
+
// Compaction boundary
|
|
46
28
|
if (record.compactedBefore) {
|
|
47
29
|
if (typeof record.compactedBefore.version !== "number") {
|
|
48
|
-
violations.push({ version: 0, category: "compaction", message: "compactedBefore.version must be a number" });
|
|
30
|
+
violations.push({ version: 0, category: "compaction", message: "compactedBefore.version must be a number", checkId: "COMPACTION.BOUNDARY" });
|
|
49
31
|
}
|
|
50
32
|
if (typeof record.compactedBefore.summaryHash !== "string" || !record.compactedBefore.summaryHash) {
|
|
51
|
-
violations.push({ version: 0, category: "compaction", message: "compactedBefore.summaryHash must be a non-empty string" });
|
|
33
|
+
violations.push({ version: 0, category: "compaction", message: "compactedBefore.summaryHash must be a non-empty string", checkId: "COMPACTION.BOUNDARY" });
|
|
52
34
|
}
|
|
53
|
-
// First entry version must be > compaction boundary
|
|
54
35
|
if (record.entries.length > 0 && record.entries[0].version <= record.compactedBefore.version) {
|
|
55
36
|
violations.push({
|
|
56
|
-
version: record.entries[0].version,
|
|
57
|
-
category: "compaction",
|
|
37
|
+
version: record.entries[0].version, category: "compaction",
|
|
58
38
|
message: `First entry version (${record.entries[0].version}) must be > compaction boundary (${record.compactedBefore.version})`,
|
|
39
|
+
checkId: "COMPACTION.BOUNDARY",
|
|
59
40
|
});
|
|
60
41
|
}
|
|
61
42
|
}
|
|
62
43
|
let prevVersion = record.compactedBefore?.version ?? 0;
|
|
63
44
|
let prevTimestamp = "";
|
|
64
45
|
let prevHash = record.compactedBefore?.summaryHash ?? "";
|
|
65
|
-
// Track last updatedPosterior per scope for posterior chain check
|
|
66
46
|
let lastUpdatedPosterior = null;
|
|
67
|
-
for (
|
|
68
|
-
const entry = record.entries[i];
|
|
47
|
+
for (const entry of record.entries) {
|
|
69
48
|
const v = entry.version ?? -1;
|
|
70
|
-
//
|
|
49
|
+
// SCHEMA.REQUIRED
|
|
71
50
|
if (typeof v !== "number" || v < 1) {
|
|
72
|
-
violations.push({ version: v, category: "schema", message: "version must be a positive integer" });
|
|
51
|
+
violations.push({ version: v, category: "schema", message: "version must be a positive integer", checkId: "SCHEMA.REQUIRED" });
|
|
73
52
|
}
|
|
74
53
|
if (typeof entry.timestamp !== "string" || !entry.timestamp) {
|
|
75
|
-
violations.push({ version: v, category: "schema", message: "timestamp is required" });
|
|
76
|
-
}
|
|
77
|
-
if (entry.event === undefined || entry.event === null) {
|
|
78
|
-
violations.push({ version: v, category: "schema", message: "event is required" });
|
|
54
|
+
violations.push({ version: v, category: "schema", message: "timestamp is required", checkId: "SCHEMA.REQUIRED" });
|
|
79
55
|
}
|
|
56
|
+
// SCHEMA.TYPES
|
|
80
57
|
if (typeof entry.evidentialWeight !== "number") {
|
|
81
|
-
violations.push({ version: v, category: "schema", message: "evidentialWeight must be a number" });
|
|
58
|
+
violations.push({ version: v, category: "schema", message: "evidentialWeight must be a number", checkId: "SCHEMA.TYPES" });
|
|
82
59
|
}
|
|
83
60
|
if (typeof entry.priorPosterior !== "number") {
|
|
84
|
-
violations.push({ version: v, category: "schema", message: "priorPosterior must be a number" });
|
|
61
|
+
violations.push({ version: v, category: "schema", message: "priorPosterior must be a number", checkId: "SCHEMA.TYPES" });
|
|
85
62
|
}
|
|
86
63
|
if (typeof entry.updatedPosterior !== "number") {
|
|
87
|
-
violations.push({ version: v, category: "schema", message: "updatedPosterior must be a number" });
|
|
64
|
+
violations.push({ version: v, category: "schema", message: "updatedPosterior must be a number", checkId: "SCHEMA.TYPES" });
|
|
88
65
|
}
|
|
89
|
-
//
|
|
66
|
+
// SCHEMA.TIMESTAMP_FORMAT
|
|
67
|
+
if (typeof entry.timestamp === "string" && entry.timestamp && !isValidISO8601(entry.timestamp)) {
|
|
68
|
+
violations.push({ version: v, category: "timestamp", message: `Timestamp is not valid ISO 8601: "${entry.timestamp}"`, checkId: "SCHEMA.TIMESTAMP_FORMAT" });
|
|
69
|
+
}
|
|
70
|
+
// VERSION.MONOTONIC
|
|
90
71
|
if (v <= prevVersion) {
|
|
91
|
-
violations.push({
|
|
92
|
-
version: v,
|
|
93
|
-
category: "version",
|
|
94
|
-
message: `Version ${v} is not strictly greater than previous ${prevVersion}`,
|
|
95
|
-
});
|
|
72
|
+
violations.push({ version: v, category: "version", message: `Version ${v} <= previous ${prevVersion}`, checkId: "VERSION.MONOTONIC" });
|
|
96
73
|
}
|
|
97
|
-
//
|
|
74
|
+
// TIMESTAMP.MONOTONIC
|
|
98
75
|
if (prevTimestamp && entry.timestamp) {
|
|
99
76
|
const prevMs = new Date(prevTimestamp).getTime();
|
|
100
77
|
const currMs = new Date(entry.timestamp).getTime();
|
|
101
78
|
if (!isNaN(prevMs) && !isNaN(currMs) && currMs < prevMs - timestampToleranceMs) {
|
|
102
|
-
violations.push({
|
|
103
|
-
version: v,
|
|
104
|
-
category: "timestamp",
|
|
105
|
-
message: `Timestamp goes backward: ${entry.timestamp} < ${prevTimestamp} (tolerance: ${timestampToleranceMs}ms)`,
|
|
106
|
-
});
|
|
79
|
+
violations.push({ version: v, category: "timestamp", message: "Timestamp goes backward", checkId: "TIMESTAMP.MONOTONIC" });
|
|
107
80
|
}
|
|
108
81
|
}
|
|
109
|
-
//
|
|
82
|
+
// ENUM.VALID
|
|
110
83
|
if (entry.scaffoldType && !VALID_SCAFFOLD_TYPES.includes(entry.scaffoldType)) {
|
|
111
|
-
violations.push({ version: v, category: "enum", message: `Invalid scaffoldType: ${entry.scaffoldType}
|
|
84
|
+
violations.push({ version: v, category: "enum", message: `Invalid scaffoldType: ${entry.scaffoldType}`, checkId: "ENUM.VALID" });
|
|
112
85
|
}
|
|
113
86
|
if (entry.depthLevel && !VALID_DEPTH_LEVELS.includes(entry.depthLevel)) {
|
|
114
|
-
violations.push({ version: v, category: "enum", message: `Invalid depthLevel: ${entry.depthLevel}
|
|
87
|
+
violations.push({ version: v, category: "enum", message: `Invalid depthLevel: ${entry.depthLevel}`, checkId: "ENUM.VALID" });
|
|
115
88
|
}
|
|
116
89
|
if (entry.epistemicMode && !VALID_EPISTEMIC_MODES.includes(entry.epistemicMode)) {
|
|
117
|
-
violations.push({ version: v, category: "enum", message: `Invalid epistemicMode: ${entry.epistemicMode}
|
|
90
|
+
violations.push({ version: v, category: "enum", message: `Invalid epistemicMode: ${entry.epistemicMode}`, checkId: "ENUM.VALID" });
|
|
118
91
|
}
|
|
119
92
|
if (entry.triageResult && !VALID_TRIAGE_RESULTS.includes(entry.triageResult)) {
|
|
120
|
-
violations.push({ version: v, category: "enum", message: `Invalid triageResult: ${entry.triageResult}
|
|
93
|
+
violations.push({ version: v, category: "enum", message: `Invalid triageResult: ${entry.triageResult}`, checkId: "ENUM.VALID" });
|
|
121
94
|
}
|
|
122
|
-
//
|
|
95
|
+
// HASH.PREV_LINK
|
|
123
96
|
if (entry.prevHash !== prevHash) {
|
|
124
|
-
violations.push({
|
|
125
|
-
version: v,
|
|
126
|
-
category: "hash",
|
|
127
|
-
message: `prevHash mismatch: expected "${prevHash}", got "${entry.prevHash}"`,
|
|
128
|
-
});
|
|
97
|
+
violations.push({ version: v, category: "hash", message: "prevHash mismatch", checkId: "HASH.PREV_LINK" });
|
|
129
98
|
}
|
|
99
|
+
// HASH.ENTRY_RECOMPUTE
|
|
130
100
|
const computedHash = hashEntry(entry);
|
|
131
101
|
if (entry.entryHash !== computedHash) {
|
|
132
|
-
violations.push({
|
|
133
|
-
version: v,
|
|
134
|
-
category: "hash",
|
|
135
|
-
message: `entryHash mismatch: expected "${computedHash}", got "${entry.entryHash}"`,
|
|
136
|
-
});
|
|
102
|
+
violations.push({ version: v, category: "hash", message: "entryHash mismatch", checkId: "HASH.ENTRY_RECOMPUTE" });
|
|
137
103
|
}
|
|
138
|
-
//
|
|
139
|
-
// Each entry's priorPosterior should equal the previous entry's updatedPosterior
|
|
104
|
+
// POSTERIOR.CHAIN
|
|
140
105
|
if (lastUpdatedPosterior !== null && typeof entry.priorPosterior === "number") {
|
|
141
106
|
const diff = Math.abs(entry.priorPosterior - lastUpdatedPosterior);
|
|
142
107
|
if (diff > 1e-9) {
|
|
143
108
|
violations.push({
|
|
144
|
-
version: v,
|
|
145
|
-
|
|
146
|
-
|
|
109
|
+
version: v, category: "posterior",
|
|
110
|
+
message: `priorPosterior (${entry.priorPosterior}) != previous updatedPosterior (${lastUpdatedPosterior})`,
|
|
111
|
+
checkId: "POSTERIOR.CHAIN",
|
|
147
112
|
});
|
|
148
113
|
}
|
|
149
114
|
}
|
|
@@ -154,10 +119,6 @@ export function verifyRecord(record, timestampToleranceMs = 1000) {
|
|
|
154
119
|
lastUpdatedPosterior = entry.updatedPosterior;
|
|
155
120
|
}
|
|
156
121
|
}
|
|
157
|
-
return {
|
|
158
|
-
valid: violations.length === 0,
|
|
159
|
-
violations,
|
|
160
|
-
entriesChecked: record.entries.length,
|
|
161
|
-
};
|
|
122
|
+
return { valid: violations.length === 0, violations, entriesChecked: record.entries.length };
|
|
162
123
|
}
|
|
163
124
|
//# sourceMappingURL=verify-record.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-record.js","sourceRoot":"","sources":["../../src/verifier/verify-record.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verify-record.js","sourceRoot":"","sources":["../../src/verifier/verify-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifier for schema 0.3 records — two-level verification.
|
|
3
|
+
* Evidence chain + estimate chains + record-level consent/attestation checks.
|
|
4
|
+
*/
|
|
5
|
+
export interface Violation03 {
|
|
6
|
+
version: number;
|
|
7
|
+
category: string;
|
|
8
|
+
message: string;
|
|
9
|
+
checkId: string;
|
|
10
|
+
chain: string;
|
|
11
|
+
}
|
|
12
|
+
export interface VerificationResult03 {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
level: number;
|
|
15
|
+
violations: Violation03[];
|
|
16
|
+
entriesChecked: number;
|
|
17
|
+
estimators: Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
opaque: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export declare function verifyRecordV03(record: Record<string, any>, timestampToleranceMs?: number, estimateTolerance?: number): VerificationResult03;
|
|
24
|
+
//# sourceMappingURL=verifier-v03.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier-v03.d.ts","sourceRoot":"","sources":["../src/verifier-v03.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoBH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACvE;AAUD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,oBAAoB,SAAO,EAC3B,iBAAiB,SAAO,GACvB,oBAAoB,CA+KtB"}
|