qlm-measure 0.2.2 → 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/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { readFileSync, writeFileSync, existsSync } from "fs";
7
7
  import { createHash } from "crypto";
8
8
  import { verifyRecord } from "./verifier/verify-record.js";
9
9
  import { CATALOG, SHIPPED_CHECKS, PLANNED_CHECKS, CATALOG_BY_ID, CATEGORIES } from "./checks.js";
10
- const VERSION = "0.2.2";
10
+ const VERSION = "0.2.3";
11
11
  function loadRecords(path) {
12
12
  let raw;
13
13
  if (path === "-") {
package/dist/index.d.ts CHANGED
@@ -32,6 +32,10 @@ export { hashEntry, hashCanonical, canonicalize, sha256 } from "./verifier/hash.
32
32
  export { CATALOG, CATALOG_BY_ID, SHIPPED_CHECKS, PLANNED_CHECKS, CATEGORIES } from "./checks.js";
33
33
  export type { CheckDef } from "./checks.js";
34
34
  export { reproduce, registerRule, getRule } from "./rules.js";
35
+ export { Recorder } from "./recorder.js";
36
+ export type { EvidenceEntry03 } from "./recorder.js";
37
+ export { verifyRecordV03 } from "./verifier-v03.js";
38
+ export type { VerificationResult03, Violation03 } from "./verifier-v03.js";
35
39
  export { OntologyClient } from "./clients/ontology-client.js";
36
40
  export type { OntologyClientConfig, DatasetName } from "./clients/ontology-client.js";
37
41
  export { EngineClient } from "./clients/engine-client.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,YAAY,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,YAAY,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAGtE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGpF,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG9D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,YAAY,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,YAAY,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAGtE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGpF,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG9D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG3E,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -29,6 +29,10 @@ export { hashEntry, hashCanonical, canonicalize, sha256 } from "./verifier/hash.
29
29
  export { CATALOG, CATALOG_BY_ID, SHIPPED_CHECKS, PLANNED_CHECKS, CATEGORIES } from "./checks.js";
30
30
  // Rules
31
31
  export { reproduce, registerRule, getRule } from "./rules.js";
32
+ // Recorder (schema 0.3)
33
+ export { Recorder } from "./recorder.js";
34
+ // Verifier v0.3
35
+ export { verifyRecordV03 } from "./verifier-v03.js";
32
36
  // Clients
33
37
  export { OntologyClient } from "./clients/ontology-client.js";
34
38
  export { EngineClient } from "./clients/engine-client.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAUH,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAI1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,UAAU;AACV,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGtE,WAAW;AACX,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEpF,gBAAgB;AAChB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGjG,QAAQ;AACR,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE9D,UAAU;AACV,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAUH,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAI1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,UAAU;AACV,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGtE,WAAW;AACX,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEpF,gBAAgB;AAChB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGjG,QAAQ;AACR,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE9D,wBAAwB;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,gBAAgB;AAChB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,UAAU;AACV,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Recorder — produce verifiable evidence records without a server.
3
+ *
4
+ * Usage:
5
+ * import { Recorder } from "qlm-measure";
6
+ * const rec = new Recorder("learner-001");
7
+ * rec.append({ correct: true, domain: "math", timestamp: "..." });
8
+ * const record = rec.export(); // Level 1, verifies clean
9
+ */
10
+ export interface EvidenceEntry03 {
11
+ version: number;
12
+ timestamp: string;
13
+ eventHash: string;
14
+ event: Record<string, unknown>;
15
+ prevHash: string;
16
+ entryHash: string;
17
+ }
18
+ export declare class Recorder {
19
+ private scopeId;
20
+ private entries;
21
+ private currentVersion;
22
+ private prevHash;
23
+ private lastTs;
24
+ private toleranceMs;
25
+ constructor(scopeId: string, toleranceMs?: number);
26
+ static fromRecord(record: Record<string, unknown>): Recorder;
27
+ append(event: Record<string, unknown>): EvidenceEntry03;
28
+ redact(version: number): void;
29
+ chainHead(): string;
30
+ export(): Record<string, unknown>;
31
+ get version(): number;
32
+ }
33
+ //# sourceMappingURL=recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,MAAM,EAAE,WAAW,SAAO;IAM/C,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAqB5D,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe;IAkDvD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO7B,SAAS,IAAI,MAAM;IAInB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQjC,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Recorder — produce verifiable evidence records without a server.
3
+ *
4
+ * Usage:
5
+ * import { Recorder } from "qlm-measure";
6
+ * const rec = new Recorder("learner-001");
7
+ * rec.append({ correct: true, domain: "math", timestamp: "..." });
8
+ * const record = rec.export(); // Level 1, verifies clean
9
+ */
10
+ import { createHash } from "crypto";
11
+ function canonicalize(obj) {
12
+ return JSON.stringify(obj, (_, value) => {
13
+ if (value && typeof value === "object" && !Array.isArray(value)) {
14
+ return Object.keys(value).sort().reduce((s, k) => {
15
+ s[k] = value[k];
16
+ return s;
17
+ }, {});
18
+ }
19
+ return value;
20
+ });
21
+ }
22
+ function sha256(s) {
23
+ return createHash("sha256").update(s).digest("hex");
24
+ }
25
+ const VALID_SCAFFOLD_TYPES = new Set(["none", "socratic", "probing", "metacognitive", "scaffolding", "explain", "hint", "demonstrate"]);
26
+ const VALID_CONSENT_ACTIONS = new Set(["granted", "withdrawn"]);
27
+ const VALID_CONSENT_SCOPES = new Set(["instruction", "research", "export"]);
28
+ export class Recorder {
29
+ scopeId;
30
+ entries = [];
31
+ currentVersion = 0;
32
+ prevHash = "";
33
+ lastTs = "";
34
+ toleranceMs;
35
+ constructor(scopeId, toleranceMs = 1000) {
36
+ if (!scopeId)
37
+ throw new Error("scopeId is required");
38
+ this.scopeId = scopeId;
39
+ this.toleranceMs = toleranceMs;
40
+ }
41
+ static fromRecord(record) {
42
+ const scopeId = record.studentScopeId || "";
43
+ const rec = new Recorder(scopeId);
44
+ const evidence = record.evidence || {};
45
+ const entries = evidence.entries || record.entries || [];
46
+ const compacted = (evidence.compactedBefore || record.compactedBefore);
47
+ if (compacted) {
48
+ rec.currentVersion = compacted.version;
49
+ rec.prevHash = compacted.summaryHash || "";
50
+ }
51
+ for (const entry of entries) {
52
+ rec.entries.push(entry);
53
+ rec.currentVersion = entry.version;
54
+ rec.prevHash = entry.entryHash;
55
+ rec.lastTs = entry.timestamp;
56
+ }
57
+ return rec;
58
+ }
59
+ append(event) {
60
+ const kind = event.redacted ? "redacted" : (event.action && VALID_CONSENT_ACTIONS.has(event.action)) ? "consent" : "observation";
61
+ // Validate
62
+ if (kind === "observation") {
63
+ const st = event.scaffoldType;
64
+ if (st && !VALID_SCAFFOLD_TYPES.has(st))
65
+ throw new Error(`Invalid scaffoldType: ${st}`);
66
+ }
67
+ if (kind === "consent") {
68
+ if (!VALID_CONSENT_ACTIONS.has(event.action))
69
+ throw new Error(`Invalid consent action`);
70
+ const scopes = event.scopes;
71
+ if (!scopes?.length)
72
+ throw new Error("ConsentEvent must have scopes");
73
+ for (const s of scopes)
74
+ if (!VALID_CONSENT_SCOPES.has(s))
75
+ throw new Error(`Invalid scope: ${s}`);
76
+ }
77
+ const ts = (event.timestamp || event.at || new Date().toISOString());
78
+ // Monotonicity
79
+ if (this.lastTs && ts) {
80
+ const prev = new Date(this.lastTs).getTime();
81
+ const curr = new Date(ts).getTime();
82
+ if (!isNaN(prev) && !isNaN(curr) && curr < prev - this.toleranceMs) {
83
+ throw new Error(`Timestamp goes backward: ${ts} < ${this.lastTs}`);
84
+ }
85
+ }
86
+ this.currentVersion++;
87
+ const eventHash = sha256(canonicalize(event));
88
+ const entryHash = sha256(canonicalize({
89
+ version: this.currentVersion,
90
+ timestamp: ts,
91
+ eventHash,
92
+ prevHash: this.prevHash,
93
+ }));
94
+ const entry = {
95
+ version: this.currentVersion,
96
+ timestamp: ts,
97
+ eventHash,
98
+ event,
99
+ prevHash: this.prevHash,
100
+ entryHash,
101
+ };
102
+ this.entries.push(entry);
103
+ this.prevHash = entryHash;
104
+ this.lastTs = ts;
105
+ return entry;
106
+ }
107
+ redact(version) {
108
+ const entry = this.entries.find(e => e.version === version);
109
+ if (!entry)
110
+ throw new Error(`No entry with version ${version}`);
111
+ if (entry.event.redacted)
112
+ return;
113
+ entry.event = { redacted: true, eventHash: entry.eventHash };
114
+ }
115
+ chainHead() {
116
+ return this.prevHash;
117
+ }
118
+ export() {
119
+ return {
120
+ schemaVersion: "0.3",
121
+ studentScopeId: this.scopeId,
122
+ evidence: { entries: [...this.entries] },
123
+ };
124
+ }
125
+ get version() {
126
+ return this.currentVersion;
127
+ }
128
+ }
129
+ //# sourceMappingURL=recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
@@ -7,10 +7,8 @@
7
7
  import type { EvidenceEntry } from "../schema/evidence-record.js";
8
8
  /**
9
9
  * Canonical JSON serialization — sorted keys, no whitespace.
10
- * Matches Python's json.dumps(sort_keys=True, separators=(",",":"), default=str).
11
- *
12
- * Key difference from bare JSON.stringify: Python writes 1.0 for integer-valued
13
- * floats while JS writes 1. We match Python by post-processing the known numeric fields.
10
+ * Matches Python's json.dumps(sort_keys=True, separators=(",",":"), default=str)
11
+ * byte-for-byte, including number and string formatting.
14
12
  */
15
13
  export declare function canonicalize(obj: unknown): string;
16
14
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/verifier/hash.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAcjD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAetD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD"}
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/verifier/hash.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAqGlE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEjD;AAsBD;;GAEG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CA8BtD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -6,25 +6,132 @@
6
6
  */
7
7
  import { createHash } from "crypto";
8
8
  /**
9
- * Canonical JSON serialization — sorted keys, no whitespace.
10
- * Matches Python's json.dumps(sort_keys=True, separators=(",",":"), default=str).
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.
11
19
  *
12
- * Key difference from bare JSON.stringify: Python writes 1.0 for integer-valued
13
- * floats while JS writes 1. We match Python by post-processing the known numeric fields.
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).
14
24
  */
15
- export function canonicalize(obj) {
16
- const raw = JSON.stringify(obj, (_, value) => {
17
- if (value && typeof value === "object" && !Array.isArray(value)) {
18
- return Object.keys(value).sort().reduce((sorted, key) => {
19
- sorted[key] = value[key];
20
- return sorted;
21
- }, {});
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;
51
+ }
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")}`;
22
85
  }
23
- return value;
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");
24
101
  });
25
- // Match Python's number formatting: integer-valued floats get .0
26
- // Python: json.dumps(1.0) -> "1.0", JSON.stringify(1.0) -> "1"
27
- return raw.replace(/"(evidentialWeight|priorPosterior|updatedPosterior|score|difficulty|classifierConfidence)":([-]?\d+)(?=[,}\]])/g, (_, key, num) => `"${key}":${num}.0`);
102
+ return result;
103
+ }
104
+ /**
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.
108
+ */
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(",") + "]";
125
+ }
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(",") + "}";
133
+ }
134
+ return JSON.stringify(val);
28
135
  }
