kibi-cli 2.31.1 → 2.32.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 (224) hide show
  1. package/dist/cli-register-proof.d.ts.map +1 -1
  2. package/dist/cli-register-proof.js +19 -7
  3. package/dist/cli-register-reporting.d.ts.map +1 -1
  4. package/dist/cli-register-reporting.js +2 -0
  5. package/dist/commands/check.d.ts +1 -0
  6. package/dist/commands/check.d.ts.map +1 -1
  7. package/dist/commands/check.js +14 -5
  8. package/dist/commands/discovery-table.d.ts.map +1 -1
  9. package/dist/commands/discovery-table.js +51 -4
  10. package/dist/commands/doctor.js +19 -4
  11. package/dist/commands/migrate.d.ts.map +1 -1
  12. package/dist/commands/migrate.js +89 -3
  13. package/dist/commands/prove.d.ts +48 -1
  14. package/dist/commands/prove.d.ts.map +1 -1
  15. package/dist/commands/prove.js +293 -8
  16. package/dist/commands/report.d.ts +1 -0
  17. package/dist/commands/report.d.ts.map +1 -1
  18. package/dist/commands/report.js +70 -5
  19. package/dist/commands/signoff.d.ts.map +1 -1
  20. package/dist/commands/signoff.js +20 -1
  21. package/dist/commands/sync/manifest.d.ts.map +1 -1
  22. package/dist/commands/sync/manifest.js +20 -4
  23. package/dist/commands/sync/persistence.d.ts.map +1 -1
  24. package/dist/commands/sync/persistence.js +16 -5
  25. package/dist/diagnostics.js +1 -1
  26. package/dist/engine.js +1 -1
  27. package/dist/extractors/markdown.d.ts +3 -1
  28. package/dist/extractors/markdown.d.ts.map +1 -1
  29. package/dist/extractors/markdown.js +17 -0
  30. package/dist/operations/bootstrap/candidate-helpers.d.ts +3 -0
  31. package/dist/operations/bootstrap/candidate-helpers.d.ts.map +1 -1
  32. package/dist/operations/bootstrap/candidate-helpers.js +4 -0
  33. package/dist/operations/migration/evidence-floor.d.ts +41 -0
  34. package/dist/operations/migration/evidence-floor.d.ts.map +1 -0
  35. package/dist/operations/migration/evidence-floor.js +230 -0
  36. package/dist/operations/migration/proof-contract-claims.d.ts +59 -0
  37. package/dist/operations/migration/proof-contract-claims.d.ts.map +1 -0
  38. package/dist/operations/migration/proof-contract-claims.js +182 -0
  39. package/dist/operations/migration/schema6.d.ts +6 -0
  40. package/dist/operations/migration/schema6.d.ts.map +1 -1
  41. package/dist/operations/migration/schema6.js +94 -0
  42. package/dist/operations/mutation/omitted-properties.d.ts +11 -0
  43. package/dist/operations/mutation/omitted-properties.d.ts.map +1 -0
  44. package/dist/operations/mutation/omitted-properties.js +76 -0
  45. package/dist/operations/mutation/serialization.d.ts.map +1 -1
  46. package/dist/operations/mutation/serialization.js +1 -0
  47. package/dist/operations/mutation/strict-fact.d.ts.map +1 -1
  48. package/dist/operations/mutation/strict-fact.js +6 -2
  49. package/dist/operations/mutation/symbol-granularity.d.ts.map +1 -1
  50. package/dist/operations/mutation/symbol-granularity.js +5 -3
  51. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  52. package/dist/operations/mutation/upsert.js +7 -1
  53. package/dist/operations/mutation/validation.d.ts +2 -0
  54. package/dist/operations/mutation/validation.d.ts.map +1 -1
  55. package/dist/operations/mutation/validation.js +4 -0
  56. package/dist/operations/proof/claim-bindings.d.ts +23 -0
  57. package/dist/operations/proof/claim-bindings.d.ts.map +1 -0
  58. package/dist/operations/proof/claim-bindings.js +211 -0
  59. package/dist/operations/proof/code-certificates.d.ts +135 -0
  60. package/dist/operations/proof/code-certificates.d.ts.map +1 -0
  61. package/dist/operations/proof/code-certificates.js +785 -0
  62. package/dist/operations/proof/code-scope.d.ts +11 -1
  63. package/dist/operations/proof/code-scope.d.ts.map +1 -1
  64. package/dist/operations/proof/code-scope.js +20 -2
  65. package/dist/operations/proof/compact-receipts.d.ts.map +1 -1
  66. package/dist/operations/proof/compact-receipts.js +4 -3
  67. package/dist/operations/proof/explain.d.ts +32 -0
  68. package/dist/operations/proof/explain.d.ts.map +1 -1
  69. package/dist/operations/proof/explain.js +91 -0
  70. package/dist/operations/proof/ingest-proof.d.ts +9 -1
  71. package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
  72. package/dist/operations/proof/ingest-proof.js +41 -2
  73. package/dist/operations/proof/receipt-compaction.d.ts +2 -0
  74. package/dist/operations/proof/receipt-compaction.d.ts.map +1 -1
  75. package/dist/operations/proof/receipt-compaction.js +6 -2
  76. package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
  77. package/dist/operations/semantic-advisor/shared.js +4 -0
  78. package/dist/operations/semantic-advisor/strict-rules.js +3 -3
  79. package/dist/operations/semantic-advisor/types.d.ts +2 -1
  80. package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
  81. package/dist/plugins/approved-source-analyzers.generated.d.ts +6 -6
  82. package/dist/plugins/approved-source-analyzers.generated.js +6 -6
  83. package/dist/proof/command-test-report.d.ts +4 -3
  84. package/dist/proof/command-test-report.d.ts.map +1 -1
  85. package/dist/proof/command-test-report.js +4 -10
  86. package/dist/proof/evaluate.d.ts.map +1 -1
  87. package/dist/proof/evaluate.js +18 -2
  88. package/dist/proof/inspect.d.ts.map +1 -1
  89. package/dist/proof/inspect.js +58 -0
  90. package/dist/proof/producers/playwright-reporter.d.ts +3 -0
  91. package/dist/proof/producers/playwright-reporter.d.ts.map +1 -1
  92. package/dist/proof/producers/playwright-reporter.js +25 -2
  93. package/dist/proof/step-runner.d.ts +36 -0
  94. package/dist/proof/step-runner.d.ts.map +1 -0
  95. package/dist/proof/step-runner.js +234 -0
  96. package/dist/public/evidence-levels.d.ts +12 -0
  97. package/dist/public/evidence-levels.d.ts.map +1 -0
  98. package/dist/public/evidence-levels.js +19 -0
  99. package/dist/public/impact/coverage-depth-quality.d.ts +7 -0
  100. package/dist/public/impact/coverage-depth-quality.d.ts.map +1 -1
  101. package/dist/public/impact/coverage-depth-quality.js +59 -0
  102. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  103. package/dist/public/impact/full-kb-quality.js +3 -1
  104. package/dist/public/operations/catalog.d.ts +167 -6
  105. package/dist/public/operations/catalog.d.ts.map +1 -1
  106. package/dist/public/operations/check-executor.d.ts +2 -0
  107. package/dist/public/operations/check-executor.d.ts.map +1 -1
  108. package/dist/public/operations/check-executor.js +6 -0
  109. package/dist/public/operations/evidence-floor.d.ts +48 -0
  110. package/dist/public/operations/evidence-floor.d.ts.map +1 -0
  111. package/dist/public/operations/evidence-floor.js +143 -0
  112. package/dist/public/operations/migration-plan.d.ts +19 -5
  113. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  114. package/dist/public/operations/migration-plan.js +22 -5
  115. package/dist/public/operations/node-ports.d.ts.map +1 -1
  116. package/dist/public/operations/node-ports.js +4 -1
  117. package/dist/public/operations/proof-contract-claims.d.ts +26 -0
  118. package/dist/public/operations/proof-contract-claims.d.ts.map +1 -0
  119. package/dist/public/operations/proof-contract-claims.js +71 -0
  120. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  121. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  122. package/dist/public/operations/specs/check-rules.generated.js +3 -1
  123. package/dist/public/operations/specs/check.d.ts +1 -1
  124. package/dist/public/operations/specs/mutation-schemas.d.ts +157 -5
  125. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  126. package/dist/public/operations/specs/mutation-schemas.js +15 -2
  127. package/dist/public/operations/specs/proof.d.ts +9 -0
  128. package/dist/public/operations/specs/proof.d.ts.map +1 -1
  129. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  130. package/dist/public/operations/specs/reporting.js +19 -10
  131. package/dist/public/operations/specs/upsert.d.ts +157 -5
  132. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  133. package/dist/public/proof-certificates.d.ts +89 -0
  134. package/dist/public/proof-certificates.d.ts.map +1 -0
  135. package/dist/public/proof-certificates.js +207 -0
  136. package/dist/public/proof-protocol.d.ts +126 -3
  137. package/dist/public/proof-protocol.d.ts.map +1 -1
  138. package/dist/public/proof-protocol.js +239 -3
  139. package/dist/public/proof-receipt.d.ts +89 -6
  140. package/dist/public/proof-receipt.d.ts.map +1 -1
  141. package/dist/public/proof-receipt.js +83 -7
  142. package/dist/public/schemas/entity.d.ts.map +1 -1
  143. package/dist/public/schemas/entity.js +18 -1
  144. package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
  145. package/dist/public/skills/kibi-commit-review/SKILL.md +1 -1
  146. package/dist/public/skills/kibi-commit-review/resources/staged-gate.md +6 -1
  147. package/dist/public/skills/kibi-decisions/SKILL.md +10 -2
  148. package/dist/public/skills/kibi-decisions/resources/decision-records.md +6 -1
  149. package/dist/public/skills/kibi-freshness/SKILL.md +5 -1
  150. package/dist/public/skills/kibi-traceability/SKILL.md +11 -1
  151. package/dist/public/skills/kibi-usage/SKILL.md +6 -4
  152. package/dist/public/skills/kibi-usage/resources/proof.md +124 -19
  153. package/dist/public/skills/kibi-usage/resources/workflows.md +4 -4
  154. package/dist/report/html-report.css +38 -1
  155. package/dist/report/html-report.d.ts +8 -0
  156. package/dist/report/html-report.d.ts.map +1 -1
  157. package/dist/report/html-report.js +141 -6
  158. package/dist/report/report-view.d.ts +1 -1
  159. package/dist/report/report-view.d.ts.map +1 -1
  160. package/dist/report/report-view.js +7 -0
  161. package/dist/schemas/entity.schema.json +212 -4
  162. package/dist/search-answer.d.ts.map +1 -1
  163. package/dist/search-answer.js +1 -0
  164. package/dist/signoff/html.d.ts.map +1 -1
  165. package/dist/signoff/html.js +3 -0
  166. package/dist/signoff/markdown.d.ts.map +1 -1
  167. package/dist/signoff/markdown.js +3 -0
  168. package/dist/signoff/model.d.ts +20 -0
  169. package/dist/signoff/model.d.ts.map +1 -1
  170. package/dist/signoff/model.js +20 -1
  171. package/dist/traceability/impact-knowledge.d.ts.map +1 -1
  172. package/dist/traceability/impact-knowledge.js +1 -0
  173. package/dist/traceability/temp-kb.d.ts.map +1 -1
  174. package/dist/traceability/temp-kb.js +1 -0
  175. package/dist/types/entities.d.ts +4 -1
  176. package/dist/types/entities.d.ts.map +1 -1
  177. package/dist/utils/merge-driver-setup.d.ts +8 -1
  178. package/dist/utils/merge-driver-setup.d.ts.map +1 -1
  179. package/dist/utils/merge-driver-setup.js +23 -2
  180. package/dist/utils/rule-registry.generated.d.ts +10 -0
  181. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  182. package/dist/utils/rule-registry.generated.js +12 -0
  183. package/dist/utils/schema-version.d.ts +1 -1
  184. package/dist/utils/schema-version.d.ts.map +1 -1
  185. package/dist/utils/schema-version.js +5 -1
  186. package/package.json +6 -2
  187. package/schema/entities.pl +6 -0
  188. package/schema/validation.pl +4 -1
  189. package/src/public/evidence-levels.ts +23 -0
  190. package/src/public/impact/coverage-depth-quality.ts +73 -0
  191. package/src/public/impact/full-kb-quality.ts +6 -1
  192. package/src/public/operations/check-executor.ts +13 -0
  193. package/src/public/operations/evidence-floor.ts +203 -0
  194. package/src/public/operations/migration-plan.ts +39 -6
  195. package/src/public/operations/node-ports.ts +3 -1
  196. package/src/public/operations/proof-contract-claims.ts +103 -0
  197. package/src/public/operations/specs/check-rules.generated.ts +3 -1
  198. package/src/public/operations/specs/mutation-schemas.ts +17 -2
  199. package/src/public/operations/specs/reporting.ts +20 -10
  200. package/src/public/proof-certificates.ts +271 -0
  201. package/src/public/proof-protocol-types.d.ts +17 -1
  202. package/src/public/proof-protocol.ts +318 -3
  203. package/src/public/proof-receipt.ts +127 -7
  204. package/src/public/schemas/entity.ts +19 -1
  205. package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
  206. package/src/public/skills/kibi-commit-review/SKILL.md +1 -1
  207. package/src/public/skills/kibi-commit-review/resources/staged-gate.md +6 -1
  208. package/src/public/skills/kibi-decisions/SKILL.md +10 -2
  209. package/src/public/skills/kibi-decisions/resources/decision-records.md +6 -1
  210. package/src/public/skills/kibi-freshness/SKILL.md +5 -1
  211. package/src/public/skills/kibi-traceability/SKILL.md +11 -1
  212. package/src/public/skills/kibi-usage/SKILL.md +6 -4
  213. package/src/public/skills/kibi-usage/resources/proof.md +124 -19
  214. package/src/public/skills/kibi-usage/resources/workflows.md +4 -4
  215. package/src/schemas/entity.schema.json +212 -4
  216. package/dist/commands/proof-impact.d.ts +0 -7
  217. package/dist/commands/proof-impact.d.ts.map +0 -1
  218. package/dist/commands/proof-impact.js +0 -39
  219. package/dist/operations/proof/baseline-diff.d.ts +0 -41
  220. package/dist/operations/proof/baseline-diff.d.ts.map +0 -1
  221. package/dist/operations/proof/baseline-diff.js +0 -153
  222. package/dist/operations/proof/impact.d.ts +0 -51
  223. package/dist/operations/proof/impact.d.ts.map +0 -1
  224. package/dist/operations/proof/impact.js +0 -138
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { executeIngestProof } from "../operations/proof/ingest-proof.js";
5
5
  import { PROOF_TEST_REPORT_ENV, PROOF_TEST_REPORT_VERSION, attributeCommandRun, proofTestReportPath, } from "../proof/command-test-report.js";
