kibi-cli 2.31.0 → 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 (226) 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 +21 -2
  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 +4 -3
  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-decisions/resources/signoff-views.md +3 -2
  150. package/dist/public/skills/kibi-freshness/SKILL.md +5 -1
  151. package/dist/public/skills/kibi-traceability/SKILL.md +11 -1
  152. package/dist/public/skills/kibi-usage/SKILL.md +6 -4
  153. package/dist/public/skills/kibi-usage/resources/proof.md +124 -19
  154. package/dist/public/skills/kibi-usage/resources/workflows.md +4 -4
  155. package/dist/report/html-report.css +38 -1
  156. package/dist/report/html-report.d.ts +8 -0
  157. package/dist/report/html-report.d.ts.map +1 -1
  158. package/dist/report/html-report.js +141 -6
  159. package/dist/report/report-view.d.ts +1 -1
  160. package/dist/report/report-view.d.ts.map +1 -1
  161. package/dist/report/report-view.js +7 -0
  162. package/dist/schemas/entity.schema.json +212 -4
  163. package/dist/search-answer.d.ts.map +1 -1
  164. package/dist/search-answer.js +1 -0
  165. package/dist/signoff/html.d.ts.map +1 -1
  166. package/dist/signoff/html.js +3 -0
  167. package/dist/signoff/markdown.d.ts.map +1 -1
  168. package/dist/signoff/markdown.js +3 -0
  169. package/dist/signoff/model.d.ts +20 -0
  170. package/dist/signoff/model.d.ts.map +1 -1
  171. package/dist/signoff/model.js +20 -1
  172. package/dist/traceability/impact-knowledge.d.ts.map +1 -1
  173. package/dist/traceability/impact-knowledge.js +1 -0
  174. package/dist/traceability/temp-kb.d.ts.map +1 -1
  175. package/dist/traceability/temp-kb.js +1 -0
  176. package/dist/types/entities.d.ts +4 -1
  177. package/dist/types/entities.d.ts.map +1 -1
  178. package/dist/utils/merge-driver-setup.d.ts +8 -1
  179. package/dist/utils/merge-driver-setup.d.ts.map +1 -1
  180. package/dist/utils/merge-driver-setup.js +23 -2
  181. package/dist/utils/rule-registry.generated.d.ts +10 -0
  182. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  183. package/dist/utils/rule-registry.generated.js +12 -0
  184. package/dist/utils/schema-version.d.ts +1 -1
  185. package/dist/utils/schema-version.d.ts.map +1 -1
  186. package/dist/utils/schema-version.js +5 -1
  187. package/package.json +6 -2
  188. package/schema/entities.pl +6 -0
  189. package/schema/validation.pl +4 -1
  190. package/src/public/evidence-levels.ts +23 -0
  191. package/src/public/impact/coverage-depth-quality.ts +73 -0
  192. package/src/public/impact/full-kb-quality.ts +6 -1
  193. package/src/public/operations/check-executor.ts +13 -0
  194. package/src/public/operations/evidence-floor.ts +203 -0
  195. package/src/public/operations/migration-plan.ts +39 -6
  196. package/src/public/operations/node-ports.ts +3 -1
  197. package/src/public/operations/proof-contract-claims.ts +103 -0
  198. package/src/public/operations/specs/check-rules.generated.ts +3 -1
  199. package/src/public/operations/specs/mutation-schemas.ts +17 -2
  200. package/src/public/operations/specs/reporting.ts +20 -10
  201. package/src/public/proof-certificates.ts +271 -0
  202. package/src/public/proof-protocol-types.d.ts +17 -1
  203. package/src/public/proof-protocol.ts +318 -3
  204. package/src/public/proof-receipt.ts +127 -7
  205. package/src/public/schemas/entity.ts +19 -1
  206. package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
  207. package/src/public/skills/kibi-commit-review/SKILL.md +4 -3
  208. package/src/public/skills/kibi-commit-review/resources/staged-gate.md +6 -1
  209. package/src/public/skills/kibi-decisions/SKILL.md +10 -2
  210. package/src/public/skills/kibi-decisions/resources/decision-records.md +6 -1
  211. package/src/public/skills/kibi-decisions/resources/signoff-views.md +3 -2
  212. package/src/public/skills/kibi-freshness/SKILL.md +5 -1
  213. package/src/public/skills/kibi-traceability/SKILL.md +11 -1
  214. package/src/public/skills/kibi-usage/SKILL.md +6 -4
  215. package/src/public/skills/kibi-usage/resources/proof.md +124 -19
  216. package/src/public/skills/kibi-usage/resources/workflows.md +4 -4
  217. package/src/schemas/entity.schema.json +212 -4
  218. package/dist/commands/proof-impact.d.ts +0 -7
  219. package/dist/commands/proof-impact.d.ts.map +0 -1
  220. package/dist/commands/proof-impact.js +0 -39
  221. package/dist/operations/proof/baseline-diff.d.ts +0 -41
  222. package/dist/operations/proof/baseline-diff.d.ts.map +0 -1
  223. package/dist/operations/proof/baseline-diff.js +0 -153
  224. package/dist/operations/proof/impact.d.ts +0 -51
  225. package/dist/operations/proof/impact.d.ts.map +0 -1
  226. package/dist/operations/proof/impact.js +0 -138
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-cli",
3
- "version": "2.31.0",
3
+ "version": "2.32.0",
4
4
  "type": "module",
