@intentset/verification 0.1.0

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.
Files changed (56) hide show
  1. package/dist/adapters/common.d.ts +51 -0
  2. package/dist/adapters/common.d.ts.map +1 -0
  3. package/dist/adapters/common.js +95 -0
  4. package/dist/adapters/common.js.map +1 -0
  5. package/dist/adapters/node-test.d.ts +3 -0
  6. package/dist/adapters/node-test.d.ts.map +1 -0
  7. package/dist/adapters/node-test.js +119 -0
  8. package/dist/adapters/node-test.js.map +1 -0
  9. package/dist/adapters/vitest.d.ts +3 -0
  10. package/dist/adapters/vitest.d.ts.map +1 -0
  11. package/dist/adapters/vitest.js +71 -0
  12. package/dist/adapters/vitest.js.map +1 -0
  13. package/dist/check.d.ts +33 -0
  14. package/dist/check.d.ts.map +1 -0
  15. package/dist/check.js +95 -0
  16. package/dist/check.js.map +1 -0
  17. package/dist/classify.d.ts +69 -0
  18. package/dist/classify.d.ts.map +1 -0
  19. package/dist/classify.js +130 -0
  20. package/dist/classify.js.map +1 -0
  21. package/dist/coverage.d.ts +88 -0
  22. package/dist/coverage.d.ts.map +1 -0
  23. package/dist/coverage.js +114 -0
  24. package/dist/coverage.js.map +1 -0
  25. package/dist/diagnostic.d.ts +21 -0
  26. package/dist/diagnostic.d.ts.map +1 -0
  27. package/dist/diagnostic.js +20 -0
  28. package/dist/diagnostic.js.map +1 -0
  29. package/dist/fixture.d.ts +43 -0
  30. package/dist/fixture.d.ts.map +1 -0
  31. package/dist/fixture.js +42 -0
  32. package/dist/fixture.js.map +1 -0
  33. package/dist/index.d.ts +16 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +16 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/placeholders.d.ts +39 -0
  38. package/dist/placeholders.d.ts.map +1 -0
  39. package/dist/placeholders.js +47 -0
  40. package/dist/placeholders.js.map +1 -0
  41. package/dist/records.d.ts +77 -0
  42. package/dist/records.d.ts.map +1 -0
  43. package/dist/records.js +354 -0
  44. package/dist/records.js.map +1 -0
  45. package/package.json +33 -0
  46. package/src/adapters/common.ts +139 -0
  47. package/src/adapters/node-test.ts +139 -0
  48. package/src/adapters/vitest.ts +82 -0
  49. package/src/check.ts +137 -0
  50. package/src/classify.ts +222 -0
  51. package/src/coverage.ts +181 -0
  52. package/src/diagnostic.ts +39 -0
  53. package/src/fixture.ts +64 -0
  54. package/src/index.ts +15 -0
  55. package/src/placeholders.ts +51 -0
  56. package/src/records.ts +482 -0
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Evidence freshness (Core §8, invariant 3). Only a `pass` at the assessed
3
+ * commit and graph hash is current; everything else is something else, and
4
+ * says which. A failing run at the snapshot stays visible however many older
5
+ * passes there are, because the status is decided by the latest run at the
6
+ * snapshot and every run is kept in the history.
7
+ */
8
+ import { sortDiagnostics } from "@intentset/core";
9
+ import { compareStrings, evidenceDiagnostic } from "./diagnostic.js";
10
+ import { CURRENT, compareRecords } from "./records.js";
11
+ export const EVIDENCE_STATUSES = [
12
+ "current-pass",
13
+ "current-fail",
14
+ "stale",
15
+ "skip",
16
+ "error",
17
+ "missing",
18
+ "unresolved",
19
+ ];
20
+ const NOTE_COMMIT_UNKNOWN = "commit unknown; matched on graph hash";
21
+ /** Core §8: classify every verification in the graph against the snapshot. Deterministic for identical inputs. */
22
+ export function classifyEvidence(graph, records, snapshot, options = {}) {
23
+ const path = options.evidencePath ?? null;
24
+ const scope = snapshot.scope ?? null;
25
+ const diagnostics = [];
26
+ const unknown = [];
27
+ const rejected = [];
28
+ const inScope = new Map();
29
+ const outOfScope = new Map();
30
+ let ignoredOutOfScope = 0;
31
+ const ids = [...graph.artifacts.keys()]
32
+ .filter((id) => graph.artifacts.get(id)?.meta.type === "verification")
33
+ .sort(compareStrings);
34
+ for (const id of ids)
35
+ inScope.set(id, []);
36
+ const ordered = [...records].sort((a, b) => compareStrings(a.evidenceId, b.evidenceId) || compareRecords(a, b));
37
+ for (const record of ordered) {
38
+ const target = graph.artifacts.get(record.verificationId);
39
+ if (target === undefined || target.meta.type !== "verification") {
40
+ unknown.push(record);
41
+ diagnostics.push(evidenceDiagnostic({
42
+ code: "EVID002",
43
+ severity: "warning",
44
+ artifact: null,
45
+ path,
46
+ message: target === undefined
47
+ ? `Run record ${record.evidenceId} names ${record.verificationId}, which is not in the graph, so it is evidence for nothing.`
48
+ : `Run record ${record.evidenceId} names ${record.verificationId}, which is a ${target.meta.type}, not a verification.`,
49
+ remediation: "Point the record at the verification it ran, or create that verification; runs are evidence for verifications only (Core §8).",
50
+ }));
51
+ continue;
52
+ }
53
+ if (target.meta.verification?.method === "manual" && record.reviewer === null) {
54
+ const reason = `${record.verificationId} is a manual review, and record ${record.evidenceId} names no reviewer.`;
55
+ rejected.push({ record, reason });
56
+ diagnostics.push(evidenceDiagnostic({
57
+ code: "EVID001",
58
+ artifact: record.verificationId,
59
+ path,
60
+ message: `Run record ${record.evidenceId} names no reviewer, but ${record.verificationId} is a manual review.`,
61
+ remediation: "Record who performed the review and the rationale; a manual record must identify both (Core §8).",
62
+ }));
63
+ continue;
64
+ }
65
+ if (scope !== null && (record.scope.product !== scope.product || record.scope.release !== scope.release)) {
66
+ ignoredOutOfScope++;
67
+ outOfScope.set(record.verificationId, (outOfScope.get(record.verificationId) ?? 0) + 1);
68
+ continue;
69
+ }
70
+ inScope.get(record.verificationId)?.push(record);
71
+ }
72
+ const verifications = {};
73
+ for (const id of ids) {
74
+ const artifact = graph.artifacts.get(id);
75
+ const history = (inScope.get(id) ?? []).sort(compareRecords);
76
+ verifications[id] = classifyOne(id, artifact?.meta.verification?.method ?? "automated", history, outOfScope.get(id) ?? 0, snapshot);
77
+ }
78
+ return {
79
+ snapshot: { commit: snapshot.commit, graphHash: snapshot.graphHash, scope },
80
+ verifications,
81
+ ignoredOutOfScope,
82
+ unknown,
83
+ rejected,
84
+ diagnostics: sortDiagnostics(diagnostics),
85
+ };
86
+ }
87
+ /** True when a record was made at the snapshot: same graph hash, and same commit unless the snapshot's is unknown. */
88
+ export function atSnapshot(record, snapshot) {
89
+ return record.graphHash === snapshot.graphHash && (snapshot.commit === null || record.commit === snapshot.commit);
90
+ }
91
+ function classifyOne(verificationId, method, history, outOfScope, snapshot) {
92
+ const counts = { pass: 0, fail: 0, skip: 0, error: 0 };
93
+ const base = { verificationId, method, history, atSnapshot: counts, outOfScope };
94
+ const ignored = outOfScope > 0 ? `${outOfScope} record${outOfScope === 1 ? "" : "s"} for another product or release ignored` : null;
95
+ if (history.length === 0) {
96
+ return withNote({ ...base, status: "missing", latest: null }, [ignored]);
97
+ }
98
+ const unbound = history.filter((record) => record.graphHash === CURRENT || (snapshot.commit !== null && record.commit === CURRENT));
99
+ if (unbound.length > 0) {
100
+ const names = unbound.map((record) => record.evidenceId).join(", ");
101
+ return withNote({ ...base, status: "unresolved", latest: history[history.length - 1] }, [
102
+ `unbound @current placeholder in ${names}; bind it to the snapshot before classifying`,
103
+ ignored,
104
+ ]);
105
+ }
106
+ const current = history.filter((record) => atSnapshot(record, snapshot));
107
+ for (const record of current)
108
+ counts[record.result]++;
109
+ if (current.length === 0) {
110
+ return withNote({ ...base, status: "stale", latest: history[history.length - 1] }, [ignored]);
111
+ }
112
+ const latest = current[current.length - 1];
113
+ const status = latest.result === "pass" ? "current-pass" : latest.result === "fail" ? "current-fail" : latest.result;
114
+ return withNote({ ...base, status, latest }, [snapshot.commit === null ? NOTE_COMMIT_UNKNOWN : null, ignored]);
115
+ }
116
+ function withNote(evidence, notes) {
117
+ const present = notes.filter((note) => note !== null);
118
+ const { verificationId, method, status, latest, history, atSnapshot: counts, outOfScope } = evidence;
119
+ return {
120
+ verificationId,
121
+ method,
122
+ status,
123
+ ...(present.length > 0 ? { note: present.join("; ") } : {}),
124
+ latest,
125
+ history,
126
+ atSnapshot: counts,
127
+ outOfScope,
128
+ };
129
+ }
130
+ //# sourceMappingURL=classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../src/classify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAA+B,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAiD,MAAM,cAAc,CAAC;AAEtG,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,cAAc;IACd,cAAc;IACd,OAAO;IACP,MAAM;IACN,OAAO;IACP,SAAS;IACT,YAAY;CACJ,CAAC;AAuDX,MAAM,mBAAmB,GAAG,uCAAuC,CAAC;AAEpE,kHAAkH;AAClH,MAAM,UAAU,gBAAgB,CAC9B,KAAY,EACZ,OAA6B,EAC7B,QAA0B,EAC1B,OAAO,GAAoB,EAAE;IAE7B,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC;IAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC;IACrC,MAAM,WAAW,GAAiB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAE1B,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;SACpC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC;SACrE,IAAI,CAAC,cAAc,CAAC,CAAC;IACxB,KAAK,MAAM,EAAE,IAAI,GAAG;QAAE,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChH,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAC1D,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,WAAW,CAAC,IAAI,CACd,kBAAkB,CAAC;gBACjB,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,IAAI;gBACd,IAAI;gBACJ,OAAO,EACL,MAAM,KAAK,SAAS;oBAClB,CAAC,CAAC,cAAc,MAAM,CAAC,UAAU,UAAU,MAAM,CAAC,cAAc,6DAA6D;oBAC7H,CAAC,CAAC,cAAc,MAAM,CAAC,UAAU,UAAU,MAAM,CAAC,cAAc,gBAAgB,MAAM,CAAC,IAAI,CAAC,IAAI,uBAAuB;gBAC3H,WAAW,EACT,+HAA+H;aAClI,CAAC,CACH,CAAC;YACF,SAAS;QACX,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC9E,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,mCAAmC,MAAM,CAAC,UAAU,qBAAqB,CAAC;YACjH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YAClC,WAAW,CAAC,IAAI,CACd,kBAAkB,CAAC;gBACjB,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,MAAM,CAAC,cAAc;gBAC/B,IAAI;gBACJ,OAAO,EAAE,cAAc,MAAM,CAAC,UAAU,2BAA2B,MAAM,CAAC,cAAc,sBAAsB;gBAC9G,WAAW,EACT,kGAAkG;aACrG,CAAC,CACH,CAAC;YACF,SAAS;QACX,CAAC;QACD,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACzG,iBAAiB,EAAE,CAAC;YACpB,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACxF,SAAS;QACX,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,aAAa,GAAyC,EAAE,CAAC;IAC/D,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7D,aAAa,CAAC,EAAE,CAAC,GAAG,WAAW,CAC7B,EAAE,EACF,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,IAAI,WAAW,EAClD,OAAO,EACP,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EACvB,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE;QAC3E,aAAa;QACb,iBAAiB;QACjB,OAAO;QACP,QAAQ;QACR,WAAW,EAAE,eAAe,CAAC,WAAW,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,UAAU,CAAC,MAAiB,EAAE,QAAsD;IAClG,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC;AACpH,CAAC;AAED,SAAS,WAAW,CAClB,cAAsB,EACtB,MAA8B,EAC9B,OAAoB,EACpB,UAAkB,EAClB,QAA0B;IAE1B,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACrE,MAAM,IAAI,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACjF,MAAM,OAAO,GACX,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,UAAU,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,yCAAyC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEtH,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,QAAQ,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAC5B,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,KAAK,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,CACpG,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,OAAO,QAAQ,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE;YACtF,mCAAmC,KAAK,8CAA8C;YACtF,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzE,KAAK,MAAM,MAAM,IAAI,OAAO;QAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IACtD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,QAAQ,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,MAAM,GACV,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;IACxG,OAAO,QAAQ,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACjH,CAAC;AAED,SAAS,QAAQ,CAAC,QAA4C,EAAE,KAAwB;IACtF,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACtE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC;IACrG,OAAO;QACL,cAAc;QACd,MAAM;QACN,MAAM;QACN,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,MAAM;QACN,OAAO;QACP,UAAU,EAAE,MAAM;QAClB,UAAU;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Coverage (Core §8, §10; invariants 3 and 6). Behaviors, rules and scenarios
3
+ * are each claims that need their own coverage: a passing behavior does not
4
+ * cover its rules or its scenarios. Two things are counted and never merged:
5
+ * link coverage (a verification definition names the claim in `verifies`) and
6
+ * current-pass coverage (every applicable verification linked to the claim has
7
+ * a pass at the snapshot). Manual and automated are counted separately again.
8
+ * Every count travels with its snapshot and denominator, and nothing is ever
9
+ * reduced to a single percentage.
10
+ */
11
+ import { type Graph, type Level, type Status } from "@intentset/core";
12
+ import type { Classification, EvidenceStatus } from "./classify.ts";
13
+ export declare const CLAIM_TYPES: readonly ["behavior", "rule", "scenario"];
14
+ export type ClaimType = (typeof CLAIM_TYPES)[number];
15
+ export interface ClaimCounts {
16
+ behaviors: number;
17
+ rules: number;
18
+ scenarios: number;
19
+ }
20
+ export interface LinkedVerification {
21
+ id: string;
22
+ method: "automated" | "manual";
23
+ status: EvidenceStatus;
24
+ }
25
+ export interface MethodCoverage {
26
+ /** At least one verification of this method names the claim. */
27
+ linked: boolean;
28
+ /** At least one does, and every one that does has a current pass. */
29
+ verified: boolean;
30
+ }
31
+ export interface ClaimCoverage {
32
+ id: string;
33
+ type: ClaimType;
34
+ path: string;
35
+ /** The claim's lifecycle status: an editorial claim, never a test result (Core §7). */
36
+ lifecycle: Status;
37
+ /** True when the level asks for evidence (L3 and above) and the claim is not a draft. */
38
+ required: boolean;
39
+ /** Applicable verifications naming this claim in `verifies`, by ID. Retired verifications are not applicable. */
40
+ verifications: LinkedVerification[];
41
+ linked: boolean;
42
+ /** At least one applicable verification, and every one of them is a current pass. */
43
+ verified: boolean;
44
+ manual: MethodCoverage;
45
+ automated: MethodCoverage;
46
+ }
47
+ export interface Unresolved {
48
+ /** What could not be checked: an artifact ID, or a word for a repository-wide gap such as "scope". */
49
+ subject: string;
50
+ reason: string;
51
+ }
52
+ export interface CoverageReport {
53
+ snapshot: Classification["snapshot"];
54
+ level: Level;
55
+ /** What each count measures, stated with the counts so no reader has to infer it. */
56
+ measures: {
57
+ linked: string;
58
+ verified: string;
59
+ };
60
+ /** Behaviors, rules and scenarios that are not retired: the denominator of every count below. */
61
+ denominator: ClaimCounts;
62
+ /** Claims outside the denominator, and why. */
63
+ excluded: {
64
+ retired: ClaimCounts;
65
+ };
66
+ linked: ClaimCounts;
67
+ verified: ClaimCounts;
68
+ manual: {
69
+ linked: ClaimCounts;
70
+ verified: ClaimCounts;
71
+ };
72
+ automated: {
73
+ linked: ClaimCounts;
74
+ verified: ClaimCounts;
75
+ };
76
+ /** One entry per claim in the denominator, by ID. */
77
+ perClaim: ClaimCoverage[];
78
+ /** What could not be checked. Never read as pass. */
79
+ unresolved: Unresolved[];
80
+ }
81
+ export interface CoverageOptions {
82
+ level?: Level;
83
+ }
84
+ export declare function zeroCounts(): ClaimCounts;
85
+ /** Core §8: link coverage and current-pass coverage of every behavior, rule and scenario, counted separately. */
86
+ export declare function coverage(graph: Graph, classification: Classification, options?: CoverageOptions): CoverageReport;
87
+ export declare function compareUnresolved(a: Unresolved, b: Unresolved): number;
88
+ //# sourceMappingURL=coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../src/coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,KAAK,EAAiB,KAAK,KAAK,EAAU,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpE,eAAO,MAAM,WAAW,YAAI,UAAU,EAAE,MAAM,EAAE,UAAU,CAAU,CAAC;AACrE,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,MAAM,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,QAAQ,EAAE,OAAO,CAAC;IAClB,iHAAiH;IACjH,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,qFAAqF;IACrF,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,sGAAsG;IACtG,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,qFAAqF;IACrF,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,iGAAiG;IACjG,WAAW,EAAE,WAAW,CAAC;IACzB,+CAA+C;IAC/C,QAAQ,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,CAAC;IACvD,SAAS,EAAE;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,CAAC;IAC1D,qDAAqD;IACrD,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,qDAAqD;IACrD,UAAU,EAAE,UAAU,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAID,wBAAgB,UAAU,IAAI,WAAW,CAExC;AAED,iHAAiH;AACjH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,OAAO,GAAE,eAAoB,GAAG,cAAc,CAuFpH;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,MAAM,CAEtE"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Coverage (Core §8, §10; invariants 3 and 6). Behaviors, rules and scenarios
3
+ * are each claims that need their own coverage: a passing behavior does not
4
+ * cover its rules or its scenarios. Two things are counted and never merged:
5
+ * link coverage (a verification definition names the claim in `verifies`) and
6
+ * current-pass coverage (every applicable verification linked to the claim has
7
+ * a pass at the snapshot). Manual and automated are counted separately again.
8
+ * Every count travels with its snapshot and denominator, and nothing is ever
9
+ * reduced to a single percentage.
10
+ */
11
+ import { incomingEdges, LEVELS } from "@intentset/core";
12
+ import { compareStrings } from "./diagnostic.js";
13
+ export const CLAIM_TYPES = ["behavior", "rule", "scenario"];
14
+ const PLURAL = { behavior: "behaviors", rule: "rules", scenario: "scenarios" };
15
+ export function zeroCounts() {
16
+ return { behaviors: 0, rules: 0, scenarios: 0 };
17
+ }
18
+ /** Core §8: link coverage and current-pass coverage of every behavior, rule and scenario, counted separately. */
19
+ export function coverage(graph, classification, options = {}) {
20
+ const level = options.level ?? "L1";
21
+ const evidenceRequired = LEVELS.indexOf(level) >= LEVELS.indexOf("L3");
22
+ const report = {
23
+ snapshot: classification.snapshot,
24
+ level,
25
+ measures: {
26
+ linked: "links: claims named in verifies by at least one applicable verification definition",
27
+ verified: "evidence: claims whose every applicable verification has a current pass at the snapshot",
28
+ },
29
+ denominator: zeroCounts(),
30
+ excluded: { retired: zeroCounts() },
31
+ linked: zeroCounts(),
32
+ verified: zeroCounts(),
33
+ manual: { linked: zeroCounts(), verified: zeroCounts() },
34
+ automated: { linked: zeroCounts(), verified: zeroCounts() },
35
+ perClaim: [],
36
+ unresolved: [],
37
+ };
38
+ for (const id of [...graph.artifacts.keys()].sort(compareStrings)) {
39
+ const artifact = graph.artifacts.get(id);
40
+ if (artifact === undefined)
41
+ continue;
42
+ const type = artifact.meta.type;
43
+ if (type !== "behavior" && type !== "rule" && type !== "scenario")
44
+ continue;
45
+ const key = PLURAL[type];
46
+ if (artifact.meta.status === "retired") {
47
+ report.excluded.retired[key]++;
48
+ continue;
49
+ }
50
+ report.denominator[key]++;
51
+ const verifications = [];
52
+ const sources = incomingEdges(graph, id, "verifies").map((edge) => edge.from);
53
+ for (const source of [...new Set(sources)].sort(compareStrings)) {
54
+ const verification = graph.artifacts.get(source);
55
+ if (verification === undefined || verification.meta.type !== "verification")
56
+ continue;
57
+ if (verification.meta.status === "retired")
58
+ continue;
59
+ const evidence = classification.verifications[source];
60
+ verifications.push({
61
+ id: source,
62
+ method: verification.meta.verification?.method ?? evidence?.method ?? "automated",
63
+ status: evidence?.status ?? "unresolved",
64
+ });
65
+ }
66
+ const byMethod = (method) => {
67
+ const linked = verifications.filter((v) => v.method === method);
68
+ return {
69
+ linked: linked.length > 0,
70
+ verified: linked.length > 0 && linked.every((v) => v.status === "current-pass"),
71
+ };
72
+ };
73
+ const claim = {
74
+ id,
75
+ type,
76
+ path: artifact.path,
77
+ lifecycle: artifact.meta.status,
78
+ required: evidenceRequired && artifact.meta.status !== "draft",
79
+ verifications,
80
+ linked: verifications.length > 0,
81
+ verified: verifications.length > 0 && verifications.every((v) => v.status === "current-pass"),
82
+ manual: byMethod("manual"),
83
+ automated: byMethod("automated"),
84
+ };
85
+ report.perClaim.push(claim);
86
+ if (claim.linked)
87
+ report.linked[key]++;
88
+ if (claim.verified)
89
+ report.verified[key]++;
90
+ for (const method of ["manual", "automated"]) {
91
+ if (claim[method].linked)
92
+ report[method].linked[key]++;
93
+ if (claim[method].verified)
94
+ report[method].verified[key]++;
95
+ }
96
+ }
97
+ if (classification.snapshot.scope === null) {
98
+ report.unresolved.push({
99
+ subject: "scope",
100
+ reason: "no product and release were requested, so records of every scope were considered",
101
+ });
102
+ }
103
+ for (const id of Object.keys(classification.verifications)) {
104
+ const evidence = classification.verifications[id];
105
+ if (evidence.status === "unresolved")
106
+ report.unresolved.push({ subject: id, reason: evidence.note ?? "evidence could not be classified" });
107
+ }
108
+ report.unresolved.sort(compareUnresolved);
109
+ return report;
110
+ }
111
+ export function compareUnresolved(a, b) {
112
+ return compareStrings(a.subject, b.subject) || compareStrings(a.reason, b.reason);
113
+ }
114
+ //# sourceMappingURL=coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../src/coverage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Evidence diagnostics in core's shape and key order (ADR 0004). Core keeps
3
+ * its constructor private, so this is the one place the evidence package
4
+ * builds a Diagnostic. Locations are never invented: a run record has no line
5
+ * in a Markdown file, and a JSON pointer into the evidence goes in `field`.
6
+ */
7
+ import type { Diagnostic, Severity } from "@intentset/core";
8
+ export interface EvidenceDiagnosticFields {
9
+ code: string;
10
+ severity?: Severity;
11
+ artifact: string | null;
12
+ path: string | null;
13
+ field?: string;
14
+ message: string;
15
+ remediation: string;
16
+ }
17
+ export declare function evidenceDiagnostic(fields: EvidenceDiagnosticFields): Diagnostic;
18
+ export declare function compareStrings(a: string, b: string): number;
19
+ /** RFC 6901 escaping of one pointer segment. */
20
+ export declare function pointerToken(segment: string | number): string;
21
+ //# sourceMappingURL=diagnostic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostic.d.ts","sourceRoot":"","sources":["../src/diagnostic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE5D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,UAAU,CAW/E;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAE7D"}
@@ -0,0 +1,20 @@
1
+ export function evidenceDiagnostic(fields) {
2
+ return {
3
+ code: fields.code,
4
+ severity: fields.severity ?? "error",
5
+ origin: "evidence",
6
+ artifact: fields.artifact,
7
+ path: fields.path,
8
+ ...(fields.field !== undefined ? { field: fields.field } : {}),
9
+ message: fields.message,
10
+ remediation: fields.remediation,
11
+ };
12
+ }
13
+ export function compareStrings(a, b) {
14
+ return a < b ? -1 : a > b ? 1 : 0;
15
+ }
16
+ /** RFC 6901 escaping of one pointer segment. */
17
+ export function pointerToken(segment) {
18
+ return String(segment).replace(/~/g, "~0").replace(/\//g, "~1");
19
+ }
20
+ //# sourceMappingURL=diagnostic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostic.js","sourceRoot":"","sources":["../src/diagnostic.ts"],"names":[],"mappings":"AAkBA,MAAM,UAAU,kBAAkB,CAAC,MAAgC;IACjE,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,OAAO;QACpC,MAAM,EAAE,UAAU;QAClB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,CAAS,EAAE,CAAS;IACjD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,YAAY,CAAC,OAAwB;IACnD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * How an evidence conformance case is run (tests/evidence.json), as one
3
+ * function, so the harness's driver and this package's own fixture test
4
+ * cannot differ. Another implementation reads the same contract here:
5
+ *
6
+ * 1. Validate the expanded tree at the case's level (core's pipeline).
7
+ * 2. The snapshot is `FIXTURE_COMMIT`, the graph hash of the validated graph,
8
+ * and the scope named by the case's `request.product` and
9
+ * `request.release` (no scope filter when either is absent).
10
+ * 3. Bind `@current` in the case's `evidence` to that snapshot, then read the
11
+ * records as if from `evidence.json`.
12
+ * 4. Check locators against the tree's paths (documents and sources) only
13
+ * when the case supplies `sources`; a case without them has no tree to
14
+ * check against, and its locators are reported unresolved instead.
15
+ * 5. The case's diagnostics are validation's plus the reader's plus the check's.
16
+ */
17
+ import { type Diagnostic, type Graph, type Level } from "@intentset/core";
18
+ import { type EvidenceCheck } from "./check.ts";
19
+ import type { EvidenceSnapshot } from "./classify.ts";
20
+ export declare const FIXTURE_EVIDENCE_PATH = "evidence.json";
21
+ export interface FixtureEvidenceInput {
22
+ /** The graph validation built from the expanded tree. */
23
+ graph: Graph;
24
+ level: Level;
25
+ /** The case's `evidence` array, placeholders unbound. */
26
+ evidence: readonly unknown[];
27
+ /** The case's `request`, read for its product and release only. */
28
+ request: {
29
+ product?: string;
30
+ release?: string;
31
+ } | null;
32
+ /** Paths of the expanded tree's documents. */
33
+ documents: Iterable<string>;
34
+ /** Paths of the case's `sources`. */
35
+ sources: Iterable<string>;
36
+ }
37
+ export interface FixtureEvidenceRun extends EvidenceCheck {
38
+ snapshot: EvidenceSnapshot;
39
+ /** The reader's diagnostics followed by the check's, sorted. Validation's are the caller's to add. */
40
+ diagnostics: Diagnostic[];
41
+ }
42
+ export declare function checkFixtureEvidence(input: FixtureEvidenceInput): FixtureEvidenceRun;
43
+ //# sourceMappingURL=fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,KAAK,EAAa,KAAK,KAAK,EAAmB,MAAM,iBAAiB,CAAC;AACtG,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,eAAO,MAAM,qBAAqB,kBAAkB,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,yDAAyD;IACzD,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,mEAAmE;IACnE,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACvD,8CAA8C;IAC9C,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,qCAAqC;IACrC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,sGAAsG;IACtG,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,kBAAkB,CAmBpF"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * How an evidence conformance case is run (tests/evidence.json), as one
3
+ * function, so the harness's driver and this package's own fixture test
4
+ * cannot differ. Another implementation reads the same contract here:
5
+ *
6
+ * 1. Validate the expanded tree at the case's level (core's pipeline).
7
+ * 2. The snapshot is `FIXTURE_COMMIT`, the graph hash of the validated graph,
8
+ * and the scope named by the case's `request.product` and
9
+ * `request.release` (no scope filter when either is absent).
10
+ * 3. Bind `@current` in the case's `evidence` to that snapshot, then read the
11
+ * records as if from `evidence.json`.
12
+ * 4. Check locators against the tree's paths (documents and sources) only
13
+ * when the case supplies `sources`; a case without them has no tree to
14
+ * check against, and its locators are reported unresolved instead.
15
+ * 5. The case's diagnostics are validation's plus the reader's plus the check's.
16
+ */
17
+ import { graphHash, sortDiagnostics } from "@intentset/core";
18
+ import { checkEvidence } from "./check.js";
19
+ import { bindPlaceholders, FIXTURE_COMMIT, requestedScope } from "./placeholders.js";
20
+ import { readRunRecords } from "./records.js";
21
+ export const FIXTURE_EVIDENCE_PATH = "evidence.json";
22
+ export function checkFixtureEvidence(input) {
23
+ const snapshot = {
24
+ commit: FIXTURE_COMMIT,
25
+ graphHash: graphHash(input.graph),
26
+ scope: requestedScope(input.request),
27
+ };
28
+ const read = readRunRecords(bindPlaceholders(input.evidence, snapshot), FIXTURE_EVIDENCE_PATH);
29
+ const sources = [...input.sources];
30
+ const files = sources.length > 0 ? new Set([...input.documents, ...sources]) : undefined;
31
+ const checked = checkEvidence(input.graph, read.records, snapshot, {
32
+ level: input.level,
33
+ files,
34
+ evidencePath: FIXTURE_EVIDENCE_PATH,
35
+ });
36
+ return {
37
+ ...checked,
38
+ snapshot,
39
+ diagnostics: sortDiagnostics([...read.diagnostics, ...checked.diagnostics]),
40
+ };
41
+ }
42
+ //# sourceMappingURL=fixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture.js","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAA+B,SAAS,EAAc,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACtG,OAAO,EAAsB,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/D,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,eAAe,CAAC;AAsBrD,MAAM,UAAU,oBAAoB,CAAC,KAA2B;IAC9D,MAAM,QAAQ,GAAqB;QACjC,MAAM,EAAE,cAAc;QACtB,SAAS,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;QACjC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC;KACrC,CAAC;IACF,MAAM,IAAI,GAAG,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAC/F,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACzF,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE;QACjE,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,KAAK;QACL,YAAY,EAAE,qBAAqB;KACpC,CAAC,CAAC;IACH,OAAO;QACL,GAAG,OAAO;QACV,QAAQ;QACR,WAAW,EAAE,eAAe,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;KAC5E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @intentset/verification: run records, evidence freshness, coverage and
3
+ * test-reporter adapters (Core §8, §10, §11; milestone M3). Definitions are
4
+ * not evidence: only a pass at the assessed commit and graph hash is current,
5
+ * and link coverage and current-pass coverage are always counted apart.
6
+ */
7
+ export * from "./adapters/common.ts";
8
+ export * from "./adapters/node-test.ts";
9
+ export * from "./adapters/vitest.ts";
10
+ export * from "./check.ts";
11
+ export * from "./classify.ts";
12
+ export * from "./coverage.ts";
13
+ export * from "./fixture.ts";
14
+ export * from "./placeholders.ts";
15
+ export * from "./records.ts";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @intentset/verification: run records, evidence freshness, coverage and
3
+ * test-reporter adapters (Core §8, §10, §11; milestone M3). Definitions are
4
+ * not evidence: only a pass at the assessed commit and graph hash is current,
5
+ * and link coverage and current-pass coverage are always counted apart.
6
+ */
7
+ export * from "./adapters/common.js";
8
+ export * from "./adapters/node-test.js";
9
+ export * from "./adapters/vitest.js";
10
+ export * from "./check.js";
11
+ export * from "./classify.js";
12
+ export * from "./coverage.js";
13
+ export * from "./fixture.js";
14
+ export * from "./placeholders.js";
15
+ export * from "./records.js";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The `@current` placeholder contract, for fixtures and the harness.
3
+ *
4
+ * A conformance case cannot know its graph hash in advance: the hash is
5
+ * computed over the tree the case expands to. So a fixture writes the literal
6
+ * `@current` for `commit` and `graphHash` when it means "the snapshot under
7
+ * assessment", and the driver binds it with `bindPlaceholders` before reading
8
+ * the records. Anything else (a 40-character commit, a 64-character hash) is
9
+ * taken literally, which is how a fixture says "stale".
10
+ *
11
+ * Binding is the caller's job and is never silent: a record that still
12
+ * carries `@current` when it is classified is `unresolved`, not current.
13
+ */
14
+ import { CURRENT, type RunScope } from "./records.ts";
15
+ export { CURRENT };
16
+ /**
17
+ * The commit the evidence fixtures are assessed at. Any value works so long
18
+ * as no fixture writes it literally; fixtures write `@current` instead, and
19
+ * their stale records use other commits.
20
+ */
21
+ export declare const FIXTURE_COMMIT = "1234567890abcdef1234567890abcdef12345678";
22
+ export interface PlaceholderSnapshot {
23
+ commit: string | null;
24
+ graphHash: string;
25
+ }
26
+ /**
27
+ * Replace `@current` in each record's `commit` and `graphHash` with the
28
+ * snapshot's values. Works on raw parsed JSON as well as on read records, and
29
+ * returns copies: the input is not changed. A null snapshot commit leaves a
30
+ * `@current` commit in place, since there is nothing to bind it to; matching
31
+ * then uses the graph hash alone (classify.ts).
32
+ */
33
+ export declare function bindPlaceholders<T>(records: readonly T[], snapshot: PlaceholderSnapshot): T[];
34
+ /** The scope a fixture requests, from the case's `request` product and release; null unless both are named. */
35
+ export declare function requestedScope(request: {
36
+ product?: string;
37
+ release?: string;
38
+ } | null | undefined): RunScope | null;
39
+ //# sourceMappingURL=placeholders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../src/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB;;;;GAIG;AACH,eAAO,MAAM,cAAc,6CAA6C,CAAC;AAEzE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,mBAAmB,GAAG,CAAC,EAAE,CAQ7F;AAED,+GAA+G;AAC/G,wBAAgB,cAAc,CAAC,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAGlH"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The `@current` placeholder contract, for fixtures and the harness.
3
+ *
4
+ * A conformance case cannot know its graph hash in advance: the hash is
5
+ * computed over the tree the case expands to. So a fixture writes the literal
6
+ * `@current` for `commit` and `graphHash` when it means "the snapshot under
7
+ * assessment", and the driver binds it with `bindPlaceholders` before reading
8
+ * the records. Anything else (a 40-character commit, a 64-character hash) is
9
+ * taken literally, which is how a fixture says "stale".
10
+ *
11
+ * Binding is the caller's job and is never silent: a record that still
12
+ * carries `@current` when it is classified is `unresolved`, not current.
13
+ */
14
+ import { CURRENT } from "./records.js";
15
+ export { CURRENT };
16
+ /**
17
+ * The commit the evidence fixtures are assessed at. Any value works so long
18
+ * as no fixture writes it literally; fixtures write `@current` instead, and
19
+ * their stale records use other commits.
20
+ */
21
+ export const FIXTURE_COMMIT = "1234567890abcdef1234567890abcdef12345678";
22
+ /**
23
+ * Replace `@current` in each record's `commit` and `graphHash` with the
24
+ * snapshot's values. Works on raw parsed JSON as well as on read records, and
25
+ * returns copies: the input is not changed. A null snapshot commit leaves a
26
+ * `@current` commit in place, since there is nothing to bind it to; matching
27
+ * then uses the graph hash alone (classify.ts).
28
+ */
29
+ export function bindPlaceholders(records, snapshot) {
30
+ return records.map((record) => {
31
+ if (typeof record !== "object" || record === null || Array.isArray(record))
32
+ return record;
33
+ const copy = { ...record };
34
+ if (copy.graphHash === CURRENT)
35
+ copy.graphHash = snapshot.graphHash;
36
+ if (copy.commit === CURRENT && snapshot.commit !== null)
37
+ copy.commit = snapshot.commit;
38
+ return copy;
39
+ });
40
+ }
41
+ /** The scope a fixture requests, from the case's `request` product and release; null unless both are named. */
42
+ export function requestedScope(request) {
43
+ if (request?.product === undefined || request.release === undefined)
44
+ return null;
45
+ return { product: request.product, release: request.release };
46
+ }
47
+ //# sourceMappingURL=placeholders.js.map