kibi-cli 2.36.0 → 2.39.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-json-command.d.ts.map +1 -1
  2. package/dist/cli-json-command.js +4 -0
  3. package/dist/cli-operation-metadata.d.ts +1 -1
  4. package/dist/cli-operation-metadata.js +1 -1
  5. package/dist/cli-register-foundation.d.ts.map +1 -1
  6. package/dist/cli-register-foundation.js +2 -1
  7. package/dist/cli-register-maintenance.d.ts.map +1 -1
  8. package/dist/cli-register-maintenance.js +5 -0
  9. package/dist/cli-register-reporting.d.ts.map +1 -1
  10. package/dist/cli-register-reporting.js +1 -0
  11. package/dist/cli-validate.d.ts.map +1 -1
  12. package/dist/cli-validate.js +21 -2
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +19 -1
  15. package/dist/commands/aggregated-checks.d.ts.map +1 -1
  16. package/dist/commands/aggregated-checks.js +89 -16
  17. package/dist/commands/coverage.d.ts +1 -0
  18. package/dist/commands/coverage.d.ts.map +1 -1
  19. package/dist/commands/coverage.js +40 -0
  20. package/dist/commands/doctor.d.ts +5 -0
  21. package/dist/commands/doctor.d.ts.map +1 -1
  22. package/dist/commands/doctor.js +70 -35
  23. package/dist/commands/github-init.d.ts.map +1 -1
  24. package/dist/commands/github-init.js +4 -2
  25. package/dist/commands/hook-managers.d.ts +43 -0
  26. package/dist/commands/hook-managers.d.ts.map +1 -0
  27. package/dist/commands/hook-managers.js +426 -0
  28. package/dist/commands/init.d.ts +1 -0
  29. package/dist/commands/init.d.ts.map +1 -1
  30. package/dist/commands/init.js +40 -5
  31. package/dist/commands/migrate.d.ts.map +1 -1
  32. package/dist/commands/migrate.js +9 -3
  33. package/dist/commands/sync/persistence.d.ts.map +1 -1
  34. package/dist/commands/sync/persistence.js +49 -1
  35. package/dist/commands/telemetry.d.ts +7 -0
  36. package/dist/commands/telemetry.d.ts.map +1 -0
  37. package/dist/commands/telemetry.js +23 -0
  38. package/dist/decision-firmness.d.ts.map +1 -1
  39. package/dist/decision-firmness.js +11 -14
  40. package/dist/engine-limits.d.ts.map +1 -1
  41. package/dist/engine-limits.js +8 -10
  42. package/dist/entity-body-context.d.ts.map +1 -1
  43. package/dist/entity-body-context.js +39 -36
  44. package/dist/extractors/markdown.d.ts +6 -0
  45. package/dist/extractors/markdown.d.ts.map +1 -1
  46. package/dist/extractors/markdown.js +21 -0
  47. package/dist/extractors/symbol-coordinates.d.ts.map +1 -1
  48. package/dist/extractors/symbol-coordinates.js +23 -4
  49. package/dist/firm-decision-edit.d.ts.map +1 -1
  50. package/dist/firm-decision-edit.js +1 -3
  51. package/dist/intent-search.d.ts +5 -1
  52. package/dist/intent-search.d.ts.map +1 -1
  53. package/dist/intent-search.js +118 -33
  54. package/dist/logic/ir.d.ts.map +1 -1
  55. package/dist/logic/ir.js +82 -108
  56. package/dist/operations/bootstrap/discovery-evidence.d.ts.map +1 -1
  57. package/dist/operations/bootstrap/discovery-evidence.js +44 -2
  58. package/dist/operations/bootstrap/discovery.d.ts.map +1 -1
  59. package/dist/operations/bootstrap/discovery.js +1 -0
  60. package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
  61. package/dist/operations/bootstrap/guidance-actions.js +2 -1
  62. package/dist/operations/bootstrap/plugin-offers.d.ts +11 -2
  63. package/dist/operations/bootstrap/plugin-offers.d.ts.map +1 -1
  64. package/dist/operations/bootstrap/plugin-offers.js +48 -9
  65. package/dist/operations/proof/claim-bindings.d.ts.map +1 -1
  66. package/dist/operations/proof/claim-bindings.js +1 -0
  67. package/dist/operations/proof/code-certificates.d.ts +2 -0
  68. package/dist/operations/proof/code-certificates.d.ts.map +1 -1
  69. package/dist/operations/proof/code-certificates.js +34 -14
  70. package/dist/operations/proof/code-models.d.ts.map +1 -1
  71. package/dist/operations/proof/code-models.js +19 -5
  72. package/dist/operations/proof/code-runner.d.ts +16 -0
  73. package/dist/operations/proof/code-runner.d.ts.map +1 -1
  74. package/dist/operations/proof/code-runner.js +42 -6
  75. package/dist/operations/proof/explain.d.ts +63 -0
  76. package/dist/operations/proof/explain.d.ts.map +1 -1
  77. package/dist/operations/proof/explain.js +162 -0
  78. package/dist/operations/semantic-advisor/analyze-prose.d.ts +9 -1
  79. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  80. package/dist/operations/semantic-advisor/analyze-prose.js +21 -0
  81. package/dist/operations/semantic-advisor/case-preview.d.ts +24 -0
  82. package/dist/operations/semantic-advisor/case-preview.d.ts.map +1 -0
  83. package/dist/operations/semantic-advisor/case-preview.js +216 -0
  84. package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
  85. package/dist/operations/semantic-advisor/shared.js +3 -0
  86. package/dist/operations/semantic-advisor/types.d.ts +23 -0
  87. package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
  88. package/dist/plugins/approved-source-analyzers.generated.d.ts +11 -8
  89. package/dist/plugins/approved-source-analyzers.generated.d.ts.map +1 -1
  90. package/dist/plugins/approved-source-analyzers.generated.js +11 -8
  91. package/dist/plugins/compose-search-hints.d.ts +29 -0
  92. package/dist/plugins/compose-search-hints.d.ts.map +1 -0
  93. package/dist/plugins/compose-search-hints.js +94 -0
  94. package/dist/plugins/external-allowlist.d.ts +7 -0
  95. package/dist/plugins/external-allowlist.d.ts.map +1 -1
  96. package/dist/plugins/external-allowlist.js +11 -0
  97. package/dist/plugins/registry.d.ts +8 -1
  98. package/dist/plugins/registry.d.ts.map +1 -1
  99. package/dist/plugins/registry.js +22 -3
  100. package/dist/plugins/source-classification.d.ts +2 -2
  101. package/dist/plugins/source-classification.d.ts.map +1 -1
  102. package/dist/plugins/source-classification.js +25 -1
  103. package/dist/prolog/codec.d.ts.map +1 -1
  104. package/dist/prolog/codec.js +10 -20
  105. package/dist/public/impact/coverage-depth-quality.d.ts.map +1 -1
  106. package/dist/public/impact/coverage-depth-quality.js +32 -16
  107. package/dist/public/impact/full-kb-quality.d.ts +6 -0
  108. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  109. package/dist/public/impact/full-kb-quality.js +8 -3
  110. package/dist/public/impact/requirement-quality.d.ts.map +1 -1
  111. package/dist/public/impact/requirement-quality.js +56 -29
  112. package/dist/public/operations/catalog.d.ts +44 -4
  113. package/dist/public/operations/catalog.d.ts.map +1 -1
  114. package/dist/public/operations/check-executor.d.ts +6 -0
  115. package/dist/public/operations/check-executor.d.ts.map +1 -1
  116. package/dist/public/operations/check-executor.js +34 -2
  117. package/dist/public/operations/discovery-executors.d.ts +5 -1
  118. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  119. package/dist/public/operations/discovery-executors.js +65 -4
  120. package/dist/public/operations/evidence-floor.d.ts +8 -1
  121. package/dist/public/operations/evidence-floor.d.ts.map +1 -1
  122. package/dist/public/operations/evidence-floor.js +3 -4
  123. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  124. package/dist/public/operations/migration-plan.js +41 -10
  125. package/dist/public/operations/node-ports.d.ts.map +1 -1
  126. package/dist/public/operations/node-ports.js +3 -1
  127. package/dist/public/operations/repair-plan.d.ts +46 -0
  128. package/dist/public/operations/repair-plan.d.ts.map +1 -1
  129. package/dist/public/operations/repair-plan.js +110 -1
  130. package/dist/public/operations/schema6-check-actions.js +1 -1
  131. package/dist/public/operations/source-analysis-cache.d.ts +22 -0
  132. package/dist/public/operations/source-analysis-cache.d.ts.map +1 -0
  133. package/dist/public/operations/source-analysis-cache.js +157 -0
  134. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  135. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  136. package/dist/public/operations/specs/check-rules.generated.js +2 -1
  137. package/dist/public/operations/specs/check.d.ts +1 -1
  138. package/dist/public/operations/specs/discovery.d.ts +7 -2
  139. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  140. package/dist/public/operations/specs/discovery.js +7 -2
  141. package/dist/public/operations/specs/mutation-schemas.d.ts +36 -1
  142. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  143. package/dist/public/operations/specs/mutation-schemas.js +30 -0
  144. package/dist/public/operations/specs/upsert.d.ts +36 -1
  145. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  146. package/dist/public/operations/symbol-granularity-check.d.ts +3 -1
  147. package/dist/public/operations/symbol-granularity-check.d.ts.map +1 -1
  148. package/dist/public/operations/symbol-granularity-check.js +21 -7
  149. package/dist/public/proof-protocol.d.ts +6 -1
  150. package/dist/public/proof-protocol.d.ts.map +1 -1
  151. package/dist/public/proof-protocol.js +12 -3
  152. package/dist/public/proof-sources.d.ts +2 -0
  153. package/dist/public/proof-sources.d.ts.map +1 -1
  154. package/dist/public/proof-sources.js +32 -6
  155. package/dist/public/schemas/entity.d.ts.map +1 -1
  156. package/dist/public/schemas/entity.js +59 -0
  157. package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
  158. package/dist/public/skills/kibi-commit-review/SKILL.md +6 -2
  159. package/dist/public/skills/kibi-decisions/SKILL.md +7 -2
  160. package/dist/public/skills/kibi-decisions/resources/signoff-views.md +3 -1
  161. package/dist/public/skills/kibi-usage/SKILL.md +50 -3
  162. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +34 -0
  163. package/dist/public/skills/kibi-usage/resources/proof.md +13 -5
  164. package/dist/public/skills/kibi-usage/resources/workflows.md +10 -0
  165. package/dist/relationships/shards.d.ts +2 -0
  166. package/dist/relationships/shards.d.ts.map +1 -1
  167. package/dist/relationships/shards.js +25 -1
  168. package/dist/schemas/entity.schema.json +98 -1
  169. package/dist/search-answer.d.ts +3 -2
  170. package/dist/search-answer.d.ts.map +1 -1
  171. package/dist/search-answer.js +57 -4
  172. package/dist/search-hints.d.ts +58 -0
  173. package/dist/search-hints.d.ts.map +1 -0
  174. package/dist/search-hints.js +187 -0
  175. package/dist/search-ranking.d.ts +7 -0
  176. package/dist/search-ranking.d.ts.map +1 -1
  177. package/dist/search-ranking.js +54 -56
  178. package/dist/search-token-cache.d.ts +30 -0
  179. package/dist/search-token-cache.d.ts.map +1 -0
  180. package/dist/search-token-cache.js +86 -0
  181. package/dist/traceability/impact-knowledge.d.ts.map +1 -1
  182. package/dist/traceability/impact-knowledge.js +2 -0
  183. package/dist/traceability/temp-kb.d.ts.map +1 -1
  184. package/dist/traceability/temp-kb.js +11 -1
  185. package/dist/types/entities.d.ts +12 -0
  186. package/dist/types/entities.d.ts.map +1 -1
  187. package/dist/usage-telemetry.d.ts +20 -0
  188. package/dist/usage-telemetry.d.ts.map +1 -0
  189. package/dist/usage-telemetry.js +93 -0
  190. package/dist/utils/branch-resolver.d.ts.map +1 -1
  191. package/dist/utils/branch-resolver.js +1 -0
  192. package/dist/utils/rule-registry.generated.d.ts +5 -0
  193. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  194. package/dist/utils/rule-registry.generated.js +6 -0
  195. package/package.json +5 -4
  196. package/schema/entities.pl +12 -0
  197. package/src/public/impact/coverage-depth-quality.ts +59 -21
  198. package/src/public/impact/full-kb-quality.ts +15 -1
  199. package/src/public/impact/requirement-quality.ts +95 -38
  200. package/src/public/operations/check-executor.ts +41 -1
  201. package/src/public/operations/discovery-executors.ts +92 -4
  202. package/src/public/operations/evidence-floor.ts +15 -3
  203. package/src/public/operations/migration-plan.ts +41 -9
  204. package/src/public/operations/node-ports.ts +3 -1
  205. package/src/public/operations/repair-plan.ts +152 -1
  206. package/src/public/operations/schema6-check-actions.ts +1 -1
  207. package/src/public/operations/source-analysis-cache.ts +196 -0
  208. package/src/public/operations/specs/check-rules.generated.ts +2 -1
  209. package/src/public/operations/specs/discovery.ts +8 -2
  210. package/src/public/operations/specs/mutation-schemas.ts +34 -0
  211. package/src/public/operations/symbol-granularity-check.ts +25 -5
  212. package/src/public/proof-protocol-types.d.ts +1 -1
  213. package/src/public/proof-protocol.ts +18 -4
  214. package/src/public/proof-sources.ts +37 -6
  215. package/src/public/schemas/entity.ts +65 -0
  216. package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
  217. package/src/public/skills/kibi-commit-review/SKILL.md +6 -2
  218. package/src/public/skills/kibi-decisions/SKILL.md +7 -2
  219. package/src/public/skills/kibi-decisions/resources/signoff-views.md +3 -1
  220. package/src/public/skills/kibi-usage/SKILL.md +50 -3
  221. package/src/public/skills/kibi-usage/resources/fact-lanes.md +34 -0
  222. package/src/public/skills/kibi-usage/resources/proof.md +13 -5
  223. package/src/public/skills/kibi-usage/resources/workflows.md +10 -0
  224. package/src/schemas/entity.schema.json +98 -1