@@ -7,9 +7,13 @@ import { PROOF_RUNS_DIR, loadProofIntegrations, resolveIntegration, } from "../p
7
7
  import { convertJUnitXml } from "../proof/producers/junit-adapter.js";
8
8
  import { convertTap } from "../proof/producers/tap-adapter.js";
9
9
  import { loadEntities } from "../public/operations/discovery-entities.js";
10
+ import { runOperationJsonQuery, toPrologAtom, } from "../public/operations/prolog-json.js";
10
11
  import { ingestProofSpec } from "../public/operations/specs/proof.js";
12
+ import { currentProofBindingMode, perContractTestBindings, } from "../public/operations/specs/reporting.js";
11
13
  import { readWorkspaceSnapshot } from "../public/operations/workspace-snapshot.js";
14
+ import { PROOF_CERTIFICATES_ENV } from "../public/proof-certificates.js";
12
15
  import { PROOF_RUN_VERSION, proofBindingsErrors, proofRunArtifactErrors, } from "../public/proof-protocol.js";
16
+ import { PROOF_RECEIPT_MAX_AGE_SECONDS } from "../public/proof-receipt.js";
13
17
  import { createCliRuntime } from "../runtime/cli-runtime.js";
14
18
  /**
15
19
  * Describe which paths moved when a proof snapshot refusal fires, so
@@ -70,19 +74,56 @@ function parseContract(test) {
70
74
  return null;
71
75
  return contract;
72
76
  }
73
- async function runChild(commandArgv, cwd, env) {
77
+ /** Run a proof integration's command and resolve with its exit code. */
78
+ // implements REQ-kibi-verification-evidence-contract
79
+ export async function runChild(commandArgv, cwd, env) {
74
80
  const [command, ...args] = commandArgv;
75
81
  if (!command)
76
82
  throw new Error("prove: integration command argv must be non-empty");
77
83
  return await new Promise((resolve, reject) => {
84
+ // The producer runs in its own process group, so stopping kibi prove
85
+ // stops the whole suite it started (a test runner and its workers), not
86
+ // only kibi prove itself.
87
+ const ownGroup = process.platform !== "win32";
78
88
  const child = spawn(command, args, {
79
89
  cwd,
80
90
  env,
81
91
  shell: false,
82
92
  stdio: "inherit",
93
+ detached: ownGroup,
94
+ });
95
+ const forward = (signal) => {
96
+ const pid = child.pid;
97
+ try {
98
+ if (ownGroup && pid !== undefined)
99
+ process.kill(-pid, signal);
100
+ else
101
+ child.kill(signal);
102
+ }
103
+ catch {
104
+ // The producer already exited.
105
+ }
106
+ process.exit(signal === "SIGINT" ? 130 : 143);
107
+ };
108
+ const onInt = () => forward("SIGINT");
109
+ const onTerm = () => forward("SIGTERM");
110
+ const onHup = () => forward("SIGHUP");
111
+ process.once("SIGINT", onInt);
112
+ process.once("SIGTERM", onTerm);
113
+ process.once("SIGHUP", onHup);
114
+ const settle = () => {
115
+ process.removeListener("SIGINT", onInt);
116
+ process.removeListener("SIGTERM", onTerm);
117
+ process.removeListener("SIGHUP", onHup);
118
+ };
119
+ child.once("error", (error) => {
120
+ settle();
121
+ reject(error);
122
+ });
123
+ child.once("close", (code) => {
124
+ settle();
125
+ resolve(code ?? 1);
83
126
  });
84
- child.once("error", reject);
85
- child.once("close", (code) => resolve(code ?? 1));
86
127
  });
87
128
  }