5
5
  "description": "Kibi CLI for knowledge base management",
6
6
  "engines": {
@@ -92,6 +92,10 @@
92
92
  "types": "./dist/public/extractors/markdown.d.ts",
93
93
  "default": "./dist/public/extractors/markdown.js"
94
94
  },
95
+ "./proof-certificates": {
96
+ "types": "./dist/public/proof-certificates.d.ts",
97
+ "default": "./dist/public/proof-certificates.js"
98
+ },
95
99
  "./schemas/entity": {
96
100
  "types": "./dist/public/schemas/entity.d.ts",
97
101
  "default": "./dist/public/schemas/entity.js"
@@ -235,7 +239,7 @@
235
239
  "fast-glob": "^3.2.12",
236
240
  "gray-matter": "^4.0.3",
237
241
  "js-yaml": "^4.1.0",
238
- "kibi-core": "^0.23.0",
242
+ "kibi-core": "^0.24.0",
239
243
  "kibi-plugin-builtin": "^0.5.3",
240
244
  "kibi-plugin-sdk": "^0.5.0",
241
245
  "kibi-swipl": "^1.0.1",
@@ -56,6 +56,9 @@ entity_property(req, exempts_claims, list).
56
56
  % Absent reads as default.
57
57
  entity_property(req, firmness, atom).
58
58
  entity_property(adr, firmness, atom).
59
+ % The weakest evidence level a current requirement accepts: proved, tested,
60
+ % asserted or unverified. Absent: tested when firm, otherwise unverified.
61
+ entity_property(req, required_evidence, atom).
59
62
  % Intended outcome of a scenario: success, rejection or error.
60
63
  entity_property(scenario, expects, atom).
61
64
 
@@ -69,6 +72,8 @@ entity_property(fact, value_string, string).
69
72
  entity_property(fact, value_int, integer).
70
73
  entity_property(fact, value_number, number).
71
74
  entity_property(fact, value_bool, boolean).
75
+ % The exact members of a set fact (value_type set).
76
+ entity_property(fact, value_set, list).
72
77
  entity_property(fact, unit, string).
73
78
  entity_property(fact, scope, string).
74
79
  entity_property(fact, polarity, atom).
@@ -150,6 +155,7 @@ optional_property(req, rationale).
150
155
  optional_property(req, exempts_claims).
151
156
  optional_property(req, firmness).
152
157
  optional_property(adr, firmness).
158
+ optional_property(req, required_evidence).
153
159
  optional_property(test, verification_scope).
154
160
  optional_property(test, verification_perspective).
155
161
  optional_property(test, proof_contract).
@@ -108,6 +108,7 @@ is_fact_only_field(value_string).
108
108
  is_fact_only_field(value_int).
109
109
  is_fact_only_field(value_number).
110
110
  is_fact_only_field(value_bool).
111
+ is_fact_only_field(value_set).
111
112
  is_fact_only_field(unit).
112
113
  is_fact_only_field(scope).
113
114
  is_fact_only_field(polarity).
@@ -227,7 +228,7 @@ text_atom(Value, Atom) :- string(Value), atom_string(Atom, Value).
227
228
  valid_operator(Op) :- memberchk(Op, [eq, neq, lt, lte, gt, gte]), !.
228
229
 
229
230
  % valid_value_type(+VT)
230
- valid_value_type(ValueType) :- memberchk(ValueType, [string, int, number, bool]), !.
231
+ valid_value_type(ValueType) :- memberchk(ValueType, [string, int, number, bool, set]), !.
231
232
 
232
233
  % valid_polarity_in_props(+Props)
233
234
  % Validates polarity if present; succeeds if no polarity in props
@@ -299,6 +300,7 @@ is_value_field(value_string).
299
300
  is_value_field(value_int).
300
301
  is_value_field(value_number).
301
302
  is_value_field(value_bool).
303
+ is_value_field(value_set).
302
304
 
303
305
  % value_type_matches_field(+ValueType, +Props)
304
306
  % Ensures that value_type matches the actual value field present
@@ -306,6 +308,7 @@ value_type_matches_field(string, Props) :- memberchk(value_string=_, Props), !.
306
308
  value_type_matches_field(int, Props) :- memberchk(value_int=_, Props), !.
307
309
  value_type_matches_field(number, Props) :- memberchk(value_number=_, Props), !.
308
310
  value_type_matches_field(bool, Props) :- memberchk(value_bool=_, Props), !.
311
+ value_type_matches_field(set, Props) :- memberchk(value_set=_, Props), !.
309
312
 
310
313
  % valid_origin_in_props(+Props)
311
314
  % Entity provenance travels as a JSON object string (or a dict). When present
@@ -0,0 +1,23 @@
1
+ /**
2
+ * How strong the evidence behind a requirement is, strongest first. proved: a
3
+ * re-checkable certificate covers every case. tested: fresh passing E2E
4
+ * receipts cover every linked scenario. asserted: a person stands behind it.
5
+ * unverified: an agent wrote it and no person approved it. The same list is
6
+ * the vocabulary of required_evidence, a requirement's evidence floor.
7
+ */
8
+ // implements REQ-kibi-evidence-levels, REQ-kibi-evidence-floors
9
+ export const EVIDENCE_LEVELS = [
10
+ "proved",
11
+ "tested",
12
+ "asserted",
13
+ "unverified",
14
+ ] as const;
15
+
16
+ // implements REQ-kibi-evidence-levels
17
+ export type EvidenceLevel = (typeof EVIDENCE_LEVELS)[number];
18
+
19
+ /** Higher is stronger: unverified 0 through proved 3. */
20
+ // implements REQ-kibi-evidence-floors
21
+ export function evidenceRank(level: EvidenceLevel): number {
22
+ return EVIDENCE_LEVELS.length - 1 - EVIDENCE_LEVELS.indexOf(level);
23
+ }
@@ -2,6 +2,7 @@ import type {
2
2
  ExtractedEntity,
3
3
  ExtractionResult,
4
4
  } from "../../extractors/markdown.js";
5
+ import { COARSE_GRANULARITY_REASONS } from "../symbol-granularity.generated.js";
5
6
  import type { QualityDiagnostic } from "./types.js";
6
7
 
7
8
  type ReviewableCoverageDepth =
@@ -347,3 +348,75 @@ export function createCoverageDepthQualityDiagnostics(
347
348
  (left.entityId ?? "").localeCompare(right.entityId ?? ""),
348
349
  );
349
350
  }
