Setting the file. One moment. Validate · Build Evidence Map · github/awesome-copilot · Skills Docsscripts/validate.mjs
JavaScript·91 lines·3 KB
}
from
"./contract.mjs"
;
12
13function receiptFor(map) {
14 const sourceSnapshots = map.sources.map((source) => ({
15 id: source.id,
16 retrievedAt: source.retrievedAt,
17 excerptSha256: createHash("sha256").update(source.excerpt).digest("hex"),
18 }));
19 return createHash("sha256")
20 .update(canonicalJson(receiptPayload(map, sourceSnapshots)))
21 .digest("hex");
22}
23
24export function inspectOfflineMap(map) {
25 const result = inspectMapContract(map);
26 return {
27 ...result,
28 receipt: result.valid ? receiptFor(map) : null,
29 };
30}
31
32export async function validateMapFile(file) {
33 let map;
34 try {
35 const input = file instanceof URL ? file : resolve(file);
36 map = JSON.parse(await readFile(input, "utf8"));
37 } catch (error) {
38 return {
39 findings: [{
40 path: "$",
41 rule: "invalid-json",
42 message: `Could not parse JSON: ${error.message}`,
43 }],
44 metrics: { claims: 0, contradictions: 0, evidence: 0, sources: 0, unknowns: 0 },
45 receipt: null,
46 valid: false,
47 };
48 }
49 return inspectOfflineMap(map);
50}
51
52function plural(count, singular, pluralForm = `${singular}s`) {
53 return count === 1 ? singular : pluralForm;
54}
55
56function printHuman(result) {
57 if (!result.valid) {
58 console.error(`INVALID ${result.findings.length} ${plural(result.findings.length, "finding")}`);
59 for (const finding of result.findings) {
60 console.error(` - ${finding.path} [${finding.rule}] ${finding.message}`);
61 }
62 return;
63 }
64 console.log(`VALID ${result.receipt}`);
65 console.log(
66 ` ✓ ${result.metrics.claims} ${plural(result.metrics.claims, "claim")} · ${result.metrics.evidence} evidence · ${result.metrics.sources} ${plural(result.metrics.sources, "source")}`,
67 );
68 console.log(
69 ` ↯ ${result.metrics.contradictions} ${plural(result.metrics.contradictions, "contradiction")} · ${result.metrics.unknowns} explicit ${plural(result.metrics.unknowns, "unknown")}`,
70 );
71}
72
73async function main(argv) {
74 if (argv.includes("--help") || argv.includes("-h")) {
75 console.log("Usage: node <skill-dir>/scripts/validate.mjs <map.doubt.json> [--json]");
76 return;
77 }
78 const file = argv.find((value) => !value.startsWith("-"));
79 if (!file) throw new Error("Pass a .doubt.json evidence map.");
80 const result = await validateMapFile(file);
81 if (argv.includes("--json")) console.log(JSON.stringify(result, null, 2));
82 else printHuman(result);
83 if (!result.valid) process.exitCode = 1;
84}
85
86if (process.argv[1] === fileURLToPath(import.meta.url)) {
87 main(process.argv.slice(2)).catch((error) => {
88 console.error(error.message);
89 process.exitCode = 1;
90 });
91}