mneme-ai 0.33.0 → 0.35.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.
@@ -12,9 +12,31 @@ export interface AuditOptions {
12
12
  explain?: boolean;
13
13
  /** Test seam: inject an enricher factory so unit tests don't hit the network. */
14
14
  explainEnricherFactory?: ExplainRequest["enricherFactory"];
15
+ /**
16
+ * --strict: treat `skipped` axes as `fail`. For compliance environments
17
+ * where missing data IS a failure (e.g. no test command defined =
18
+ * cannot ship).
19
+ */
20
+ strict?: boolean;
15
21
  }
16
22
  /** Top-level dispatcher. */
17
23
  export declare function auditCommand(opts: AuditOptions): Promise<number>;
24
+ /**
25
+ * Render a forensic-grade markdown report.
26
+ *
27
+ * Every axis surfaces:
28
+ * • verdict (pass / warn / fail / skipped)
29
+ * • reason (one-liner)
30
+ * • confidence rating
31
+ * • structured evidence bullets (the FACTS — counts, hashes, paths,
32
+ * deltas — that back the verdict)
33
+ * • caveat (the "ⓘ" line declaring what this axis does NOT check)
34
+ *
35
+ * The headline carries coverage (X/5 axes verified) so the user
36
+ * immediately sees how trustworthy the verdict is. Sniper-accuracy:
37
+ * a "PASS · 2/5 verified · low confidence" report is a yellow flag,
38
+ * not a green light.
39
+ */
18
40
  export declare function renderMarkdownReport(cert: audit.AuditCertificate): string;
19
41
  /** Run `git show` for one commit; return unified diff + files touched. */
20
42
  export declare function collectDiffForCommit(repoRoot: string, hash: string): {
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,EAAO,MAAM,gBAAgB,CAAC;AAG5C,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IACvE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;CAC5D;AAED,4BAA4B;AAC5B,wBAAsB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBtE;AA2cD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,gBAAgB,GAAG,MAAM,CAwCzE;AAID,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAuB1C"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,EAAO,MAAM,gBAAgB,CAAC;AAG5C,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IACvE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC3D;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,4BAA4B;AAC5B,wBAAsB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBtE;AAwfD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,gBAAgB,GAAG,MAAM,CAsHzE;AAID,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAuB1C"}
@@ -12,38 +12,109 @@ function mkCert(over = {}) {
12
12
  sessionId: "abc1234",
13
13
  capturedAt: "2026-05-07T12:00:00Z",
14
14
  axes: {
15
- behavioralParity: { verdict: "pass", reason: "all sample commands match the baseline", details: [] },
16
- apiContractDrift: { verdict: "pass", reason: "API surface identical", details: [] },
15
+ behavioralParity: {
16
+ verdict: "pass",
17
+ reason: "all sample commands match the baseline",
18
+ details: [],
19
+ evidence: [
20
+ { label: "git_head", value: "exit 0 · 1 line · sha abc12345", ok: true },
21
+ { label: "node_version", value: "exit 0 · 1 line · sha def67890", ok: true },
22
+ ],
23
+ caveat: "Sampling: 3 of 12 commands a real CI would run.",
24
+ confidence: "medium",
25
+ },
26
+ apiContractDrift: {
27
+ verdict: "pass",
28
+ reason: "API surface identical (47 exports, hash matches)",
29
+ details: [],
30
+ evidence: [
31
+ { label: "exports scanned", value: "47 across 5 package(s)" },
32
+ { label: "added", value: "0", ok: true },
33
+ { label: "removed", value: "0", ok: true },
34
+ { label: "surface hash (after)", value: "sha256:xyz789abc" },
35
+ ],
36
+ caveat: "Surface = top-level public exports.",
37
+ confidence: "high",
38
+ },
17
39
  testPassRate: {
18
40
  verdict: "pass",
19
41
  reason: "no new test failures",
42
+ details: [],
20
43
  before: "100 passed / 0 failed (5 files)",
21
44
  after: "100 passed / 0 failed (5 files)",
45
+ evidence: [
46
+ { label: "before", value: "100 passed / 0 failed (5 files)" },
47
+ { label: "after", value: "100 passed / 0 failed (5 files)", ok: true },
48
+ { label: "delta", value: "+0 passed · +0 failed · +0 file(s)", ok: true },
49
+ ],
50
+ confidence: "high",
22
51
  },
23
- perfRegression: { verdict: "pass", reason: "no perf regression", deltaPercent: 0 },
24
- aiNarrative: { verdict: "pass", reason: "narrative trust 1.00", checks: [] },
52
+ perfRegression: {
53
+ verdict: "pass",
54
+ reason: "no perf regression",
55
+ details: [],
56
+ deltaPercent: 0,
57
+ evidence: [
58
+ { label: "git_head", value: "baseline 5 ms → current 5 ms (+0%)", ok: true },
59
+ ],
60
+ confidence: "medium",
61
+ },
62
+ aiNarrative: {
63
+ verdict: "pass",
64
+ reason: "narrative trust 1.00",
65
+ details: [],
66
+ checks: [],
67
+ evidence: [
68
+ { label: "AI commits checked", value: "1", ok: true },
69
+ { label: "claims verified", value: "2", ok: true },
70
+ ],
71
+ confidence: "high",
72
+ },
73
+ },
74
+ forensicAxes: {
75
+ size: { verdict: "pass", score: 0.1, reason: "well within median", evidence: [{ label: "score", value: "0.10" }] },
76
+ files: { verdict: "pass", score: 0.1, reason: "all files seen", evidence: [{ label: "score", value: "0.10" }] },
77
+ style: { verdict: "pass", score: 0.1, reason: "verb in vocab", evidence: [{ label: "score", value: "0.10" }] },
78
+ time: { verdict: "pass", score: 0.1, reason: "in window", evidence: [{ label: "score", value: "0.10" }] },
25
79
  },
26
- forensicAxes: { size: "pass", files: "pass", style: "pass", time: "pass" },
27
80
  overallVerdict: "pass",
81
+ coverage: { verified: 5, skipped: 0, total: 5, confidence: "high" },
28
82
  exitCode: 0,
29
83
  ...over,
30
84
  };