351
+
352
+ const COARSE_REASONS: ReadonlySet<string> = new Set(COARSE_GRANULARITY_REASONS);
353
+
354
+ /**
355
+ * Requirements whose every implementing production symbol is a whole-file
356
+ * anchor (a coarse granularity_reason). Coverage then only says some test
357
+ * touches the file, so `tested` evidence is cheap; narrower behavioral
358
+ * symbols would tie it to the code that implements the claim.
359
+ */
360
+ // implements REQ-audit-quality-diagnostics-v1
361
+ export function createCoarseAnchorQualityDiagnostics(
362
+ manifestResults: readonly ExtractionResult[],
363
+ ): readonly QualityDiagnostic[] {
364
+ const superseded = supersededRequirementIds(manifestResults);
365
+ const anchorsByRequirement = new Map<string, ExtractedEntity[]>();
366
+ for (const result of manifestResults) {
367
+ if (result.entity.type !== "symbol") continue;
368
+ // Executable test code is not a production symbol.
369
+ if (result.relationships.some((rel) => rel.type === "executable_for"))
370
+ continue;
371
+ for (const relationship of result.relationships) {
372
+ if (relationship.type !== "implements") continue;
373
+ const list = anchorsByRequirement.get(relationship.to) ?? [];
374
+ list.push(result.entity);
375
+ anchorsByRequirement.set(relationship.to, list);
376
+ }
377
+ }
378
+ return manifestResults
379
+ .filter(
380
+ (result) =>
381
+ result.entity.type === "req" &&
382
+ isReviewableRequirement(result.entity, superseded),
383
+ )
384
+ .flatMap((result): QualityDiagnostic[] => {
385
+ const symbols = uniqueEntities(
386
+ anchorsByRequirement.get(result.entity.id) ?? [],
387
+ );
388
+ if (
389
+ symbols.length === 0 ||
390
+ !symbols.every((symbol) =>
391
+ COARSE_REASONS.has(symbol.granularity_reason ?? ""),
392
+ )
393
+ )
394
+ return [];
395
+ const requirement = result.entity;
396
+ return [
397
+ {
398
+ id: "coarse_anchor_only_review",
399
+ severity: "review",
400
+ blocking: false,
401
+ category: "coverage",
402
+ entityId: requirement.id,
403
+ source: requirement.source,
404
+ files: [requirement.source],
405
+ message: `Requirement ${requirement.id} (${requirement.title}) is implemented only by whole-file anchors, so its coverage says a test touches those files, not the code behind the claim.`,
406
+ suggestion:
407
+ "Link the functions, methods or types that implement the requirement with implements (and covered_by to their tests); keep the file anchor only for genuinely module-level behavior.",
408
+ evidence: {
409
+ requirementId: requirement.id,
410
+ title: requirement.title,
411
+ symbols: symbols.map((symbol) => ({
412
+ id: symbol.id,
413
+ granularityReason: symbol.granularity_reason ?? "",
414
+ })),
415
+ },
416
+ },
417
+ ];
418
+ })
419
+ .toSorted((left, right) =>
420
+ (left.entityId ?? "").localeCompare(right.entityId ?? ""),
421
+ );
422
+ }
@@ -24,7 +24,10 @@ import {
24
24
  createTelemetryAcceptanceDiagnostics,
25
25
  parseTelemetryUsageLog,
26
26
  } from "../telemetry-acceptance.js";