29
136
  /**
30
137
  * Compute SHA-256 hash of a string.
@@ -50,7 +157,18 @@ export function hashEntry(entry) {
50
157
  updatedPosterior: entry.updatedPosterior,
51
158
  nonInterventionDecision: entry.nonInterventionDecision ?? null,
52
159
  };
53
- return sha256(canonicalize(hashable));
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);
54
172
  }
55
173
  /**
56
174
  * Compute a hash over a canonical string.
@@ -1 +1 @@
1
- {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/verifier/hash.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGpC;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;QAC3C,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,CAA0B,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;gBAC/E,MAAM,CAAC,GAAG,CAAC,GAAI,KAAiC,CAAC,GAAG,CAAC,CAAC;gBACtD,OAAO,MAAM,CAAC;YAChB,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IACH,iEAAiE;IACjE,+DAA+D;IAC/D,OAAO,GAAG,CAAC,OAAO,CAAC,iHAAiH,EAClI,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAoB;IAC5C,MAAM,QAAQ,GAAG;QACf,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;QACxC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACpC,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;QAC1C,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;QACxC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,uBAAuB,EAAE,KAAK,CAAC,uBAAuB,IAAI,IAAI;KAC/D,CAAC;IACF,OAAO,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/verifier/hash.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"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Verifier for schema 0.3 records — two-level verification.
3
+ * Evidence chain + estimate chains + record-level consent/attestation checks.
4
+ */
5
+ import { createHash } from "crypto";
6
+ import { reproduce } from "./rules.js";
7
+ function canonicalize(obj) {
8
+ return JSON.stringify(obj, (_, v) => {
9
+ if (v && typeof v === "object" && !Array.isArray(v)) {
10
+ return Object.keys(v).sort().reduce((s, k) => {
11
+ s[k] = v[k];
12
+ return s;
13
+ }, {});
14
+ }
15
+ return v;
16
+ });
17
+ }
18
+ function sha256(s) {
19
+ return createHash("sha256").update(s).digest("hex");
20
+ }
21
+ const VALID_ST = new Set(["none", "socratic", "probing", "metacognitive", "scaffolding", "explain", "hint", "demonstrate"]);
22
+ const VALID_EM = new Set(["experience", "inference", "analogy", "testimony"]);
23
+ const VALID_CA = new Set(["granted", "withdrawn"]);
24
+ const VALID_CS = new Set(["instruction", "research", "export"]);
25
+ const VALID_TR = new Set(["correct", "slip", "misconception", "disengagement", "ambiguous"]);
26
+ const VALID_DL = new Set(["surface", "conceptual", "transfer", "integration"]);
27
+ const VALID_DT = new Set(["INSTRUCTIONAL", "REPORTING", "PLACEMENT", "CREDENTIAL"]);
28
+ export function verifyRecordV03(record, timestampToleranceMs = 1000, estimateTolerance = 1e-4) {
29
+ const violations = [];
30
+ const V = (v, cat, msg, id, chain) => violations.push({ version: v, category: cat, message: msg, checkId: id, chain });
31
+ if (!record.studentScopeId)
32
+ V(0, "schema", "Missing studentScopeId", "SCHEMA.REQUIRED", "record");
33
+ const evidence = record.evidence || {};
34
+ const evEntries = evidence.entries || [];
35
+ const estimates = record.estimates || [];
36
+ const attestations = record.attestations || [];
37
+ const exportBlock = record.export;
38
+ const level = estimates.length > 0 ? 2 : 1;
39
+ // Evidence chain
40
+ const evByVersion = {};
41
+ const evByHash = {};
42
+ const compacted = evidence.compactedBefore;
43
+ let prevV = compacted?.version ?? 0;
44
+ let prevH = compacted?.summaryHash ?? "";
45
+ let prevTs = "";
46
+ const consentState = {};
47
+ for (const entry of evEntries) {
48
+ const v = entry.version ?? -1;
49
+ const ch = "evidence";
50
+ for (const req of ["version", "timestamp", "eventHash", "event", "prevHash", "entryHash"]) {
51
+ if (!(req in entry))
52
+ V(v, "schema", `${req} is required`, "SCHEMA.REQUIRED", ch);
53
+ }
54
+ const ts = entry.timestamp || "";
55
+ const tsMs = ts ? new Date(ts).getTime() : NaN;
56
+ if (ts && isNaN(tsMs))
57
+ V(v, "timestamp", `Not valid ISO 8601: ${ts}`, "SCHEMA.TIMESTAMP_FORMAT", ch);
58
+ if (typeof v === "number" && v <= prevV)
59
+ V(v, "version", `Version ${v} <= previous ${prevV}`, "VERSION.MONOTONIC", ch);
60
+ if (prevTs && ts && !isNaN(tsMs)) {
61
+ const pm = new Date(prevTs).getTime();
62
+ if (!isNaN(pm) && tsMs < pm - timestampToleranceMs)
63
+ V(v, "timestamp", "Timestamp goes backward", "TIMESTAMP.MONOTONIC", ch);
64
+ }
65
+ const event = entry.event || {};
66
+ const kind = event.redacted ? "redacted" : (event.action && VALID_CA.has(event.action)) ? "consent" : "correct" in event ? "observation" : "unknown";
67
+ if (kind === "unknown")
68
+ V(v, "schema", "Event is not Observation, Consent, or Redacted", "SCHEMA.EVENT_UNION", ch);
69
+ if (kind === "observation") {
70
+ if (event.scaffoldType && !VALID_ST.has(event.scaffoldType))
71
+ V(v, "enum", `Invalid scaffoldType`, "ENUM.VALID", ch);
72
+ if (event.epistemicMode && !VALID_EM.has(event.epistemicMode))
73
+ V(v, "enum", `Invalid epistemicMode`, "ENUM.VALID", ch);
74
+ }
75
+ if (kind === "consent") {
76
+ if (!VALID_CA.has(event.action))
77
+ V(v, "enum", `Invalid consent action`, "ENUM.VALID", ch);
78
+ for (const s of event.scopes || []) {
79
+ if (!VALID_CS.has(s))
80
+ V(v, "enum", `Invalid consent scope: ${s}`, "ENUM.VALID", ch);
81
+ consentState[s] = event.action;
82
+ }
83
+ }
84
+ if (kind !== "redacted") {
85
+ const computedEvHash = sha256(canonicalize(event));
86
+ if (entry.eventHash !== computedEvHash)
87
+ V(v, "hash", "eventHash mismatch", "EVENT.COMMITMENT", ch);
88
+ }
89
+ if (entry.prevHash !== prevH)
90
+ V(v, "hash", "prevHash mismatch", "HASH.PREV_LINK", ch);
91
+ const entryHashable = { version: entry.version, timestamp: entry.timestamp, eventHash: entry.eventHash, prevHash: entry.prevHash };
92
+ const computedEntryHash = sha256(canonicalize(entryHashable));
93
+ if (entry.entryHash !== computedEntryHash)
94
+ V(v, "hash", "entryHash mismatch", "HASH.ENTRY_RECOMPUTE", ch);
95
+ evByVersion[v] = entry;
96
+ evByHash[entry.entryHash || ""] = entry;
97
+ if (typeof v === "number")
98
+ prevV = v;
99
+ prevTs = ts;
100
+ prevH = entry.entryHash || "";
101
+ }
102
+ // Estimate chains
103
+ const estimatorSummaries = [];
104
+ for (let idx = 0; idx < estimates.length; idx++) {
105
+ const estChain = estimates[idx];
106
+ const chainLabel = `estimate:${idx}`;
107
+ const estimator = estChain.estimator || {};
108
+ if (!estimator.name)
109
+ V(0, "schema", "Estimator name missing", "ESTIMATE.DECLARED", chainLabel);
110
+ const isOpaque = estimator.opaque !== false;
111
+ const ruleId = estimator.ruleId;
112
+ const defaultParams = estimator.params || {};
113
+ estimatorSummaries.push({ name: estimator.name || "unknown", version: estimator.version || "?", opaque: isOpaque });
114
+ const estEntries = estChain.entries || [];
115
+ let estPrevV = estChain.compactedBefore?.version ?? 0;
116
+ let estPrevH = estChain.compactedBefore?.summaryHash ?? "";
117
+ let lastUp = null;
118
+ let lastEvV = 0;
119
+ for (const entry of estEntries) {
120
+ const v = entry.version ?? -1;
121
+ if (typeof v === "number" && v <= estPrevV)
122
+ V(v, "version", `Version ${v} <= previous ${estPrevV}`, "VERSION.MONOTONIC", chainLabel);
123
+ if (entry.prevHash !== estPrevH)
124
+ V(v, "hash", "prevHash mismatch", "HASH.PREV_LINK", chainLabel);
125
+ const hashable = { ...entry };
126
+ delete hashable.entryHash;
127
+ const computed = sha256(canonicalize(hashable));
128
+ if (entry.entryHash !== computed)
129
+ V(v, "hash", "entryHash mismatch", "HASH.ENTRY_RECOMPUTE", chainLabel);
130
+ const evVer = entry.evidenceVersion;
131
+ if (evVer != null && !(evVer in evByVersion))
132
+ V(v, "schema", `evidenceVersion ${evVer} not in evidence`, "ESTIMATE.LINKS_EVIDENCE", chainLabel);
133
+ if (evVer != null && evVer <= lastEvV)
134
+ V(v, "version", `evidenceVersion ${evVer} <= previous ${lastEvV}`, "ESTIMATE.ORDER", chainLabel);
135
+ if (entry.triageResult && !VALID_TR.has(entry.triageResult))
136
+ V(v, "enum", `Invalid triageResult`, "ENUM.VALID", chainLabel);
137
+ if (entry.depthLevel && !VALID_DL.has(entry.depthLevel))
138
+ V(v, "enum", `Invalid depthLevel`, "ENUM.VALID", chainLabel);
139
+ if (entry.decisionTier && !VALID_DT.has(entry.decisionTier))
140
+ V(v, "enum", `Invalid decisionTier`, "ENUM.VALID", chainLabel);
141
+ const w = entry.evidentialWeight;
142
+ if (typeof w === "number" && (w < 0 || w > 1))
143
+ V(v, "schema", `Weight ${w} outside [0,1]`, "WEIGHT.RANGE", chainLabel);
144
+ for (const f of ["priorPosterior", "updatedPosterior"]) {
145
+ const pv = entry[f];
146
+ if (typeof pv === "number" && (pv <= 0 || pv >= 1))
147
+ V(v, "schema", `${f} ${pv} outside (0,1)`, "POSTERIOR.RANGE", chainLabel);
148
+ }
149
+ if (lastUp !== null && typeof entry.priorPosterior === "number" && Math.abs(entry.priorPosterior - lastUp) > 1e-9) {
150
+ V(v, "posterior", `priorPosterior (${entry.priorPosterior}) != previous updatedPosterior (${lastUp})`, "POSTERIOR.CHAIN", chainLabel);
151
+ }
152
+ if (!isOpaque && ruleId) {
153
+ const params = entry.params || defaultParams;
154
+ const evEntry = evByVersion[evVer] || {};
155
+ const evEvent = evEntry.event || {};
156
+ const correct = evEvent.correct;
157
+ if (correct !== undefined && typeof entry.priorPosterior === "number" && typeof w === "number") {
158
+ const reproduced = reproduce(ruleId, params, entry.priorPosterior, correct, w);
159
+ if (reproduced !== null && Math.abs(reproduced - entry.updatedPosterior) > estimateTolerance) {
160
+ V(v, "estimate", `Reproduced ${reproduced.toFixed(6)} != recorded ${entry.updatedPosterior.toFixed(6)}`, "ESTIMATE.REPRODUCE", chainLabel);
161
+ }
162
+ }
163
+ }
164
+ if (typeof v === "number")
165
+ estPrevV = v;
166
+ estPrevH = entry.entryHash || "";
167
+ if (typeof entry.updatedPosterior === "number")
168
+ lastUp = entry.updatedPosterior;
169
+ if (evVer != null)
170
+ lastEvV = evVer;
171
+ }
172
+ }
173
+ // Record-level
174
+ if (exportBlock) {
175
+ const scope = exportBlock.scope;
176
+ if (scope && consentState[scope] === "withdrawn") {
177
+ V(0, "consent", `Scope '${scope}' was withdrawn`, "CONSENT.WITHDRAWAL", "record");
178
+ }
179
+ else if (scope && !(scope in consentState)) {
180
+ V(0, "consent", `No consent for scope '${scope}'`, "CONSENT.SCOPE", "record");
181
+ }
182
+ }
183
+ for (const att of attestations) {
184
+ if (att.payload && att.payloadHash) {
185
+ const computed = sha256(canonicalize(att.payload));
186
+ if (computed !== att.payloadHash)
187
+ V(0, "hash", "Attestation payloadHash mismatch", "ATTEST.INTEGRITY", "record");
188
+ }
189
+ const covers = att.covers || {};
190
+ if (covers.throughEvidenceVersion != null && !(covers.throughEvidenceVersion in evByVersion)) {
191
+ V(0, "schema", `Attestation covers version ${covers.throughEvidenceVersion} not in evidence`, "ATTEST.COVERS", "record");
192
+ }
193
+ if (covers.evidenceEntryHash && !(covers.evidenceEntryHash in evByHash)) {
194
+ V(0, "hash", "Attestation covers hash not in evidence", "ATTEST.COVERS", "record");
195
+ }
196
+ }
197
+ return {
198
+ valid: violations.length === 0,
199
+ level,
200
+ violations,
201
+ entriesChecked: evEntries.length + estimates.reduce((s, e) => s + (e.entries?.length || 0), 0),
202
+ estimators: estimatorSummaries,
203
+ };
204
+ }
205
+ //# sourceMappingURL=verifier-v03.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier-v03.js","sourceRoot":"","sources":["../src/verifier-v03.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qlm-measure",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Open measurement SDK for education AI — evidence events, record verification, and dataset clients",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -39,6 +39,7 @@
39
39
  ],
40
40
  "scripts": {
41
41
  "build": "tsc",
42
+ "postbuild": "chmod +x dist/cli.js 2>/dev/null || true",
42
43
  "test": "vitest run",
43
44
  "test:watch": "vitest",
44
45
  "lint": "tsc --noEmit",