31
85
  }
32
- describe("mneme audit — markdown report", () => {
86
+ describe("mneme audit — markdown report (forensic-grade)", () => {
33
87
  it("renders a valid markdown skeleton for a passing certificate", () => {
34
88
  const md = renderMarkdownReport(mkCert());
35
89
  expect(md).toContain("# AI Audit Trust Certificate");
36
90
  expect(md).toContain("`abc1234`");
37
91
  expect(md).toContain("PASS");
38
- expect(md).toContain("| Axis | Verdict | Reason |");
92
+ // Headline carries coverage + confidence
93
+ expect(md).toContain("5/5 axes verified");
94
+ expect(md).toContain("high confidence");
95
+ // Verdict table
96
+ expect(md).toContain("| Axis | Verdict | Confidence | Reason |");
39
97
  expect(md).toContain("Behavioral parity");
40
98
  expect(md).toContain("API contract drift");
41
99
  expect(md).toContain("Test pass rate");
42
100
  expect(md).toContain("Perf regression");
43
101
  expect(md).toContain("AI narrative");
102
+ // Per-axis evidence section
103
+ expect(md).toContain("## Per-Axis Evidence");
44
104
  expect(md).toContain("## Forensic Axes");
45
105
  });
46
- it("renders FAIL prominently when overall verdict is fail", () => {
106
+ it("renders evidence bullets per axis (the FACTS — sniper-grade)", () => {
107
+ const md = renderMarkdownReport(mkCert());
108
+ // Every axis gets evidence bullets in the report
109
+ expect(md).toContain("**git_head**");
110
+ expect(md).toContain("**exports scanned**");
111
+ expect(md).toContain("**before**");
112
+ expect(md).toContain("**after**");
113
+ expect(md).toContain("**AI commits checked**");
114
+ // Caveats surface as italic ⓘ lines
115
+ expect(md).toContain("ⓘ Sampling");
116
+ });
117
+ it("FAIL prominently when overall verdict is fail; reason in the table", () => {
47
118
  const cert = mkCert({
48
119
  overallVerdict: "fail",
49
120
  exitCode: 1,
@@ -53,6 +124,11 @@ describe("mneme audit — markdown report", () => {
53
124
  verdict: "fail",
54
125
  reason: "1 export(s) removed — silent breaking change",
55
126
  details: ["removed: core.foo"],
127
+ evidence: [
128
+ { label: "removed", value: "1", ok: false },
129
+ { label: "removed (sample)", value: "core.foo", ok: false },
130
+ ],
131
+ confidence: "high",
56
132
  },
57
133
  },
58
134
  });
@@ -60,6 +136,21 @@ describe("mneme audit — markdown report", () => {
60
136
  expect(md).toContain("FAIL");
61
137
  expect(md).toContain("(exit 1)");
62
138
  expect(md).toContain("silent breaking change");
139
+ expect(md).toContain("**removed**");
140
+ });
141
+ it("INSUFFICIENT DATA tripwire surfaces in the header — refuses to certify", () => {
142
+ const cert = mkCert({
143
+ overallVerdict: "warn",
144
+ coverage: { verified: 0, skipped: 5, total: 5, confidence: "low" },
145
+ insufficientData: {
146
+ reason: "no AI-attributed commits AND no measurable change",
147
+ hint: "Capture a baseline, run an AI session, then re-run --certify.",
148
+ },
149
+ });
150
+ const md = renderMarkdownReport(cert);
151
+ expect(md).toContain("INSUFFICIENT DATA");
152
+ expect(md).toContain("Capture a baseline");
153
+ expect(md).toContain("0/5 axes verified");
63
154
  });
64
155
  it("includes per-commit narrative checks when present", () => {
65
156
  const cert = mkCert({
@@ -68,6 +159,7 @@ describe("mneme audit — markdown report", () => {
68
159
  aiNarrative: {
69
160
  verdict: "fail",
70
161
  reason: "1 commit-message claim(s) contradicted by diff",
162
+ details: [],
71
163
  checks: [
72
164
  {
73
165
  commitHash: "deadbeef0000",
@@ -83,6 +175,8 @@ describe("mneme audit — markdown report", () => {
83
175
  narrativeTrustScore: 0,
84
176
  },
85
177
  ],
178
+ evidence: [{ label: "claims contradicted", value: "1", ok: false }],
179
+ confidence: "high",
86
180
  },
87
181
  },
88
182
  overallVerdict: "fail",
@@ -94,12 +188,12 @@ describe("mneme audit — markdown report", () => {
94
188
  expect(md).toContain("contradicted");
95
189
  expect(md).toContain("no change to db.ts");
96
190
  });
97
- it("forensic axes are listed even when all pass", () => {
191
+ it("forensic axes are listed even when all pass (with reason)", () => {
98
192
  const md = renderMarkdownReport(mkCert());
99
- expect(md).toContain("- size: pass");
100
- expect(md).toContain("- files: pass");
101
- expect(md).toContain("- style: pass");
102
- expect(md).toContain("- time: pass");
193
+ expect(md).toContain("**size**");
194
+ expect(md).toContain("**files**");
195
+ expect(md).toContain("**style**");
196
+ expect(md).toContain("**time**");
103
197
  });
104
198
  it("exit code is preserved in the report header", () => {
105
199
  expect(renderMarkdownReport(mkCert({ exitCode: 0 }))).toContain("(exit 0)");
@@ -1 +1 @@
1
- {"version":3,"file":"audit.integration.test.js","sourceRoot":"","sources":["../../src/commands/audit.integration.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.integration.test.js","sourceRoot":"","sources":["../../src/commands/audit.integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKlD,SAAS,MAAM,CAAC,OAAkC,EAAE;IAClD,OAAO;QACL,SAAS,EAAE,SAAS;QACpB,UAAU,EAAE,sBAAsB;QAClC,IAAI,EAAE;YACJ,gBAAgB,EAAE;gBAChB,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,wCAAwC;gBAChD,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE;oBACR,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,gCAAgC,EAAE,EAAE,EAAE,IAAI,EAAE;oBACxE,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,gCAAgC,EAAE,EAAE,EAAE,IAAI,EAAE;iBAC7E;gBACD,MAAM,EAAE,iDAAiD;gBACzD,UAAU,EAAE,QAAQ;aACrB;YACD,gBAAgB,EAAE;gBAChB,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,kDAAkD;gBAC1D,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE;oBACR,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,wBAAwB,EAAE;oBAC7D,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE;oBACxC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE;oBAC1C,EAAE,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,kBAAkB,EAAE;iBAC7D;gBACD,MAAM,EAAE,qCAAqC;gBAC7C,UAAU,EAAE,MAAM;aACnB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,iCAAiC;gBACzC,KAAK,EAAE,iCAAiC;gBACxC,QAAQ,EAAE;oBACR,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,iCAAiC,EAAE;oBAC7D,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,iCAAiC,EAAE,EAAE,EAAE,IAAI,EAAE;oBACtE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,oCAAoC,EAAE,EAAE,EAAE,IAAI,EAAE;iBAC1E;gBACD,UAAU,EAAE,MAAM;aACnB;YACD,cAAc,EAAE;gBACd,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,oBAAoB;gBAC5B,OAAO,EAAE,EAAE;gBACX,YAAY,EAAE,CAAC;gBACf,QAAQ,EAAE;oBACR,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,oCAAoC,EAAE,EAAE,EAAE,IAAI,EAAE;iBAC7E;gBACD,UAAU,EAAE,QAAQ;aACrB;YACD,WAAW,EAAE;gBACX,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,EAAE;gBACV,QAAQ,EAAE;oBACR,EAAE,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE;oBACrD,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE;iBACnD;gBACD,UAAU,EAAE,MAAM;aACnB;SACF;QACD,YAAY,EAAE;YACZ,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE;YAClH,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE;YAC/G,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE;YAC9G,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE;SAC1G;QACD,cAAc,EAAE,MAAM;QACtB,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE;QACnE,QAAQ,EAAE,CAAC;QACX,GAAG,IAAI;KACR,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,EAAE,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACrD,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAClC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC7B,yCAAyC;QACzC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACxC,gBAAgB;QAChB,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,0CAA0C,CAAC,CAAC;QACjE,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAC3C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACvC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACxC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACrC,4BAA4B;QAC5B,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QAC7C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,EAAE,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1C,iDAAiD;QACjD,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACrC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACnC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAClC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QAC/C,oCAAoC;QACpC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,IAAI,GAAG,MAAM,CAAC;YAClB,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,CAAC;YACX,IAAI,EAAE;gBACJ,GAAG,MAAM,EAAE,CAAC,IAAI;gBAChB,gBAAgB,EAAE;oBAChB,OAAO,EAAE,MAAM;oBACf,MAAM,EAAE,8CAA8C;oBACtD,OAAO,EAAE,CAAC,mBAAmB,CAAC;oBAC9B,QAAQ,EAAE;wBACR,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE;wBAC3C,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE;qBAC5D;oBACD,UAAU,EAAE,MAAM;iBACnB;aACF;SACF,CAAC,CAAC;QACH,MAAM,EAAE,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACjC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QAC/C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,IAAI,GAAG,MAAM,CAAC;YAClB,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE;YAClE,gBAAgB,EAAE;gBAChB,MAAM,EAAE,mDAAmD;gBAC3D,IAAI,EAAE,+DAA+D;aACtE;SACF,CAAC,CAAC;QACH,MAAM,EAAE,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAC3C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAG,MAAM,CAAC;YAClB,IAAI,EAAE;gBACJ,GAAG,MAAM,EAAE,CAAC,IAAI;gBAChB,WAAW,EAAE;oBACX,OAAO,EAAE,MAAM;oBACf,MAAM,EAAE,gDAAgD;oBACxD,OAAO,EAAE,EAAE;oBACX,MAAM,EAAE;wBACN;4BACE,UAAU,EAAE,cAAc;4BAC1B,MAAM,EAAE,EAAE;4BACV,YAAY,EAAE,CAAC,WAAW,CAAC;4BAC3B,aAAa,EAAE;gCACb;oCACE,KAAK,EAAE,oBAAoB;oCAC3B,OAAO,EAAE,cAAc;oCACvB,MAAM,EAAE,oDAAoD;iCAC7D;6BACF;4BACD,mBAAmB,EAAE,CAAC;yBACvB;qBACF;oBACD,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;oBACnE,UAAU,EAAE,MAAM;iBACnB;aACF;YACD,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,CAAC;SACZ,CAAC,CAAC;QACH,MAAM,EAAE,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;QACvD,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;QACjD,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACrC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,EAAE,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACjC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAClC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAClC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC5E,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1D,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1D,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -202,7 +202,7 @@ function renderVerify(trace, checks) {
202
202
  // ─── --certify ───────────────────────────────────────────────────────
203
203
  async function runCertify(opts) {
204
204
  const meta = await git.getRepoMeta(opts.cwd);
205
- const cert = await runFullCertifyPipeline(meta.rootPath);
205
+ const cert = await runFullCertifyPipeline(meta.rootPath, { strict: opts.strict });
206
206
  if (!cert) {
207
207
  ui.error("No baseline. Run `mneme audit --baseline` first.");
208
208
  return 1;
@@ -261,7 +261,7 @@ function certificateToExplainPrompt(cert) {
261
261
  };
262
262
  return JSON.stringify(compact, null, 2);
263
263
  }
264
- async function runFullCertifyPipeline(repoRoot) {
264
+ async function runFullCertifyPipeline(repoRoot, opts = {}) {
265
265
  const baseline = audit.loadBaseline(repoRoot);
266
266
  if (!baseline)
267
267
  return null;
@@ -277,6 +277,7 @@ async function runFullCertifyPipeline(repoRoot) {
277
277
  afterBaseline: after,
278
278
  trace,
279
279
  diffs,
280
+ strict: opts.strict,
280
281
  });
281
282
  }
282
283
  function renderCertificate(cert, explainSection = null, explainHeadsUp = null) {
@@ -285,45 +286,80 @@ function renderCertificate(cert, explainSection = null, explainHeadsUp = null) {
285
286
  : cert.overallVerdict === "warn"
286
287
  ? pill("WARN", "warn")
287
288
  : pill("PASS", "ok");
289
+ // Confidence pill — high (4+ axes verified) / medium (3) / low (≤2).
290
+ const confBadge = (() => {
291
+ switch (cert.coverage.confidence) {
292
+ case "high": return kleur.green("high confidence");
293
+ case "medium": return kleur.yellow("medium confidence");
294
+ case "low": return kleur.red("low confidence");
295
+ }
296
+ })();
297
+ // Headline carries the coverage summary so the user sees verified/skipped
298
+ // up front — never just "PASS · exit 0".
288
299
  const headline = (() => {
300
+ if (cert.insufficientData) {
301
+ return `🔍 AI Audit · INSUFFICIENT DATA · refusing to certify`;
302
+ }
303
+ const cov = `${cert.coverage.verified}/${cert.coverage.total} axes verified`;
304
+ const skip = cert.coverage.skipped > 0
305
+ ? ` · ${cert.coverage.skipped} skipped (insufficient data)`
306
+ : "";
289
307
  if (cert.overallVerdict === "fail") {
290
- return `🔍 AI Audit · FAIL · review before merging`;
308
+ return `🔍 AI Audit · FAIL · ${cov}${skip} · review evidence below`;
291
309
  }
292
310
  if (cert.overallVerdict === "warn") {
293
- return `🔍 AI Audit · warn · 1 or more axes drifted`;
311
+ return `🔍 AI Audit · WARN · ${cov}${skip} · review evidence below`;
294
312
  }
295
- return `🔍 AI Audit · PASS · trust certificate issued`;
313
+ return `🔍 AI Audit · PASS · ${cov}${skip} · ${cert.coverage.confidence} confidence`;
296
314
  })();
297
315
  const ledeLines = [
298
- ` ${kleur.gray("session:")} ${kleur.bold(cert.sessionId)}`,
299
- ` ${kleur.gray("captured:")} ${cert.capturedAt}`,
300
- ` ${kleur.gray("verdict:")} ${verdictBadge}`,
316
+ ` ${kleur.gray("session:")} ${kleur.bold(cert.sessionId)}`,
317
+ ` ${kleur.gray("captured:")} ${cert.capturedAt}`,
318
+ ` ${kleur.gray("verdict:")} ${verdictBadge}`,
319
+ ` ${kleur.gray("coverage:")} ${kleur.bold(`${cert.coverage.verified}/${cert.coverage.total}`)} axes verified · ${confBadge}`,
301
320
  ];
302
- const axisRow = (name, v) => {
303
- const mark = v.verdict === "pass" ? kleur.green("") : v.verdict === "warn" ? kleur.yellow("!") : kleur.red("");
304
- return ` ${mark} ${kleur.bold(name.padEnd(20))} ${kleur.gray(v.reason)}`;
321
+ if (cert.insufficientData) {
322
+ ledeLines.push(` ${kleur.gray("reason:")} ${kleur.yellow(cert.insufficientData.reason)}`, ` ${kleur.gray("hint:")} ${kleur.cyan(cert.insufficientData.hint)}`);
323
+ }
324
+ const mark = (v) => v === "pass" ? kleur.green("✓")
325
+ : v === "warn" ? kleur.yellow("!")
326
+ : v === "skipped" ? kleur.gray("⊘")
327
+ : kleur.red("✗");
328
+ const axisLines = (name, a) => {
329
+ const out = [];
330
+ out.push(` ${mark(a.verdict)} ${kleur.bold(name.padEnd(22))} ${kleur.gray(a.reason)} ${kleur.gray(`[${a.confidence} conf.]`)}`);
331
+ for (const e of a.evidence) {
332
+ const m = e.ok === true ? kleur.green("✓")
333
+ : e.ok === false ? kleur.red("✗")
334
+ : kleur.gray("·");
335
+ out.push(` ${m} ${kleur.gray(e.label.padEnd(22))} ${e.value}`);
336
+ }
337
+ if (a.caveat) {
338
+ out.push(` ${kleur.gray("ⓘ " + a.caveat)}`);
339
+ }
340
+ return out;
305
341
  };
306
- const factLines = [
307
- axisRow("behavioral parity", cert.axes.behavioralParity),
308
- axisRow("API contract drift", cert.axes.apiContractDrift),
309
- axisRow("test pass rate", { verdict: cert.axes.testPassRate.verdict, reason: cert.axes.testPassRate.reason }),
310
- axisRow("perf regression", { verdict: cert.axes.perfRegression.verdict, reason: cert.axes.perfRegression.reason }),
311
- axisRow("AI narrative", cert.axes.aiNarrative),
312
- ];
313
- const forensicLines = [
314
- ` ${formatForensic("size", cert.forensicAxes.size)}`,
315
- ` ${formatForensic("files", cert.forensicAxes.files)}`,
316
- ` ${formatForensic("style", cert.forensicAxes.style)}`,
317
- ` ${formatForensic("time", cert.forensicAxes.time)}`,
318
- ];
319
- const detailLines = [];
320
- for (const [axis, info] of Object.entries(cert.axes)) {
321
- const det = info.details;
322
- if (Array.isArray(det) && det.length > 0) {
323
- detailLines.push(` ${kleur.bold(axis)}`);
324
- for (const d of det)
325
- detailLines.push(` ${kleur.gray(d)}`);
342
+ const evidenceLines = [];
343
+ evidenceLines.push(...axisLines("behavioral parity", cert.axes.behavioralParity));
344
+ evidenceLines.push(...axisLines("API contract drift", cert.axes.apiContractDrift));
345
+ evidenceLines.push(...axisLines("test pass rate", cert.axes.testPassRate));
346
+ evidenceLines.push(...axisLines("perf regression", cert.axes.perfRegression));
347
+ evidenceLines.push(...axisLines("AI narrative", cert.axes.aiNarrative));
348
+ const forensicAxisLines = (name, a) => {
349
+ const out = [];
350
+ out.push(` ${mark(a.verdict)} ${kleur.bold(name.padEnd(8))} ${kleur.gray(a.reason)}`);
351
+ for (const e of a.evidence) {
352
+ out.push(` ${kleur.gray("·")} ${kleur.gray(e.label.padEnd(8))} ${e.value}`);
326
353
  }
354
+ return out;
355
+ };
356
+ const forensicLines = [];
357
+ forensicLines.push(...forensicAxisLines("size", cert.forensicAxes.size));
358
+ forensicLines.push(...forensicAxisLines("files", cert.forensicAxes.files));
359
+ forensicLines.push(...forensicAxisLines("style", cert.forensicAxes.style));
360
+ forensicLines.push(...forensicAxisLines("time", cert.forensicAxes.time));
361
+ if (cert.forensicAxes.size.caveat) {
362
+ forensicLines.push(` ${kleur.gray("ⓘ " + cert.forensicAxes.size.caveat)}`);
327
363
  }
328
364
  const sections = [];
329
365
  // --explain narrative goes ABOVE the certificate so a release manager
@@ -334,16 +370,14 @@ function renderCertificate(cert, explainSection = null, explainHeadsUp = null) {
334
370
  sections.push({ tier: "lede", lines: [` ${explainHeadsUp}`] });
335
371
  }
336
372
  sections.push({ tier: "lede", title: "✦ Certificate", lines: ledeLines });
337
- sections.push({ tier: "key-facts", title: "◆ 5-axis verdict", lines: factLines });
373
+ sections.push({ tier: "key-facts", title: "◆ 5-axis verdict (with evidence)", lines: evidenceLines });
338
374
  sections.push({ tier: "body", title: "◆ Forensic axes (insider-threat reuse)", lines: forensicLines });
339
- if (detailLines.length > 0) {
340
- sections.push({ tier: "details", title: "◆ Per-axis details", lines: detailLines });
341
- }
342
375
  sections.push({
343
376
  tier: "sources",
344
377
  title: "→ Try next",
345
378
  lines: [
346
379
  ` ${kleur.cyan("$")} ${kleur.bold("mneme audit --report --out audit.md")} ${kleur.gray("(markdown for compliance)")}`,
380
+ ` ${kleur.cyan("$")} ${kleur.bold("mneme audit --certify --strict")} ${kleur.gray("(treat skipped axes as fail)")}`,
347
381
  ` ${kleur.cyan("$")} ${kleur.bold("mneme audit --watch")} ${kleur.gray("(CI gate — re-run automatically)")}`,
348
382
  ],
349
383
  });
@@ -351,10 +385,6 @@ function renderCertificate(cert, explainSection = null, explainHeadsUp = null) {
351
385
  process.stdout.write(iris.render({ headline, sections }));
352
386
  process.stdout.write("\n");
353
387
  }
354
- function formatForensic(name, v) {
355
- const mark = v === "pass" ? kleur.green("✓") : v === "warn" ? kleur.yellow("!") : kleur.red("✗");
356
- return `${mark} ${kleur.gray(name.padEnd(8))} ${v}`;
357
- }
358
388
  // ─── --watch ─────────────────────────────────────────────────────────
359
389
  async function runWatch(opts) {
360
390
  const meta = await git.getRepoMeta(opts.cwd);
@@ -393,7 +423,7 @@ async function runWatch(opts) {
393
423
  // ─── --report ────────────────────────────────────────────────────────
394
424
  async function runReport(opts) {
395
425
  const meta = await git.getRepoMeta(opts.cwd);
396
- const cert = await runFullCertifyPipeline(meta.rootPath);
426
+ const cert = await runFullCertifyPipeline(meta.rootPath, { strict: opts.strict });
397
427
  if (!cert) {
398
428
  ui.error("No baseline. Run `mneme audit --baseline` first.");
399
429
  return 1;
@@ -408,31 +438,104 @@ async function runReport(opts) {
408
438
  }
409
439
  return cert.exitCode;
410
440
  }
441
+ /**
442
+ * Render a forensic-grade markdown report.
443
+ *
444
+ * Every axis surfaces:
445
+ * • verdict (pass / warn / fail / skipped)
446
+ * • reason (one-liner)
447
+ * • confidence rating
448
+ * • structured evidence bullets (the FACTS — counts, hashes, paths,
449
+ * deltas — that back the verdict)
450
+ * • caveat (the "ⓘ" line declaring what this axis does NOT check)
451
+ *
452
+ * The headline carries coverage (X/5 axes verified) so the user
453
+ * immediately sees how trustworthy the verdict is. Sniper-accuracy:
454
+ * a "PASS · 2/5 verified · low confidence" report is a yellow flag,
455
+ * not a green light.
456
+ */
411
457
  export function renderMarkdownReport(cert) {
412
458
  const lines = [];
459
+ // ── Header ────────────────────────────────────────────────────────
413
460
  lines.push(`# AI Audit Trust Certificate`);
414
461
  lines.push(``);
415
462
  lines.push(`- **Session**: \`${cert.sessionId}\``);
416
463
  lines.push(`- **Captured**: ${cert.capturedAt}`);
417
- lines.push(`- **Overall verdict**: **${cert.overallVerdict.toUpperCase()}** (exit ${cert.exitCode})`);
464
+ lines.push(`- **Overall verdict**: **${cert.overallVerdict.toUpperCase()}** ` +
465
+ `· ${cert.coverage.verified}/${cert.coverage.total} axes verified` +
466
+ (cert.coverage.skipped > 0 ? ` · ${cert.coverage.skipped} skipped` : "") +
467
+ ` · ${cert.coverage.confidence} confidence (exit ${cert.exitCode})`);
468
+ if (cert.insufficientData) {
469
+ lines.push(``);
470
+ lines.push(`> **INSUFFICIENT DATA** — ${cert.insufficientData.reason}`);
471
+ lines.push(`>`);
472
+ lines.push(`> ${cert.insufficientData.hint}`);
473
+ }
418
474
  lines.push(``);
475
+ // ── 5-axis verdict (with evidence) ────────────────────────────────
419
476
  lines.push(`## 5-Axis Verdict`);
420
477
  lines.push(``);
421
- lines.push(`| Axis | Verdict | Reason |`);
422
- lines.push(`| ---- | ------- | ------ |`);
423
- lines.push(`| Behavioral parity | ${cert.axes.behavioralParity.verdict} | ${cert.axes.behavioralParity.reason} |`);
424
- lines.push(`| API contract drift | ${cert.axes.apiContractDrift.verdict} | ${cert.axes.apiContractDrift.reason} |`);
425
- lines.push(`| Test pass rate | ${cert.axes.testPassRate.verdict} | ${cert.axes.testPassRate.reason} (${cert.axes.testPassRate.before} → ${cert.axes.testPassRate.after}) |`);
426
- lines.push(`| Perf regression | ${cert.axes.perfRegression.verdict} | ${cert.axes.perfRegression.reason} (${cert.axes.perfRegression.deltaPercent}%) |`);
427
- lines.push(`| AI narrative | ${cert.axes.aiNarrative.verdict} | ${cert.axes.aiNarrative.reason} |`);
478
+ lines.push(`| Axis | Verdict | Confidence | Reason |`);
479
+ lines.push(`| ---- | ------- | ---------- | ------ |`);
480
+ const tableRow = (name, a) => `| ${name} | ${a.verdict} | ${a.confidence} | ${a.reason} |`;
481
+ lines.push(tableRow("Behavioral parity", cert.axes.behavioralParity));
482
+ lines.push(tableRow("API contract drift", cert.axes.apiContractDrift));
483
+ lines.push(tableRow("Test pass rate", cert.axes.testPassRate));
484
+ lines.push(tableRow("Perf regression", cert.axes.perfRegression));
485
+ lines.push(tableRow("AI narrative", cert.axes.aiNarrative));
486
+ lines.push(``);
487
+ // ── Per-axis evidence ─────────────────────────────────────────────
488
+ lines.push(`## Per-Axis Evidence`);
428
489
  lines.push(``);
490
+ const renderAxis = (name, a) => {
491
+ const icon = a.verdict === "pass" ? "✓"
492
+ : a.verdict === "warn" ? "!"
493
+ : a.verdict === "skipped" ? "⊘"
494
+ : "✗";
495
+ lines.push(`### ${icon} ${name} — \`${a.verdict}\` (${a.confidence} confidence)`);
496
+ lines.push(``);
497
+ lines.push(`> ${a.reason}`);
498
+ lines.push(``);
499
+ if (a.evidence.length > 0) {
500
+ for (const e of a.evidence) {
501
+ const m = e.ok === true ? "✓" : e.ok === false ? "✗" : "·";
502
+ lines.push(`- ${m} **${e.label}** — ${e.value}`);
503
+ }
504
+ lines.push(``);
505
+ }
506
+ if (a.caveat) {
507
+ lines.push(`*ⓘ ${a.caveat}*`);
508
+ lines.push(``);
509
+ }
510
+ };
511
+ renderAxis("Behavioral parity", cert.axes.behavioralParity);
512
+ renderAxis("API contract drift", cert.axes.apiContractDrift);
513
+ renderAxis("Test pass rate", cert.axes.testPassRate);
514
+ renderAxis("Perf regression", cert.axes.perfRegression);
515
+ renderAxis("AI narrative", cert.axes.aiNarrative);
516
+ // ── Forensic axes ─────────────────────────────────────────────────
429
517
  lines.push(`## Forensic Axes`);
430
518
  lines.push(``);
431
- lines.push(`- size: ${cert.forensicAxes.size}`);
432
- lines.push(`- files: ${cert.forensicAxes.files}`);
433
- lines.push(`- style: ${cert.forensicAxes.style}`);
434
- lines.push(`- time: ${cert.forensicAxes.time}`);
519
+ const renderForensic = (name, a) => {
520
+ const icon = a.verdict === "pass" ? "✓"
521
+ : a.verdict === "warn" ? "!"
522
+ : a.verdict === "skipped" ? "⊘"
523
+ : "✗";
524
+ lines.push(`- ${icon} **${name}** (\`${a.verdict}\`) — ${a.reason}`);
525
+ for (const e of a.evidence) {
526
+ lines.push(` - ${e.label}: ${e.value}`);
527
+ }
528
+ };
529
+ renderForensic("size", cert.forensicAxes.size);
530
+ renderForensic("files", cert.forensicAxes.files);
531
+ renderForensic("style", cert.forensicAxes.style);
532
+ renderForensic("time", cert.forensicAxes.time);
533
+ if (cert.forensicAxes.size.caveat) {
534
+ lines.push(``);
535
+ lines.push(`*ⓘ ${cert.forensicAxes.size.caveat}*`);
536
+ }
435
537
  lines.push(``);
538
+ // ── Per-commit narrative checks (full detail when available) ─────
436
539
  if (cert.axes.aiNarrative.checks.length > 0) {
437
540
  lines.push(`## Per-Commit Narrative Checks`);
438
541
  lines.push(``);