27
- import { createCoverageDepthQualityDiagnostics } from "./coverage-depth-quality.js";
27
+ import {
28
+ createCoarseAnchorQualityDiagnostics,
29
+ createCoverageDepthQualityDiagnostics,
30
+ } from "./coverage-depth-quality.js";
28
31
  import { createRequirementQualityDiagnostics } from "./requirement-quality.js";
29
32
  import { createSymbolQualityDiagnostics } from "./symbol-quality.js";
30
33
  import type { QualityDiagnostic } from "./types.js";
@@ -297,6 +300,7 @@ function toExtractedEntity(entity: Record<string, unknown>): ExtractedEntity {
297
300
  "aliases",
298
301
  "examples",
299
302
  "predicate_args",
303
+ "value_set",
300
304
  ] as const) {
301
305
  const values = optionalStringArrayField(entity, field);
302
306
  if (values !== undefined) extracted[field] = values;
@@ -786,6 +790,7 @@ export async function collectFullKbQualityDiagnostics(
786
790
  : {}),
787
791
  }),
788
792
  ...coverageDepthDiagnostics,
793
+ ...createCoarseAnchorQualityDiagnostics(manifestResults),
789
794
  ...createSymbolQualityDiagnostics({
790
795
  manifestResults,
791
796
  symbolsByFile: new Map(),
@@ -46,6 +46,7 @@ import { collectCheckPolicyViolations } from "./check-policy-rules.js";
46
46
  import { collectDecisionFirmnessViolations } from "./decision-firmness.js";
47
47
  import { executeStatus } from "./discovery-executors.js";
48
48
  import { collectEntityContextViolations } from "./entity-context.js";
49
+ import { collectEvidenceFloorViolations } from "./evidence-floor.js";
49
50
  import {
50
51
  buildActionsFromCheck,
51
52
  buildMigrationPlan,
@@ -53,6 +54,7 @@ import {
53
54
  } from "./migration-plan.js";
54
55
  import { collectOriginReviewViolations } from "./origin-review.js";
55
56
  import { collectPredicateSchemaConformanceViolations } from "./predicate-schema-conformance.js";
57
+ import { collectProofContractClaimViolations } from "./proof-contract-claims.js";
56
58
  import type { OperationContext, PrologPort } from "./runtime-types.js";
57
59
  import {
58
60
  SOURCE_PATH_DANGLING_RULE,
@@ -72,6 +74,8 @@ export type CheckInput = {
72
74
  readonly includeWorkingTreeDiff?: boolean;
73
75
  readonly includeImpactDiagnostics?: boolean;
74
76
  readonly maxDiagnostics?: number;
77
+ /** evidence-floor judges receipts as they are and needs a clean snapshot. */
78
+ readonly freshEvidence?: boolean;
75
79
  };
76
80
 
77
81
  export type CheckPayload = CheckStructuredContent;
@@ -393,6 +397,13 @@ export async function executeCheck(
393
397
  rulesAllowlist,
394
398
  workspaceRoot,
395
399
  );
400
+ const evidenceFloorFindings = await collectEvidenceFloorViolations(
401
+ context,
402
+ rulesAllowlist,
403
+ { freshEvidence: args.freshEvidence === true },
404
+ );
405
+ const proofContractClaimFindings =
406
+ await collectProofContractClaimViolations(context, rulesAllowlist);
396
407
  const partitioned = partitionCheckFindings([
397
408
  ...aggregatedFindings,
398
409
  ...queryPlanViolations,
@@ -404,6 +415,8 @@ export async function executeCheck(
404
415
  ...entityContextFindings,
405
416
  ...decisionFirmnessFindings,
406
417
  ...checkPolicyFindings,
418
+ ...evidenceFloorFindings,
419
+ ...proofContractClaimFindings,
407
420
  ]);
408
421
  const violations: Violation[] = partitioned.violations;
409
422
  const evaluated = await describeEvaluation(prolog, rulesAllowlist);
@@ -0,0 +1,203 @@
1
+ /*
2
+ Kibi — repo-local, per-branch, queryable long-term memory for software projects
3
+ Copyright (C) 2026 Piotr Franczyk
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Affero General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Affero General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Affero General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ import { readKbManifest } from "../../utils/kb-manifest.js";
20
+ import type { Violation } from "../../utils/rule-registry.js";
21
+ import { PROOF_RECEIPT_MAX_AGE_SECONDS } from "../proof-receipt.js";
22
+ import { runOperationJsonQuery, toPrologAtom } from "./prolog-json.js";
23
+ import type { OperationContext } from "./runtime-types.js";
24
+ import { readWorkspaceSnapshot } from "./workspace-snapshot.js";
25
+
26
+ /**
27
+ * The schema version from which a current requirement's evidence level must
28
+ * reach its floor; older KBs get the floor with their schema upgrade, whose
29
+ * migration records the floor of firm requirements without a proof path.
30
+ */
31
+ // implements REQ-kibi-evidence-floors
32
+ export const EVIDENCE_FLOOR_SCHEMA_VERSION = 11;
33
+
34
+ /** A current requirement's evidence level must reach its floor. */
35
+ // implements REQ-kibi-evidence-floors
36
+ export const EVIDENCE_FLOOR_RULE = "evidence-floor";
37
+
38
+ // implements REQ-kibi-evidence-floors
39
+ export type EvidenceFloorRow = Readonly<{
40
+ id: string;
41
+ evidenceLevel: string;
42
+ requiredEvidence: string;
43
+ requiredEvidenceSource: string;
44
+ status: "below" | "pending_reproof";
45
+ proofStatus: string;
46
+ proofGaps: readonly string[];
47
+ /** The tests and symbols behind the gaps; empty lists are left out. */
48
+ gapDetails?: Readonly<Record<string, readonly string[]>>;
49
+ }>;
50
+
51
+ type EvidenceFloorPayload = {
52
+ readonly evaluated?: number;
53
+ readonly rows?: readonly EvidenceFloorRow[];
54
+ };
55
+
56
+ export type EvidenceFloorOptions = Readonly<{
57
+ /**
58
+ * Judge receipts as they are: stale receipts no longer count as pending
59
+ * re-proof, and the proof snapshot must be clean. CI uses it right after
60
+ * `kibi prove --all`.
61
+ */
62
+ freshEvidence?: boolean;
63
+ }>;
64
+
65
+ function floorSource(row: EvidenceFloorRow): string {
66
+ return row.requiredEvidenceSource === "explicit"
67
+ ? "its required_evidence"
68
+ : row.requiredEvidence === "tested"
69
+ ? "the floor of a firm requirement"
70
+ : "its floor";
71
+ }
72
+
73
+ const GAP_DETAIL_LABELS: Readonly<Record<string, string>> = {
74
+ failedReceiptTests: "failing receipts",
75
+ missingReceiptTests: "no receipt",
76
+ staleReceiptTests: "stale receipts",
77
+ invalidReceiptTests: "invalid receipts",
78
+ contractMismatchReceiptTests: "receipts for an older contract",
79
+ uncoveredSymbols: "production symbols without qualifying coverage",
80
+ missingCoordinateSymbols: "symbols without source coordinates",
81
+ unprovedClaims: "claims no certificate derives",
82
+ };
83
+
84
+ /** The entities behind the gaps, for a person reading kibi check's text. */
85
+ // implements REQ-kibi-evidence-floors
86
+ export function gapDetailLines(row: EvidenceFloorRow): string[] {
87
+ return Object.entries(row.gapDetails ?? {}).flatMap(([key, ids]) =>
88
+ ids.length === 0
89
+ ? []
90
+ : [
91
+ `${GAP_DETAIL_LABELS[key] ?? key}: ${ids.slice(0, 8).join(", ")}${ids.length > 8 ? ` (+${ids.length - 8} more)` : ""}`,
92
+ ],
93
+ );
94
+ }
95
+
96
+ function suggestionFor(row: EvidenceFloorRow): string {
97
+ if (row.status === "pending_reproof")
98
+ return `Run kibi prove for the tests behind ${row.id} so its receipts match the current code, then rerun kibi check --fresh-evidence.`;
99
+ switch (row.requiredEvidence) {
100
+ case "proved":
101
+ return `Prove every applicable claim of ${row.id}${(row.gapDetails?.unprovedClaims ?? []).length > 0 ? ` (unproved: ${(row.gapDetails?.unprovedClaims ?? []).join(", ")})` : ""}: model each as a set fact (prove it with exhaustive or typecheck) or a numeric fact (arithmetic or smt), name it in a certificate test's proof_contract.proves, and run kibi prove; kibi proof explain ${row.id} shows each claim's bindings. A claim with no such fact can only be attested, so lower required_evidence to tested with the reason in the requirement if that is enough.`;
102
+ case "tested":
103
+ return `Link ${row.id} to a scenario with an end-to-end test that has a proof contract, run kibi prove, and fix its proof gaps (${row.proofGaps.join(", ") || "none listed"}${gapDetailLines(row).length > 0 ? `; ${gapDetailLines(row).join("; ")}` : ""}); kibi proof explain ${row.id} shows each one. Or lower required_evidence if a person decides it does not need tests.`;
104
+ default:
105
+ return `Have a person approve ${row.id} (origin.approved_by) or state it as firm with origin.ref to their words, or lower required_evidence.`;
106
+ }
107
+ }
108
+
109
+ /** One finding per current requirement whose evidence is below its floor. */
110
+ // implements REQ-kibi-evidence-floors
111
+ export function evidenceFloorViolations(
112
+ rows: readonly EvidenceFloorRow[],
113
+ options: EvidenceFloorOptions = {},
114
+ ): Violation[] {
115
+ return rows
116
+ .filter((row) => options.freshEvidence === true || row.status === "below")
117
+ .map((row) => ({
118
+ rule: EVIDENCE_FLOOR_RULE,
119
+ entityId: row.id,
120
+ description:
121
+ row.status === "pending_reproof"
122
+ ? `${row.id} is ${row.evidenceLevel} but needs ${row.requiredEvidence} (${floorSource(row)}); its receipts are older than the code`
123
+ : `${row.id} is ${row.evidenceLevel} but needs ${row.requiredEvidence} (${floorSource(row)})`,
124
+ suggestion: suggestionFor(row),
125
+ evidence: {
126
+ evidenceLevel: row.evidenceLevel,
127
+ requiredEvidence: row.requiredEvidence,
128
+ requiredEvidenceSource: row.requiredEvidenceSource,
129
+ status: row.status,
130
+ proofStatus: row.proofStatus,
131
+ proofGaps: row.proofGaps,
132
+ ...(row.gapDetails === undefined ? {} : { gapDetails: row.gapDetails }),
133
+ },
134
+ }));
135
+ }
136
+
137
+ /**
138
+ * Every current requirement whose evidence is below its floor (status below
139
+ * or pending_reproof), judged on the attached store with the same
140
+ * per-contract receipt binding kb_coverage uses. Unlike the check rule this
141
+ * ignores the schema version, so a migration can read the floor the ladder
142
+ * will apply before it records the new schema.
143
+ */
144
+ // implements REQ-kibi-evidence-floors
145
+ export async function readEvidenceFloorRows(
146
+ context: OperationContext,
147
+ snapshotEvidence?: Awaited<ReturnType<typeof readWorkspaceSnapshot>>,
148
+ ): Promise<readonly EvidenceFloorRow[]> {
149
+ const prolog = context.prolog ?? (await context.ensureProlog?.());
150
+ if (prolog === undefined) return [];
151
+ const evidence = snapshotEvidence ?? (await readWorkspaceSnapshot(context));
152
+ const snapshot = evidence.available ? evidence.snapshot.hash : "unknown";
153
+ const { perContractTestBindings } = await import("./specs/reporting.js");
154
+ const bindings = await perContractTestBindings({ ...context, prolog });
155
+ const checkedAt = context.clock().toISOString();
156
+ const goal = `discovery:evidence_floor_json(${bindings === null ? "strict_snapshot, _{}" : `per_contract, ${bindings}`}, ${toPrologAtom(snapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`;
157
+ const payload = await runOperationJsonQuery<EvidenceFloorPayload>(
158
+ prolog,
159
+ "discovery.pl",
160
+ goal,
161
+ "Evidence floor evaluation",
162
+ );
163
+ return payload.rows ?? [];
164
+ }
165
+
166
+ /**
167
+ * Evaluate the evidence-floor rule against the attached store, with the same
168
+ * per-contract receipt binding kb_coverage uses.
169
+ */
170
+ // implements REQ-kibi-evidence-floors
171
+ export async function collectEvidenceFloorViolations(
172
+ context: OperationContext,
173
+ rules: ReadonlySet<string>,
174
+ options: EvidenceFloorOptions = {},
175
+ ): Promise<Violation[]> {
176
+ if (!rules.has(EVIDENCE_FLOOR_RULE) || context.prolog === undefined)
177
+ return [];
178
+ // A store without a manifest predates schema 11 as much as an old one.
179
+ const schemaVersion = readKbManifest(context.workspaceRoot)?.schemaVersion;
180
+ if (
181
+ schemaVersion === undefined ||
182
+ schemaVersion < EVIDENCE_FLOOR_SCHEMA_VERSION
183
+ )
184
+ return [];
185
+ const snapshotEvidence = await readWorkspaceSnapshot(context);
186
+ const rows = await readEvidenceFloorRows(context, snapshotEvidence);
187
+ const violations = evidenceFloorViolations(rows, options);
188
+ if (options.freshEvidence === true) {
189
+ const clean =
190
+ snapshotEvidence.available && snapshotEvidence.snapshot.dirty === false;
191
+ if (!clean)
192
+ violations.unshift({
193
+ rule: EVIDENCE_FLOOR_RULE,
194
+ entityId: "proof-snapshot",
195
+ description: snapshotEvidence.available
196
+ ? "--fresh-evidence needs a clean proof snapshot, but the working tree has uncommitted changes"
197
+ : `--fresh-evidence needs a proof snapshot, but none is available: ${snapshotEvidence.error}`,
198
+ suggestion:
199
+ "Commit or stash working-tree changes (or run inside a Git checkout), run kibi prove, then rerun kibi check --fresh-evidence.",
200
+ });
201
+ }
202
+ return violations;
203
+ }
@@ -297,8 +297,11 @@ export function mergeMigrationPlans(
297
297
  });
298
298
  }
299
299
 
300
+ /** How many automatic actions a plan digest lists by id. */
301
+ const DIGEST_AUTOMATIC_ACTIONS = 20;
302
+
300
303
  export const MIGRATION_PLAN_DIGEST_VERSION =
301
- "kibi.migration-plan-digest.v1" as const;
304
+ "kibi.migration-plan-digest.v2" as const;
302
305
 
303
306
  export type MigrationPlanDigest = Readonly<{
304
307
  version: typeof MIGRATION_PLAN_DIGEST_VERSION;
@@ -307,7 +310,20 @@ export type MigrationPlanDigest = Readonly<{
307
310
  expected: MigrationPlan["expected"];
308
311
  scope: MigrationPlan["scope"];
309
312
  summary: MigrationPlan["summary"];
310
- automaticActions: readonly MigrationAction[];
313
+ /** The first automatic actions, by id; automaticSummary counts them all. */
314
+ automaticActions: readonly Readonly<{
315
+ id: string;
316
+ code: string;
317
+ state: MigrationActionState;
318
+ autoApplicable: boolean;
319
+ dependsOn: readonly string[];
320
+ }>[];
321
+ automaticSummary: Readonly<{
322
+ total: number;
323
+ ready: number;
324
+ blocked: number;
325
+ omitted: number;
326
+ }>;
311
327
  actionGroups: readonly Readonly<{
312
328
  code: string;
313
329
  safety: MigrationSafety;
@@ -320,9 +336,10 @@ export type MigrationPlanDigest = Readonly<{
320
336
 
321
337
  /**
322
338
  * A bounded summary of a plan for responses that report what is left rather
323
- * than hand over a plan to apply: automatic actions stay whole, every other
324
- * action is counted by code. The full plan comes from status, check and
325
- * coverage.
339
+ * than hand over a plan to apply: the first automatic actions are listed by
340
+ * id and all of them counted, every other action is counted by code. The full
341
+ * plan comes from status, check and coverage, so a response stays small even
342
+ * when hundreds of actions remain.
326
343
  */
327
344
  // implements REQ-agent-guided-migration-orchestration
328
345
  export function digestMigrationPlan(plan: MigrationPlan): MigrationPlanDigest {
@@ -365,7 +382,23 @@ export function digestMigrationPlan(plan: MigrationPlan): MigrationPlanDigest {
365
382
  expected: plan.expected,
366
383
  scope: plan.scope,
367
384
  summary: plan.summary,
368
- automaticActions,
385
+ automaticActions: automaticActions
386
+ .slice(0, DIGEST_AUTOMATIC_ACTIONS)
387
+ .map((action) => ({
388
+ id: action.id,
389
+ code: action.code,
390
+ state: action.state,
391
+ autoApplicable: action.autoApplicable,
392
+ dependsOn: action.dependsOn,
393
+ })),
394
+ automaticSummary: {
395
+ total: automaticActions.length,
396
+ ready: automaticActions.filter(
397
+ (action) => action.state === "ready" && action.autoApplicable,
398
+ ).length,
399
+ blocked: blockedAutomatic,
400
+ omitted: Math.max(0, automaticActions.length - DIGEST_AUTOMATIC_ACTIONS),
401
+ },
369
402
  actionGroups: [...groups.values()].sort(
370
403
  (left, right) =>
371
404
  right.count - left.count ||
@@ -79,7 +79,9 @@ function isReceiptOnlyMarkdownChange(
79
79
  const committed = execFileSync(
80
80
  "git",
81
81
  ["-C", workspaceRoot, "show", `HEAD:${relativePath}`],
82
- { encoding: "buffer", maxBuffer: 64 * 1024 * 1024 },
82
+ // A file staged but not committed yet has no HEAD blob; git's error is
83
+ // expected here and must not reach the terminal.
84
+ { encoding: "buffer", maxBuffer: 64 * 1024 * 1024, stdio: "pipe" },
83
85
  ) as Buffer;
84
86
  return working.equals(snapshotFileContent(relativePath, committed));
85
87
  } catch {
@@ -0,0 +1,103 @@
1
+ /*
2
+ Kibi — repo-local, per-branch, queryable long-term memory for software projects
3
+ Copyright (C) 2026 Piotr Franczyk
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Affero General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Affero General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Affero General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ import { readKbManifest } from "../../utils/kb-manifest.js";
20
+ import type { Violation } from "../../utils/rule-registry.js";
21
+ import { runOperationJsonQuery } from "./prolog-json.js";
22
+ import type { OperationContext } from "./runtime-types.js";
23
+
24
+ /**
25
+ * The schema version from which every certificate-kind proof contract must
26
+ * name the claims it proves; older KBs get the rule with their schema
27
+ * upgrade, whose migration pins proves where the claim is unambiguous.
28
+ */
29
+ // implements REQ-kibi-claim-bound-proofs
30
+ export const CLAIM_BINDING_SCHEMA_VERSION = 12;
31
+
32
+ /** A certificate-kind proof contract must name provable claims in proves. */
33
+ // implements REQ-kibi-claim-bound-proofs
34
+ export const PROOF_CONTRACT_CLAIMS_RULE = "proof-contract-claims";
35
+
36
+ // implements REQ-kibi-claim-bound-proofs
37
+ export type ProofContractClaimIssue = Readonly<{
38
+ test_id: string;
39
+ claim_key?: string;
40
+ reason: string;
41
+ }>;
42
+
43
+ /**
44
+ * Every certificate-kind proof contract that names no claims, and every
45
+ * named claim no certificate of its kind could prove. Ignores the schema
46
+ * version, so a migration can read what the rule will report.
47
+ */
48
+ // implements REQ-kibi-claim-bound-proofs
49
+ export async function readProofContractClaimIssues(
50
+ context: OperationContext,
51
+ ): Promise<readonly ProofContractClaimIssue[]> {
52
+ const prolog = context.prolog ?? (await context.ensureProlog?.());
53
+ if (prolog === undefined) return [];
54
+ return runOperationJsonQuery<ProofContractClaimIssue[]>(
55
+ prolog,
56
+ "claim_obligations.pl",
57
+ "claim_obligations:proof_contract_claim_issues_json(JsonString)",
58
+ "Proof contract claims",
59
+ );
60
+ }
61
+
62
+ /** One finding per contract without proves and per unprovable claim key. */
63
+ // implements REQ-kibi-claim-bound-proofs
64
+ export function proofContractClaimViolations(
65
+ issues: readonly ProofContractClaimIssue[],
66
+ ): Violation[] {
67
+ return issues.map((issue) => ({
68
+ rule: PROOF_CONTRACT_CLAIMS_RULE,
69
+ entityId: issue.test_id,
70
+ description:
71
+ issue.claim_key === undefined
72
+ ? `${issue.test_id}: ${issue.reason}`
73
+ : `${issue.test_id} proves ${issue.claim_key}: ${issue.reason}`,
74
+ suggestion:
75
+ issue.claim_key === undefined
76
+ ? `Add proof_contract.proves to ${issue.test_id} with the claim keys (CLAIM-...) of the requirement its certificate establishes; kibi proof explain on the requirement lists its claims.`
77
+ : `Name a modeled claim of a requirement ${issue.test_id} reaches through its scenarios, grounded by a fact this proof kind can express (a set fact for exhaustive or typecheck, a numeric fact for arithmetic or smt), or remove ${issue.claim_key} from proves.`,
78
+ evidence: {
79
+ testId: issue.test_id,
80
+ ...(issue.claim_key === undefined ? {} : { claimKey: issue.claim_key }),
81
+ reason: issue.reason,
82
+ },
83
+ }));
84
+ }
85
+
86
+ /** Evaluate the proof-contract-claims rule from schema 12. */
87
+ // implements REQ-kibi-claim-bound-proofs
88
+ export async function collectProofContractClaimViolations(
89
+ context: OperationContext,
90
+ rules: ReadonlySet<string>,
91
+ ): Promise<Violation[]> {
92
+ if (!rules.has(PROOF_CONTRACT_CLAIMS_RULE) || context.prolog === undefined)
93
+ return [];
94
+ const schemaVersion = readKbManifest(context.workspaceRoot)?.schemaVersion;
95
+ if (
96
+ schemaVersion === undefined ||
97
+ schemaVersion < CLAIM_BINDING_SCHEMA_VERSION
98
+ )
99
+ return [];
100
+ return proofContractClaimViolations(
101
+ await readProofContractClaimIssues(context),
102
+ );
103
+ }