88
129
  // implements REQ-kibi-verification-evidence-contract
@@ -98,6 +139,84 @@ export function commandEnvironment(integration, commandArgv, snapshot, workspace
98
139
  KIBI_PROOF_TEST_IDS: JSON.stringify(testIds),
99
140
  };
100
141
  }
142
+ /** Where `kibi prove` records which integrations it has ingested. */
143
+ export const PROVE_PROGRESS_FILE = "prove-progress.json";
144
+ /** Read the progress file; anything unreadable counts as no progress. */
145
+ export async function readProveProgress(runsDir) {
146
+ try {
147
+ const parsed = JSON.parse(await readFile(path.join(runsDir, PROVE_PROGRESS_FILE), "utf8"));
148
+ if (parsed.version !== "kibi.prove-progress.v1" ||
149
+ typeof parsed.snapshot !== "string" ||
150
+ parsed.ingested === null ||
151
+ typeof parsed.ingested !== "object")
152
+ return null;
153
+ return parsed;
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ }
159
+ /** Start a fresh progress record for a run that is not resuming. */
160
+ async function recordProgressReset(runsDir, progress) {
161
+ await writeFile(path.join(runsDir, PROVE_PROGRESS_FILE), `${JSON.stringify(progress, null, 2)}\n`, "utf8");
162
+ }
163
+ /**
164
+ * Integrations a resumed run can skip: ingested at the same snapshot, for
165
+ * every test this run selects for them.
166
+ */
167
+ export function resumableIntegrations(progress, snapshot, groups) {
168
+ const skip = new Set();
169
+ if (progress === null || progress.snapshot !== snapshot)
170
+ return skip;
171
+ for (const [integrationId, tests] of groups) {
172
+ const done = new Set(progress.ingested[integrationId] ?? []);
173
+ if (done.size > 0 && tests.every((test) => done.has(String(test.id))))
174
+ skip.add(integrationId);
175
+ }
176
+ return skip;
177
+ }
178
+ /**
179
+ * Narrow a Playwright integration's command to the spec files the selected
180
+ * tests are bound to, so `kibi prove --test X` runs X's specs instead of the
181
+ * whole suite. Only a direct `playwright test` invocation is narrowed, and
182
+ * only when every selected test names its `proof_bindings[].source_file`;
183
+ * otherwise the command runs unchanged and the reason says why.
184
+ */
185
+ // implements REQ-kibi-verification-evidence-contract
186
+ export function narrowedPlaywrightCommand(command, tests) {
187
+ const argv = [...command];
188
+ const runner = argv.findIndex((arg, index) => /(^|[\\/])playwright(\.c?js)?$/.test(arg) && argv[index + 1] === "test");
189
+ if (runner < 0)
190
+ return {
191
+ argv,
192
+ narrowed: false,
193
+ reason: "the command is not a direct `playwright test` invocation; narrow it from KIBI_PROOF_SOURCE_FILES",
194
+ };
195
+ const files = new Set();
196
+ for (const test of tests) {
197
+ const own = proofSourceFiles(test);
198
+ if (own.length === 0)
199
+ return {
200
+ argv,
201
+ narrowed: false,
202
+ reason: `test ${String(test.id)} has no proof_bindings[].source_file`,
203
+ };
204
+ for (const file of own)
205
+ files.add(file);
206
+ }
207
+ return { argv: [...argv, ...[...files].sort()], narrowed: true };
208
+ }
209
+ function proofSourceFiles(test) {
210
+ const raw = test.proof_bindings;
211
+ if (!Array.isArray(raw))
212
+ return [];
213
+ return raw.flatMap((entry) => {
214
+ const file = entry !== null && typeof entry === "object"
215
+ ? entry.source_file
216
+ : undefined;
217
+ return typeof file === "string" && file !== "" ? [file] : [];
218
+ });
219
+ }
101
220
  function collectBindings(tests) {
102
221
  const bindings = [];
103
222
  for (const test of tests) {
@@ -151,7 +270,30 @@ function dedupeObligations(tests) {
151
270
  function markAggregateFailure(results) {
152
271
  return results.map((result) => ({ ...result, outcome: "failed" }));
153
272
  }
273
+ /**
274
+ * Receipt state per proof-bearing test under the binding mode reports use.
275
+ * `passed` means the test already has a current passing receipt; anything
276
+ * else (missing, stale, contract_mismatch, invalid, failed) needs a run.
277
+ */
278
+ // implements REQ-kibi-verification-evidence-contract
279
+ export async function readTestReceiptStates(context) {
280
+ const snapshotEvidence = await readWorkspaceSnapshot(context);
281
+ const snapshot = snapshotEvidence.available
282
+ ? snapshotEvidence.snapshot.hash
283
+ : "unknown";
284
+ const bindingsDict = await perContractTestBindings(context);
285
+ const bindingMode = bindingsDict !== null ? "per_contract" : currentProofBindingMode();
286
+ const payload = await runOperationJsonQuery(context.prolog, "discovery.pl", `discovery:test_receipt_states_json(${bindingMode}, ${bindingsDict ?? "_{}"}, ${toPrologAtom(snapshot)}, ${toPrologAtom(context.clock().toISOString())}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`, "prove --stale");
287
+ return new Map(Object.entries(payload.states ?? {}));
288
+ }
154
289
  async function selectTests(context, options) {
290
+ const selected = await selectTestsBySelector(context, options);
291
+ if (!options.stale || selected.length === 0)
292
+ return selected;
293
+ const states = await readTestReceiptStates(context);
294
+ return selected.filter((entity) => states.get(String(entity.id)) !== "passed");
295
+ }
296
+ async function selectTestsBySelector(context, options) {
155
297
  // Contract plus bindings only. Receipt histories are loaded again at ingest
156
298
  // and blow the Prolog output buffer when selected up front. JUnit and TAP
157
299
  // conversion needs the bindings before that reload.
@@ -227,6 +369,22 @@ async function selectTests(context, options) {
227
369
  }
228
370
  return filtered;
229
371
  }
372
+ // implements REQ-kibi-claim-bound-proofs
373
+ // One line per claim a certificate test names in proves: derived (with its
374
+ // scope), attested, or rejected with the reason.
375
+ function claimBindingLines(result) {
376
+ return result.results.flatMap((row) => (row.claimBindings ?? []).map((binding) => `${row.testId} ${binding.claim_key}${binding.requirement_id ? ` (${binding.requirement_id})` : ""}: ${binding.binding}${binding.binding === "derived" && binding.scope ? `, ${binding.scope} scope` : ""}${binding.reason ? `: ${binding.reason}` : ""}`));
377
+ }
378
+ // implements REQ-kibi-code-proof-kinds
379
+ // One line per certificate test whose certificate did not re-check: the test
380
+ // passed, but its receipt supports tested, not proved.
381
+ function certificateRejectionLines(result) {
382
+ return result.results.flatMap((row) => row.certificateRejection === undefined
383
+ ? []
384
+ : [
385
+ `${row.testId} ${row.kind ?? "certificate"}: certificate rejected, ${row.certificateRejection}`,
386
+ ]);
387
+ }
230
388
  function summarize(result) {
231
389
  const parts = [`passed ${result.passed}`, `failed ${result.failed}`];
232
390
  if (result.unchanged > 0)
@@ -249,7 +407,7 @@ export async function proveCommand(options, deps = {}) {
249
407
  throw new Error(`prove: ${integrations.error}`);
250
408
  const selected = await selectTests(context, options);
251
409
  if (selected.length === 0) {
252
- process.stdout.write(`${JSON.stringify({ proved: 0, failed: 0, message: "no proof-bearing tests selected" })}\n`);
410
+ process.stdout.write(`${JSON.stringify({ passed: 0, certified: 0, failed: 0, message: options.stale ? "every selected test already has a current passing receipt" : "no proof-bearing tests selected" })}\n`);
253
411
  completed = true;
254
412
  return { exitCode: 0 };
255
413
  }
@@ -273,16 +431,51 @@ export async function proveCommand(options, deps = {}) {
273
431
  }
274
432
  const runsDir = path.join(context.workspaceRoot, PROOF_RUNS_DIR);
275
433
  await mkdir(runsDir, { recursive: true });
434
+ // Each integration's receipts are written as soon as it finishes; the
435
+ // progress file lets an interrupted run continue with --resume.
436
+ const previous = options.resume ? await readProveProgress(runsDir) : null;
437
+ const skipped = options.resume
438
+ ? resumableIntegrations(previous, snapshot, groups)
439
+ : new Set();
440
+ if (options.resume && previous !== null && previous.snapshot !== snapshot)
441
+ process.stderr.write("prove: the workspace changed since the interrupted run; running every selected integration\n");
442
+ const progress = {
443
+ version: "kibi.prove-progress.v1",
444
+ snapshot,
445
+ ingested: previous !== null && previous.snapshot === snapshot
446
+ ? { ...previous.ingested }
447
+ : {},
448
+ };
449
+ const recordProgress = async (integrationId, testIds) => {
450
+ const done = new Set(progress.ingested[integrationId] ?? []);
451
+ for (const id of testIds)
452
+ done.add(id);
453
+ progress.ingested[integrationId] = [...done].sort();
454
+ await writeFile(path.join(runsDir, PROVE_PROGRESS_FILE), `${JSON.stringify(progress, null, 2)}\n`, "utf8");
455
+ };
456
+ if (!options.resume)
457
+ await recordProgressReset(runsDir, progress);
458
+ if (skipped.size > 0)
459
+ process.stderr.write(`prove: resuming; skipping integrations already ingested at this snapshot: ${[...skipped].join(", ")}\n`);
460
+ const narrowSelection = Boolean(options.testId || options.requirement || options.stale);
461
+ const pending = [...groups].filter(([id]) => !skipped.has(id));
276
462
  const ingestResults = [];
277
463
  const runAttribution = new Map();
278
464
  const failures = [];
279
- for (const [integrationId, tests] of groups) {
465
+ for (const [index, [integrationId, tests]] of pending.entries()) {
466
+ process.stderr.write(`prove: running integration '${integrationId}' (${index + 1}/${pending.length}, ${tests.length} test${tests.length === 1 ? "" : "s"})\n`);
280
467
  const integration = resolveIntegration(integrations.integrations, integrationId);
281
468
  if (!integration) {
282
469
  failures.push(`integration '${integrationId}' (required by ${tests.map((t) => t.id).join(", ")}) is not configured in .kb/proof/integrations.json; plan it with kibi proof inspect --update ${integrationId} --json and apply the plan with kb_apply_plan`);
283
470
  continue;
284
471
  }
285
- const commandArgv = [...integration.command];
472
+ let commandArgv = [...integration.command];
473
+ if (narrowSelection && integration.producer === "playwright") {
474
+ const narrowed = narrowedPlaywrightCommand(commandArgv, tests);
475
+ commandArgv = narrowed.argv;
476
+ if (!narrowed.narrowed)
477
+ process.stderr.write(`prove: running the whole '${integrationId}' integration: ${narrowed.reason}\n`);
478
+ }
286
479
  const artifactPath = path.resolve(context.workspaceRoot, integration.artifact ??
287
480
  path.join(PROOF_RUNS_DIR, `${integrationId}.json`));
288
481
  await mkdir(path.dirname(artifactPath), { recursive: true });
@@ -298,6 +491,13 @@ export async function proveCommand(options, deps = {}) {
298
491
  const env = commandEnvironment(integration, commandArgv, snapshot, context.workspaceRoot, selectedTestIds, artifactPath);
299
492
  if (testReportPath !== undefined)
300
493
  env[PROOF_TEST_REPORT_ENV] = testReportPath;
494
+ env.KIBI_PROOF_SOURCE_FILES = JSON.stringify([...new Set(tests.flatMap(proofSourceFiles))].sort());
495
+ // Certificate-kind tests leave one certificate per test here; any left
496
+ // by an earlier run must never be read as this run's proof.
497
+ const certificateDir = path.join(runsDir, "certificates", integrationId);
498
+ await rm(certificateDir, { recursive: true, force: true });
499
+ await mkdir(certificateDir, { recursive: true });
500
+ env[PROOF_CERTIFICATES_ENV] = certificateDir;
301
501
  const startedAt = new Date().toISOString();
302
502
  let exitCode;
303
503
  try {
@@ -344,6 +544,13 @@ export async function proveCommand(options, deps = {}) {
344
544
  failures.push(`integration '${integrationId}': ${error instanceof Error ? error.message : String(error)}`);
345
545
  continue;
346
546
  }
547
+ try {
548
+ evaluated = attachCertificates(evaluated, await readCertificateDir(certificateDir));
549
+ }
550
+ catch (error) {
551
+ failures.push(`integration '${integrationId}': ${error instanceof Error ? error.message : String(error)}`);
552
+ continue;
553
+ }
347
554
  const artifactErrors = [
348
555
  ...(evaluated.wholeRun === undefined ? [] : [evaluated.wholeRun]),
349
556
  ...evaluated.artifacts.map((entry) => entry.artifact),
@@ -379,6 +586,13 @@ export async function proveCommand(options, deps = {}) {
379
586
  if (merged !== undefined) {
380
587
  ingestResults.push(merged);
381
588
  runAttribution.set(merged, evaluated);
589
+ await recordProgress(integrationId, selectedTestIds);
590
+ process.stderr.write(`prove: ingested integration '${integrationId}' (${summarize(merged)})\n`);
591
+ for (const line of [
592
+ ...certificateRejectionLines(merged),
593
+ ...claimBindingLines(merged),
594
+ ])
595
+ process.stderr.write(`prove: ${line}\n`);
382
596
  }
383
597
  const afterIngest = await readWorkspaceSnapshot(context);
384
598
  if (!afterIngest.available || afterIngest.snapshot.hash !== snapshot) {
@@ -388,8 +602,14 @@ export async function proveCommand(options, deps = {}) {
388
602
  const passed = ingestResults.reduce((sum, result) => sum + result.passed, 0);
389
603
  const failed = ingestResults.reduce((sum, result) => sum + result.failed, 0);
390
604
  const unchanged = ingestResults.reduce((sum, result) => sum + result.unchanged, 0);
605
+ // implements REQ-kibi-code-proof-kinds
606
+ // passed counts passing receipts; certified counts those whose certificate
607
+ // re-checked, the only receipts that can support proved.
608
+ const certified = ingestResults.reduce((sum, result) => sum +
609
+ result.results.filter((row) => row.outcome === "passed" && row.evidenceLevel === "proved").length, 0);
391
610
  const summary = {
392
- proved: passed,
611
+ passed,
612
+ certified,
393
613
  failed,
394
614
  unchanged,
395
615
  runs: ingestResults.map((result) => {
@@ -413,6 +633,8 @@ export async function proveCommand(options, deps = {}) {
413
633
  }),
414
634
  ...(failures.length > 0 ? { failures } : {}),
415
635
  };
636
+ if (failures.length > 0 && Object.keys(progress.ingested).length > 0)
637
+ process.stderr.write("prove: integrations that failed to produce receipts run again with the same selector plus --resume\n");
416
638
  process.stdout.write(`${JSON.stringify(summary)}\n`);
417
639
  completed = true;
418
640
  await runtime.afterSuccess(ingestProofSpec, context);
@@ -424,6 +646,69 @@ export async function proveCommand(options, deps = {}) {
424
646
  : { status: "error", error: 1 });
425
647
  }
426
648
  }
649
+ /** Read every certificate a run left in its certificate directory. */
650
+ // implements REQ-kibi-code-proof-kinds
651
+ export async function readCertificateDir(directory) {
652
+ let names;
653
+ try {
654
+ names = (await readdir(directory)).filter((name) => name.endsWith(".json"));
655
+ }
656
+ catch {
657
+ return [];
658
+ }
659
+ const certificates = [];
660
+ for (const name of names.sort()) {
661
+ let parsed;
662
+ try {
663
+ parsed = JSON.parse(await readFile(path.join(directory, name), "utf8"));
664
+ }
665
+ catch {
666
+ throw new Error(`certificate ${name} is not valid JSON`);
667
+ }
668
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
669
+ throw new Error(`certificate ${name} is not a JSON object`);
670
+ certificates.push(parsed);
671
+ }
672
+ return certificates;
673
+ }
674
+ /**
675
+ * Give each artifact the certificates of its own tests, so a partition is
676
+ * never evaluated against another test's certificate. A test may leave at
677
+ * most one certificate per run.
678
+ */
679
+ // implements REQ-kibi-code-proof-kinds
680
+ export function attachCertificates(evaluated, certificates) {
681
+ if (certificates.length === 0)
682
+ return evaluated;
683
+ const seen = new Set();
684
+ for (const certificate of certificates) {
685
+ const id = String(certificate.test_id ?? "");
686
+ if (seen.has(id))
687
+ throw new Error(`the run left more than one certificate for ${id}`);
688
+ seen.add(id);
689
+ }
690
+ const withOwn = (artifact, testIds) => {
691
+ const own = certificates.filter((certificate) => testIds.includes(String(certificate.test_id ?? "")));
692
+ return own.length === 0
693
+ ? artifact
694
+ : {
695
+ ...artifact,
696
+ certificates: [...(artifact.certificates ?? []), ...own],
697
+ };
698
+ };
699
+ return {
700
+ ...evaluated,
701
+ artifacts: evaluated.artifacts.map((entry) => ({
702
+ ...entry,
703
+ artifact: withOwn(entry.artifact, entry.testIds),
704
+ })),
705
+ ...(evaluated.wholeRun === undefined
706
+ ? {}
707
+ : {
708
+ wholeRun: withOwn(evaluated.wholeRun, evaluated.artifacts.flatMap((entry) => entry.testIds)),
709
+ }),
710
+ };
711
+ }
427
712
  /** Read a command integration's optional per-test report and attribute it. */
428
713
  // implements REQ-kibi-fresh-verification-receipts-v2
429
714
  export async function readCommandAttribution(reportPath, selectedTestIds, exitCode) {
@@ -5,6 +5,7 @@ export type ReportOptions = Readonly<{
5
5
  open?: boolean;
6
6
  tag?: string;
7
7
  limit?: string;
8
+ certificates?: string;
8
9
  }>;
9
10
  type ReportCoverage = Readonly<{
10
11
  requirements: HtmlReportCoverage;
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,yBAAyB,CAAC;AAGjC,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,KAAK,cAAc,GAAG,QAAQ,CAAC;IAC7B,YAAY,EAAE,kBAAkB,CAAC;IACjC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,YAAY,CAAC,EAAE,CACb,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B,CAAC,CAAC;AA+HH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChE;AAeD,wBAAsB,aAAa,CACjC,OAAO,EAAE,aAAa,EACtB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAyEjB"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,kBAAkB,EAGxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,yBAAyB,CAAC;AAGjC,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC,CAAC;AAEH,KAAK,cAAc,GAAG,QAAQ,CAAC;IAC7B,YAAY,EAAE,kBAAkB,CAAC;IACjC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,YAAY,CAAC,EAAE,CACb,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B,CAAC,CAAC;AA+HH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChE;AA8DD,wBAAsB,aAAa,CACjC,OAAO,EAAE,aAAa,EACtB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAgHjB"}
@@ -1,6 +1,6 @@
1
1
  import { execFileSync, spawn } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
- import { mkdir, rename, rm, writeFile } from "node:fs/promises";
3
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
6
  import { coverageSpec } from "../public/operations/specs/reporting.js";
@@ -146,6 +146,41 @@ function assertCompleteRequirementRows(coverage, limit) {
146
146
  throw new Error(`Report requires complete requirement rows, but ${coverage.rows.length} of ${total} were returned. Increase --limit above ${limit}.`);
147
147
  }
148
148
  }
149
+ const KB_CERTIFICATES_VERSION = "kibi.certificates.v1";
150
+ /**
151
+ * Read a saved kibi check --write-certificates bundle, so the report can
152
+ * publish it next to the page and say how many certificates it holds.
153
+ */
154
+ // implements REQ-kibi-kb-certificates
155
+ async function readKbCertificates(bundlePath) {
156
+ const raw = await readFile(bundlePath, "utf8");
157
+ let parsed;
158
+ try {
159
+ parsed = JSON.parse(raw);
160
+ }
161
+ catch {
162
+ throw new Error(`report: ${bundlePath} is not JSON`);
163
+ }
164
+ const bundle = parsed !== null && typeof parsed === "object"
165
+ ? parsed
166
+ : {};
167
+ if (bundle.version !== KB_CERTIFICATES_VERSION ||
168
+ !Array.isArray(bundle.certificates))
169
+ throw new Error(`report: ${bundlePath} is not a ${KB_CERTIFICATES_VERSION} bundle (write one with kibi check --write-certificates)`);
170
+ return { raw, certificates: bundle.certificates };
171
+ }
172
+ function certificateKinds(certificates) {
173
+ const kinds = {};
174
+ for (const certificate of certificates) {
175
+ const kind = certificate !== null &&
176
+ typeof certificate === "object" &&
177
+ typeof certificate.kind === "string"
178
+ ? certificate.kind
179
+ : "unknown";
180
+ kinds[kind] = (kinds[kind] ?? 0) + 1;
181
+ }
182
+ return kinds;
183
+ }
149
184
  // implements REQ-kibi-html-health-report
150
185
  export async function reportCommand(options, deps = {}) {
151
186
  const limit = parseLimit(options.limit);
@@ -154,16 +189,31 @@ export async function reportCommand(options, deps = {}) {
154
189
  assertCompleteRequirementRows(coverage.requirements, limit);
155
190
  const now = (deps.now ?? (() => new Date()))();
156
191
  const workspace = (deps.cwd ?? (() => process.cwd()))();
192
+ const outputPath = reportTarget(workspace, options.output);
193
+ const requestedOutput = options.output?.trim() || "kibi-report";
194
+ const outputIsHtmlFile = [".html", ".htm"].includes(path.extname(requestedOutput).toLowerCase());
195
+ const kbCertificatesSource = options.certificates?.trim() === undefined ||
196
+ options.certificates.trim() === ""
197
+ ? undefined
198
+ : await readKbCertificates(path.resolve(workspace, options.certificates.trim()));
199
+ const kbCertificatesPath = outputIsHtmlFile
200
+ ? path.join(path.dirname(outputPath), `${path.basename(outputPath, path.extname(outputPath))}.kb-certificates.json`)
201
+ : path.join(path.dirname(outputPath), "kb-certificates.json");
202
+ const kbCertificates = kbCertificatesSource === undefined
203
+ ? undefined
204
+ : {
205
+ count: kbCertificatesSource.certificates.length,
206
+ kinds: certificateKinds(kbCertificatesSource.certificates),
207
+ href: path.basename(kbCertificatesPath),
208
+ };
157
209
  const html = renderHtmlReport({
158
210
  requirements: coverage.requirements,
159
211
  symbols: coverage.symbols,
160
212
  branch: coverage.branch,
161
213
  generatedAt: now,
162
214
  repository: deps.repository ?? loadReportRepository(workspace, coverage.branch),
215
+ ...(kbCertificates === undefined ? {} : { kbCertificates }),
163
216
  });
164
- const outputPath = reportTarget(workspace, options.output);
165
- const requestedOutput = options.output?.trim() || "kibi-report";
166
- const outputIsHtmlFile = [".html", ".htm"].includes(path.extname(requestedOutput).toLowerCase());
167
217
  const badgePath = outputIsHtmlFile
168
218
  ? path.join(path.dirname(outputPath), `${path.basename(outputPath, path.extname(outputPath))}.badge.svg`)
169
219
  : path.join(path.dirname(outputPath), "badge.svg");
@@ -177,12 +227,23 @@ export async function reportCommand(options, deps = {}) {
177
227
  : Math.round((tested / currentRequirements) * 100);
178
228
  const hasContradiction = coverage.requirements.rows.some((row) => Array.isArray(row.proofGaps) &&
179
229
  row.proofGaps.includes("blocking_contradiction"));
230
+ // implements REQ-kibi-evidence-floors
231
+ // A requirement below its evidence floor fails kibi check, like a
232
+ // contradiction, so the badge turns red whatever the tested share.
233
+ const belowFloor = coverage.requirements.rows.some((row) => {
234
+ const floor = row.evidenceFloor;
235
+ return (row.proofStatus !== "not_applicable" &&
236
+ floor !== null &&
237
+ typeof floor === "object" &&
238
+ floor.status === "below" &&
239
+ floor.required !== "unverified");
240
+ });
180
241
  const snapshotStale = coverage.requirements.meta?.dirty === true ||
181
242
  coverage.requirements.meta?.proofSnapshotDirty === true;
182
243
  const badgeMessage = currentRequirements === 0 ? "no requirements" : `${proofPercent}% tested`;
183
244
  const badgeColor = currentRequirements === 0
184
245
  ? KIBI_BRAND.rail
185
- : hasContradiction
246
+ : hasContradiction || belowFloor
186
247
  ? KIBI_BRAND.danger
187
248
  : snapshotStale
188
249
  ? KIBI_BRAND.warning
@@ -196,8 +257,12 @@ export async function reportCommand(options, deps = {}) {
196
257
  const badge = renderKibiBadge(badgeMessage, badgeColor);
197
258
  await writeAtomically(outputPath, html);
198
259
  await writeAtomically(badgePath, badge);
260
+ if (kbCertificatesSource !== undefined)
261
+ await writeAtomically(kbCertificatesPath, kbCertificatesSource.raw);
199
262
  console.log(`Kibi report written to ${outputPath}`);
200
263
  console.log(`Kibi badge written to ${badgePath}`);
264
+ if (kbCertificatesSource !== undefined)
265
+ console.log(`KB certificates written to ${kbCertificatesPath}`);
201
266
  if (options.open === true) {
202
267
  await (deps.openReport ?? openReport)(outputPath);
203
268
  console.log("Opened Kibi report in the default browser");
@@ -1 +1 @@
1
- {"version":3,"file":"signoff.d.ts","sourceRoot":"","sources":["../../src/commands/signoff.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,kBAAkB,EAKxB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC,CAAC;AAGH,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,GACjD,QAAQ,CAAC;IACP,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD,CAAC,CAAC;AAKL,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAGxD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAGnE;AAuFD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,WAAW,CAAC,CA4EtB"}
1
+ {"version":3,"file":"signoff.d.ts","sourceRoot":"","sources":["../../src/commands/signoff.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,kBAAkB,EAKxB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAML,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC,CAAC;AAGH,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,GACjD,QAAQ,CAAC;IACP,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD,CAAC,CAAC;AAKL,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAGxD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAGnE;AA6GD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,WAAW,CAAC,CA4EtB"}
@@ -3,7 +3,7 @@ import { readFileSync } from "node:fs";
3
3
  import { mkdir, rename, rm, writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { coverageSpec } from "../public/operations/specs/reporting.js";
6
- import { earliestUnmetGate } from "../report/html-report.js";
6
+ import { certificateRejectionLines, earliestUnmetGate, } from "../report/html-report.js";
7
7
  import { collectSignoffQuestions, collectSignoffSummary, parseSignoffQuestionsInput, } from "../signoff/collect.js";
8
8
  import { renderSignoffHtml } from "../signoff/html.js";
9
9
  import { renderQuestionsMarkdownBody, renderSignoffChatMarkdown, renderSignoffMarkdown, renderSummaryMarkdownBody, } from "../signoff/markdown.js";
@@ -37,6 +37,21 @@ async function writeAtomically(filePath, contents) {
37
37
  throw error;
38
38
  }
39
39
  }
40
+ // implements REQ-kibi-evidence-floors
41
+ function signoffFloor(value) {
42
+ const floor = record(value);
43
+ return typeof floor.required === "string" &&
44
+ floor.required !== "unverified" &&
45
+ typeof floor.status === "string"
46
+ ? {
47
+ evidenceFloor: {
48
+ required: floor.required,
49
+ source: typeof floor.source === "string" ? floor.source : "explicit",
50
+ status: floor.status,
51
+ },
52
+ }
53
+ : {};
54
+ }
40
55
  function record(value) {
41
56
  return value !== null && typeof value === "object"
42
57
  ? value
@@ -78,6 +93,10 @@ async function loadProof() {
78
93
  ...(typeof row.evidenceLevel === "string"
79
94
  ? { evidenceLevel: row.evidenceLevel }
80
95
  : {}),
96
+ ...signoffFloor(row.evidenceFloor),
97
+ ...(certificateRejectionLines(row).length > 0
98
+ ? { certificateRejections: certificateRejectionLines(row) }
99
+ : {}),
81
100
  contradictions: conflicts
82
101
  .map(record)
83
102
  .filter((conflict) => conflict.status === "contradiction")
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/manifest.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,EAML,uBAAuB,EACxB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,KAAK,mBAAmB,EACxB,uBAAuB,EACxB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EACL,0BAA0B,EAG3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAG5E,UAAU,YAAY;IACpB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,2BAA2B,EAAE,OAAO,2BAA2B,CAAC;CACjE;AAkBD,eAAO,MAAM,8BAA8B,QAc1C,CAAC;AAeF;;;;;;;;;GASG;AACH,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAG7E;AAyHD,wBAAsB,0BAA0B,CAE9C,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GACA,OAAO,CAAC,IAAI,CAAC,CA0Mf;AAED;;;;;;;;GAQG;AAEH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC3B,MAAM,GAAG,IAAI,CAWf;AAED,wBAAgB,0BAA0B,CAExC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAOT;AAED,wBAAgB,8BAA8B,CAE5C,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC3B,OAAO,CAST"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/manifest.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,EAML,uBAAuB,EACxB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,KAAK,mBAAmB,EACxB,uBAAuB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,0BAA0B,EAG3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAI5E,UAAU,YAAY;IACpB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,2BAA2B,EAAE,OAAO,2BAA2B,CAAC;CACjE;AAkBD,eAAO,MAAM,8BAA8B,QAc1C,CAAC;AA0BF;;;;;;;;;GASG;AACH,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAG7E;AAyHD,wBAAsB,0BAA0B,CAE9C,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GACA,OAAO,CAAC,IAAI,CAAC,CAsNf;AAED;;;;;;;;GAQG;AAEH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC3B,MAAM,GAAG,IAAI,CAWf;AAED,wBAAgB,0BAA0B,CAExC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAOT;AAED,wBAAgB,8BAA8B,CAE5C,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC3B,OAAO,CAST"}