@@ -0,0 +1,93 @@
1
+ import { detectTelemetryHost, flushTelemetry, recordOperationTelemetry, takeTelemetryNotice, } from "kibi-agent-core/usage-telemetry";
2
+ /**
3
+ * Anonymous usage telemetry for the CLI: one summary event per command,
4
+ * named by its registered command path (`sync`, `storage compact`, `search`)
5
+ * and never by its arguments. The queue is sent only when a batch is due, so
6
+ * most runs exit without touching the network.
7
+ */
8
+ /** Commands that manage telemetry itself are not recorded. */
9
+ const UNRECORDED_COMMANDS = new Set(["telemetry"]);
10
+ let notedErrorCategory;
11
+ // implements REQ-kibi-anonymous-usage-telemetry
12
+ /**
13
+ * Name why this command failed with a fixed identifier (an error code, a
14
+ * failing doctor check), for the event recorded at exit. The first note wins.
15
+ */
16
+ export function noteCliErrorCategory(category) {
17
+ if (category && notedErrorCategory === undefined) {
18
+ notedErrorCategory = category;
19
+ }
20
+ }
21
+ // implements REQ-kibi-anonymous-usage-telemetry
22
+ /** The error code of a failed JSON result printed by a command, if any. */
23
+ export function noteCliResultError(stdout) {
24
+ if (!stdout)
25
+ return;
26
+ try {
27
+ const parsed = JSON.parse(stdout);
28
+ if (parsed.status === "error" && typeof parsed.error?.code === "string") {
29
+ noteCliErrorCategory(parsed.error.code);
30
+ }
31
+ }
32
+ catch {
33
+ // Not a JSON result.
34
+ }
35
+ }
36
+ /** A thrown error's code or class name; never its message. */
37
+ function thrownErrorCategory(error) {
38
+ if (error !== null && typeof error === "object") {
39
+ const code = error.code;
40
+ if (typeof code === "string" && code !== "")
41
+ return code;
42
+ const name = error.name;
43
+ if (typeof name === "string" && name !== "" && name !== "Error") {
44
+ return name;
45
+ }
46
+ }
47
+ return "uncaught_error";
48
+ }
49
+ // implements REQ-kibi-anonymous-usage-telemetry
50
+ /** Remember which registered command ran, for the event recorded at exit. */
51
+ export function trackCommandPath(program) {
52
+ let commandPath;
53
+ program.hook("preAction", (_root, actionCommand) => {
54
+ const names = [];
55
+ for (let current = actionCommand; current?.parent; current = current.parent) {
56
+ names.unshift(current.name());
57
+ }
58
+ commandPath = names.join(" ");
59
+ });
60
+ return () => commandPath;
61
+ }
62
+ // implements REQ-kibi-anonymous-usage-telemetry
63
+ export async function finishCliTelemetry(input) {
64
+ const env = input.env ?? process.env;
65
+ const command = input.commandPath;
66
+ const noted = notedErrorCategory;
67
+ notedErrorCategory = undefined;
68
+ if (!command || UNRECORDED_COMMANDS.has(command.split(" ")[0] ?? "")) {
69
+ return;
70
+ }
71
+ const notice = takeTelemetryNotice(env);
72
+ if (notice)
73
+ process.stderr.write(`${notice}\n`);
74
+ recordOperationTelemetry({
75
+ packageName: "kibi-cli",
76
+ packageVersion: input.packageVersion,
77
+ host: detectTelemetryHost(env),
78
+ workspaceRoot: process.cwd(),
79
+ }, {
80
+ interface: "cli",
81
+ operation: command,
82
+ status: input.exitCode === 0 ? "success" : "error",
83
+ durationMs: Date.now() - input.startedAt.getTime(),
84
+ exitCode: input.exitCode,
85
+ errorCategory: input.exitCode === 0
86
+ ? undefined
87
+ : (noted ??
88
+ (input.error === undefined
89
+ ? undefined
90
+ : thrownErrorCategory(input.error))),
91
+ }, env);
92
+ await flushTelemetry({}, env);
93
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"branch-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/branch-resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"branch-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/branch-resolver.ts"],"names":[],"mappings":"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"}
@@ -45,6 +45,7 @@ export function _setBranchResolverDepsForTests(deps) {
45
45
  // Files to exclude when copying branch snapshots (volatile artifacts)
46
46
  const VOLATILE_ARTIFACTS = new Set([
47
47
  "sync-cache.json",
48
+ "source-analysis-cache.json",
48
49
  "journal.log",
49
50
  "audit.log",
50
51
  "lock",
@@ -68,6 +68,11 @@ export declare const GENERATED_RULES: readonly [{
68
68
  readonly description: "Advisory: a scenario that expects success assumes nothing, assumes values that contradict each other, assumes a property value no governing requirement constrains comparably (type, unit or operator mismatch, or conflicting requirements), leaves a governing rule's conditions undecided, or conflicts only with constraints whose validity window may not cover it, so its feasibility cannot be decided";
69
69
  readonly enforcementClass: "advisory";
70
70
  readonly category: "integrity";
71
+ }, {
72
+ readonly name: "scenario-outcome-mismatch";
73
+ readonly description: "A scenario that expects rejection or error must assume a value or situation a current requirement forbids: when every assumption is governed by current requirements and they permit all of them together, the knowledge base predicts success and the scenario exercises no failure side";
74
+ readonly enforcementClass: "canonical";
75
+ readonly category: "integrity";
71
76
  }, {
72
77
  readonly name: "exception-claim-keys";
73
78
  readonly description: "An exception requirement's exempts_claims must name claim keys of a requirement it exempts, and requires an exempts link";
@@ -1 +1 @@
1
- {"version":3,"file":"rule-registry.generated.d.ts","sourceRoot":"","sources":["../../src/utils/rule-registry.generated.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmZlB,CAAC"}
1
+ {"version":3,"file":"rule-registry.generated.d.ts","sourceRoot":"","sources":["../../src/utils/rule-registry.generated.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0ZlB,CAAC"}
@@ -92,6 +92,12 @@ export const GENERATED_RULES = [
92
92
  enforcementClass: "advisory",
93
93
  category: "integrity",
94
94
  },
95
+ {
96
+ name: "scenario-outcome-mismatch",
97
+ description: "A scenario that expects rejection or error must assume a value or situation a current requirement forbids: when every assumption is governed by current requirements and they permit all of them together, the knowledge base predicts success and the scenario exercises no failure side",
98
+ enforcementClass: "canonical",
99
+ category: "integrity",
100
+ },
95
101
  {
96
102
  name: "exception-claim-keys",
97
103
  description: "An exception requirement's exempts_claims must name claim keys of a requirement it exempts, and requires an exempts link",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-cli",
3
- "version": "2.36.0",
3
+ "version": "2.39.0",
4
4
  "type": "module",
5
5
  "description": "Kibi CLI for knowledge base management",
6
6
  "engines": {
@@ -239,13 +239,14 @@
239
239
  "fast-glob": "^3.2.12",
240
240
  "gray-matter": "^4.0.3",
241
241
  "js-yaml": "^4.1.0",
242
- "kibi-core": "^0.26.1",
242
+ "kibi-core": "^0.28.0",
243
243
  "kibi-plugin-builtin": "^0.5.3",
244
- "kibi-plugin-sdk": "^0.5.0",
244
+ "kibi-plugin-sdk": "^0.5.1",
245
245
  "kibi-swipl": "^1.0.1",
246
246
  "saxes": "^6.0.0",
247
247
  "ignore": "^5.3.0",
248
- "yaml": "^2.9.1"
248
+ "yaml": "^2.9.1",
249
+ "kibi-agent-core": "^0.11.0"
249
250
  },
250
251
  "devDependencies": {
251
252
  "@types/node": "latest",
@@ -47,6 +47,10 @@ entity_property(req, proof_exempt_reason, string).
47
47
  % Only an approved exception makes a success scenario feasible.
48
48
  entity_property(req, approved_by, string).
49
49
  entity_property(req, approval_ref, string).
50
+ % Why a requirement has no failure side to test (its claims state a
51
+ % capability or output shape), and the human who accepted that.
52
+ entity_property(req, failure_side_waiver, string).
53
+ entity_property(req, failure_side_waiver_approved_by, string).
50
54
  % Why the requirement exists, in the words of whoever stated the intent.
51
55
  % Explanation only: it is never part of the checked meaning.
52
56
  entity_property(req, rationale, string).
@@ -83,6 +87,12 @@ entity_property(fact, valid_to, datetime).
83
87
  entity_property(fact, canonical_key, string).
84
88
  entity_property(fact, claim_key, string).
85
89
  entity_property(fact, claim_text, string).
90
+ % Why a quantifier word in claim_text ranges over nothing, so the claim
91
+ % needs no dimension, and the human who accepted that.
92
+ entity_property(fact, dimension_waiver, string).
93
+ entity_property(fact, dimension_waiver_approved_by, string).
94
+ entity_property(fact, case_waiver, string).
95
+ entity_property(fact, case_waiver_approved_by, string).
86
96
  entity_property(fact, predicate_name, string).
87
97
  entity_property(fact, predicate_namespace, string).
88
98
  entity_property(fact, predicate_arity, integer).
@@ -149,6 +159,8 @@ optional_property(req, semantic_source_hash).
149
159
  optional_property(req, semantic_inventory).
150
160
  optional_property(req, proof_exempt).
151
161
  optional_property(req, proof_exempt_reason).
162
+ optional_property(req, failure_side_waiver).
163
+ optional_property(req, failure_side_waiver_approved_by).
152
164
  optional_property(req, approved_by).
153
165
  optional_property(req, approval_ref).
154
166
  optional_property(req, rationale).
@@ -33,12 +33,59 @@ type RelationshipLookup = {
33
33
  };
34
34
 
35
35
  type IncomingLookup = {
36
- readonly manifestResults: readonly ExtractionResult[];
36
+ readonly index: CoverageIndex;
37
37
  readonly targetId: string;
38
38
  readonly relationshipType: string;
39
39
  readonly type: string;
40
40
  };
41
41
 
42
+ /**
43
+ * Built once per report, so each requirement's coverage is a lookup rather
44
+ * than a scan over every manifest result.
45
+ */
46
+ type CoverageIndex = {
47
+ readonly resultsById: ReadonlyMap<string, ExtractionResult>;
48
+ /** Source entities by `${type}\0${relationshipType}\0${targetId}`, in manifest order. */
49
+ readonly incomingByTarget: ReadonlyMap<string, readonly ExtractedEntity[]>;
50
+ };
51
+
52
+ function incomingKey(
53
+ type: string,
54
+ relationshipType: string,
55
+ targetId: string,
56
+ ): string {
57
+ return `${type}\0${relationshipType}\0${targetId}`;
58
+ }
59
+
60
+ function coverageIndex(
61
+ manifestResults: readonly ExtractionResult[],
62
+ ): CoverageIndex {
63
+ const incomingByTarget = new Map<string, ExtractedEntity[]>();
64
+ // A result linking the same target twice is listed twice; every reader
65
+ // dedupes by entity id.
66
+ for (const result of manifestResults) {
67
+ for (const relationship of result.relationships) {
68
+ const key = incomingKey(
69
+ result.entity.type,
70
+ relationship.type,
71
+ relationship.to,
72
+ );
73
+ const list = incomingByTarget.get(key);
74
+ if (list === undefined) incomingByTarget.set(key, [result.entity]);
75
+ else list.push(result.entity);
76
+ }
77
+ }
78
+ return {
79
+ resultsById: new Map(
80
+ manifestResults.map((manifestResult) => [
81
+ manifestResult.entity.id,
82
+ manifestResult,
83
+ ]),
84
+ ),
85
+ incomingByTarget,
86
+ };
87
+ }
88
+
42
89
  function entitiesById(
43
90
  manifestResults: readonly ExtractionResult[],
44
91
  ): ReadonlyMap<string, ExtractedEntity> {
@@ -58,16 +105,11 @@ function outgoingTargets(
58
105
  }
59
106
 
60
107
  function incomingSources(lookup: IncomingLookup): readonly ExtractedEntity[] {
61
- return lookup.manifestResults.flatMap((result) => {
62
- if (result.entity.type !== lookup.type) return [];
63
- return result.relationships.some(
64
- (relationship) =>
65
- relationship.type === lookup.relationshipType &&
66
- relationship.to === lookup.targetId,
67
- )
68
- ? [result.entity]
69
- : [];
70
- });
108
+ return (
109
+ lookup.index.incomingByTarget.get(
110
+ incomingKey(lookup.type, lookup.relationshipType, lookup.targetId),
111
+ ) ?? []
112
+ );
71
113
  }
72
114
 
73
115
  function uniqueEntities(
@@ -213,15 +255,10 @@ function createDiagnostic(
213
255
 
214
256
  function coverageForRequirement(
215
257
  result: ExtractionResult,
216
- manifestResults: readonly ExtractionResult[],
258
+ index: CoverageIndex,
217
259
  entities: ReadonlyMap<string, ExtractedEntity>,
218
260
  ): RequirementCoverage {
219
- const resultsById = new Map(
220
- manifestResults.map((manifestResult) => [
221
- manifestResult.entity.id,
222
- manifestResult,
223
- ]),
224
- );
261
+ const resultsById = index.resultsById;
225
262
  const scenarios = outgoingTargets({
226
263
  result,
227
264
  relationshipType: "specified_by",
@@ -242,7 +279,7 @@ function coverageForRequirement(
242
279
  entities,
243
280
  }),
244
281
  ...incomingSources({
245
- manifestResults,
282
+ index,
246
283
  targetId: result.entity.id,
247
284
  relationshipType: "validates",
248
285
  type: "test",
@@ -253,7 +290,7 @@ function coverageForRequirement(
253
290
  const scenarioResult = resultsById.get(scenario.id);
254
291
  return [
255
292
  ...incomingSources({
256
- manifestResults,
293
+ index,
257
294
  targetId: scenario.id,
258
295
  relationshipType: "validates",
259
296
  type: "test",
@@ -331,6 +368,7 @@ export function createCoverageDepthQualityDiagnostics(
331
368
  ): readonly QualityDiagnostic[] {
332
369
  const entities = entitiesById(manifestResults);
333
370
  const superseded = supersededRequirementIds(manifestResults);
371
+ const index = coverageIndex(manifestResults);
334
372
  return manifestResults
335
373
  .filter(
336
374
  (result) =>
@@ -339,7 +377,7 @@ export function createCoverageDepthQualityDiagnostics(
339
377
  )
340
378
  .flatMap((result) => {
341
379
  const diagnostic = createDiagnostic(
342
- coverageForRequirement(result, manifestResults, entities),
380
+ coverageForRequirement(result, index, entities),
343
381
  proofByRequirement.get(result.entity.id),
344
382
  );
345
383
  return diagnostic === undefined ? [] : [diagnostic];
@@ -47,6 +47,12 @@ type FullKbQualityDiagnosticsOptions = {
47
47
  readonly now?: Date;
48
48
  readonly proofSnapshot?: string;
49
49
  readonly checkedAt?: string;
50
+ /**
51
+ * The per-contract receipt bindings kb_coverage uses (a Prolog
52
+ * TestId -> binding hash dict), or null for strict snapshot binding. Read
53
+ * lazily so a check without proof evidence never pays for it.
54
+ */
55
+ readonly proofTestBindings?: () => Promise<string | null>;
50
56
  };
51
57
 
52
58
  type CoverageEvidencePayload = Readonly<{
@@ -633,6 +639,7 @@ async function loadCoverageProofEvidence(
633
639
  requirementCount: number,
634
640
  proofSnapshot: string | undefined,
635
641
  checkedAt: string | undefined,
642
+ proofTestBindings: (() => Promise<string | null>) | undefined,
636
643
  ): Promise<
637
644
  ReadonlyMap<
638
645
  string,
@@ -653,6 +660,12 @@ async function loadCoverageProofEvidence(
653
660
  }
654
661
  let payload: CoverageEvidencePayload;
655
662
  try {
663
+ // Judge receipts with the binding kb_coverage uses; under strict snapshot
664
+ // binding any unrelated change since kibi prove would read every receipt
665
+ // as stale and bring back the weak-depth review.
666
+ const bindings = (await proofTestBindings?.()) ?? null;
667
+ const binding =
668
+ bindings === null ? "strict_snapshot, _{}" : `per_contract, ${bindings}`;
656
669
  // W1 push-down: the slim evidence projection returns only the fields the
657
670
  // quality diagnostics consume, keeping the response far below the output
658
671
  // capacity limit on large KBs (the full coverage report rows were the
@@ -660,7 +673,7 @@ async function loadCoverageProofEvidence(
660
673
  payload = await runOperationJsonQuery<CoverageEvidencePayload>(
661
674
  prolog as PrologPort,
662
675
  "discovery.pl",
663
- `discovery:coverage_evidence_json(${toPrologList([])}, ${toPrologAtom(proofSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`,
676
+ `discovery:coverage_evidence_json(${toPrologList([])}, ${binding}, ${toPrologAtom(proofSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`,
664
677
  "Quality diagnostic coverage evidence",
665
678
  );
666
679
  } catch {
@@ -776,6 +789,7 @@ export async function collectFullKbQualityDiagnostics(
776
789
  manifestResults.filter((result) => result.entity.type === "req").length,
777
790
  options.proofSnapshot,
778
791
  options.checkedAt,
792
+ options.proofTestBindings,
779
793
  );
780
794
  const coverageDepthDiagnostics = createCoverageDepthQualityDiagnostics(
781
795
  manifestResults,
@@ -70,55 +70,111 @@ function targetHasType(
70
70
  return targetType === undefined || targetType === type;
71
71
  }
72
72
 
73
+ type FanoutIndex = {
74
+ readonly relationshipsByEntityId: ReadonlyMap<
75
+ string,
76
+ readonly ExtractionResult["relationships"][number][]
77
+ >;
78
+ readonly implementerIdsByRequirementId: ReadonlyMap<
79
+ string,
80
+ readonly string[]
81
+ >;
82
+ readonly dependentRequirementIdsByRequirementId: ReadonlyMap<
83
+ string,
84
+ readonly string[]
85
+ >;
86
+ };
87
+
88
+ /** One pass over every relationship, so each requirement's fanout is a lookup. */
89
+ function fanoutIndex(results: readonly ExtractionResult[]): FanoutIndex {
90
+ const relationshipsByEntityId = new Map<
91
+ string,
92
+ ExtractionResult["relationships"][number][]
93
+ >();
94
+ const implementerIdsByRequirementId = new Map<string, string[]>();
95
+ const dependentRequirementIdsByRequirementId = new Map<string, string[]>();
96
+ const append = (
97
+ index: Map<string, string[]>,
98
+ key: string,
99
+ value: string,
100
+ ): void => {
101
+ const list = index.get(key);
102
+ if (list === undefined) index.set(key, [value]);
103
+ else list.push(value);
104
+ };
105
+ for (const result of results) {
106
+ const relationships = relationshipsByEntityId.get(result.entity.id);
107
+ if (relationships === undefined) {
108
+ relationshipsByEntityId.set(result.entity.id, [...result.relationships]);
109
+ } else {
110
+ relationships.push(...result.relationships);
111
+ }
112
+ for (const relationship of result.relationships) {
113
+ if (relationship.type === "implements") {
114
+ append(
115
+ implementerIdsByRequirementId,
116
+ relationship.to,
117
+ result.entity.id,
118
+ );
119
+ }
120
+ if (relationship.type === "depends_on" && result.entity.type === "req") {
121
+ append(
122
+ dependentRequirementIdsByRequirementId,
123
+ relationship.to,
124
+ result.entity.id,
125
+ );
126
+ }
127
+ }
128
+ }
129
+ return {
130
+ relationshipsByEntityId,
131
+ implementerIdsByRequirementId,
132
+ dependentRequirementIdsByRequirementId,
133
+ };
134
+ }
135
+
73
136
  function collectFanout(
74
137
  requirementId: string,
75
- results: readonly ExtractionResult[],
138
+ index: FanoutIndex,
76
139
  typesByEntityId: ReadonlyMap<string, string>,
77
140
  ): RequirementFanout {
78
- const implementingSymbolIds: string[] = [];
79
141
  const scenarioIds: string[] = [];
80
142
  const testIds: string[] = [];
81
143
  const dependentRequirementIds: string[] = [];
82
144
 
83
- for (const result of results) {
84
- for (const relationship of result.relationships) {
85
- if (
86
- relationship.type === "implements" &&
87
- relationship.to === requirementId
88
- ) {
89
- implementingSymbolIds.push(result.entity.id);
90
- }
91
- if (
92
- result.entity.id === requirementId &&
93
- relationship.type === "specified_by" &&
94
- targetHasType(relationship.to, "scenario", typesByEntityId)
95
- ) {
96
- scenarioIds.push(relationship.to);
97
- }
98
- if (
99
- result.entity.id === requirementId &&
100
- (relationship.type === "verified_by" ||
101
- relationship.type === "covered_by") &&
102
- targetHasType(relationship.to, "test", typesByEntityId)
103
- ) {
104
- testIds.push(relationship.to);
105
- }
106
- if (relationship.type === "depends_on") {
107
- if (
108
- result.entity.id === requirementId &&
109
- targetHasType(relationship.to, "req", typesByEntityId)
110
- ) {
111
- dependentRequirementIds.push(relationship.to);
112
- }
113
- if (relationship.to === requirementId && result.entity.type === "req") {
114
- dependentRequirementIds.push(result.entity.id);
115
- }
116
- }
145
+ // rationale: every requirement this is called for is in the index, so the
146
+ // fallback list is never read.
147
+ // Stryker disable next-line ArrayDeclaration
148
+ const relationships = index.relationshipsByEntityId.get(requirementId) ?? [];
149
+ for (const relationship of relationships) {
150
+ if (
151
+ relationship.type === "specified_by" &&
152
+ targetHasType(relationship.to, "scenario", typesByEntityId)
153
+ ) {
154
+ scenarioIds.push(relationship.to);
155
+ }
156
+ if (
157
+ (relationship.type === "verified_by" ||
158
+ relationship.type === "covered_by") &&
159
+ targetHasType(relationship.to, "test", typesByEntityId)
160
+ ) {
161
+ testIds.push(relationship.to);
162
+ }
163
+ if (
164
+ relationship.type === "depends_on" &&
165
+ targetHasType(relationship.to, "req", typesByEntityId)
166
+ ) {
167
+ dependentRequirementIds.push(relationship.to);
117
168
  }
118
169
  }
170
+ dependentRequirementIds.push(
171
+ ...(index.dependentRequirementIdsByRequirementId.get(requirementId) ?? []),
172
+ );
119
173
 
120
174
  return {
121
- implementingSymbolIds: sortedUnique(implementingSymbolIds),
175
+ implementingSymbolIds: sortedUnique(
176
+ index.implementerIdsByRequirementId.get(requirementId) ?? [],
177
+ ),
122
178
  scenarioIds: sortedUnique(scenarioIds),
123
179
  testIds: sortedUnique(testIds),
124
180
  dependentRequirementIds: sortedUnique(dependentRequirementIds),
@@ -145,9 +201,10 @@ function createBroadRequirementDiagnostics(
145
201
  results: readonly ExtractionResult[],
146
202
  ): readonly KibiImpactDiagnostic[] {
147
203
  const typesByEntityId = entityTypeById(results);
204
+ const index = fanoutIndex(results);
148
205
  return results.flatMap((result) => {
149
206
  if (!isCurrentRequirement(result)) return [];
150
- const fanout = collectFanout(result.entity.id, results, typesByEntityId);
207
+ const fanout = collectFanout(result.entity.id, index, typesByEntityId);
151
208
  if (!exceedsBroadThreshold(fanout)) return [];
152
209
  if (isUmbrellaRequirement(result)) return [];
153
210
 
@@ -225,6 +225,35 @@ export function uncompiledBranchStoreViolation(
225
225
  };
226
226
  }
227
227
 
228
+ /**
229
+ * The branch store this check reads, when the engine is known to read it, as
230
+ * the place to keep parsed source analyses. A read-only detached snapshot
231
+ * and an unknown store (an explicit --kb-path) get no cache.
232
+ */
233
+ // implements REQ-cli-check
234
+ export function analysisCacheDir(
235
+ context: OperationContext,
236
+ workspaceRoot: string,
237
+ options: CheckExecutionOptions,
238
+ ): string | undefined {
239
+ if (
240
+ context.branchAttachment === undefined &&
241
+ options.branchStoreAttached !== true
242
+ ) {
243
+ return undefined;
244
+ }
245
+ try {
246
+ const attachment =
247
+ context.branchAttachment ?? resolveBranchAttachment(workspaceRoot);
248
+ if ("storePath" in attachment && attachment.readOnly === undefined) {
249
+ return attachment.storePath;
250
+ }
251
+ } catch {
252
+ // A branch that cannot be resolved gets no cache.
253
+ }
254
+ return undefined;
255
+ }
256
+
228
257
  // implements REQ-kibi-operation-interface-parity, REQ-002
229
258
  export async function executeCheck(
230
259
  args: CheckInput,
@@ -239,6 +268,10 @@ export async function executeCheck(
239
268
  ? snapshotEvidence.snapshot.hash
240
269
  : undefined;
241
270
  const checkedAt = context.clock().toISOString();
271
+ const proofTestBindings = async () => {
272
+ const { perContractTestBindings } = await import("./specs/reporting.js");
273
+ return perContractTestBindings({ ...context, prolog });
274
+ };
242
275
  // Enforcement policy is Kibi-owned and deterministic from the installed
243
276
  // version. `args.rules` is an invocation-time diagnostic selector only.
244
277
  const rulesAllowlist = resolveCheckRules(args);
@@ -316,6 +349,7 @@ export async function executeCheck(
316
349
  workspaceRoot,
317
350
  ...(proofSnapshot !== undefined ? { proofSnapshot } : {}),
318
351
  checkedAt,
352
+ proofTestBindings,
319
353
  now: context.clock(),
320
354
  ...maxDiagnosticsOption,
321
355
  });
@@ -378,6 +412,7 @@ export async function executeCheck(
378
412
  prolog,
379
413
  workspaceRoot,
380
414
  rulesAllowlist,
415
+ analysisCacheDir(context, workspaceRoot, options),
381
416
  );
382
417
  const originReviewFindings = await collectOriginReviewViolations(
383
418
  prolog,
@@ -400,7 +435,11 @@ export async function executeCheck(
400
435
  const evidenceFloorFindings = await collectEvidenceFloorViolations(
401
436
  context,
402
437
  rulesAllowlist,
403
- { freshEvidence: args.freshEvidence === true },
438
+ {
439
+ freshEvidence: args.freshEvidence === true,
440
+ checkedAt,
441
+ snapshotEvidence,
442
+ },
404
443
  );
405
444
  const proofContractClaimFindings =
406
445
  await collectProofContractClaimViolations(context, rulesAllowlist);
@@ -442,6 +481,7 @@ export async function executeCheck(
442
481
  workspaceRoot,
443
482
  ...(proofSnapshot !== undefined ? { proofSnapshot } : {}),
444
483
  checkedAt,
484
+ proofTestBindings,
445
485
  now: context.clock(),
446
486
  ...maxDiagnosticsOption,
447
487
  });