kibi-cli 2.1.0 → 2.2.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 (246) hide show
  1. package/dist/cli-protocol.d.ts.map +1 -1
  2. package/dist/cli-protocol.js +3 -2
  3. package/dist/commands/aggregated-checks.d.ts +7 -0
  4. package/dist/commands/aggregated-checks.d.ts.map +1 -1
  5. package/dist/commands/aggregated-checks.js +25 -1
  6. package/dist/commands/check.d.ts.map +1 -1
  7. package/dist/commands/check.js +21 -13
  8. package/dist/commands/doctor.d.ts.map +1 -1
  9. package/dist/commands/doctor.js +105 -24
  10. package/dist/commands/github-init.d.ts +1 -1
  11. package/dist/commands/github-init.d.ts.map +1 -1
  12. package/dist/commands/github-init.js +1 -1
  13. package/dist/commands/init-helpers.d.ts +17 -2
  14. package/dist/commands/init-helpers.d.ts.map +1 -1
  15. package/dist/commands/init-helpers.js +123 -23
  16. package/dist/commands/init.d.ts.map +1 -1
  17. package/dist/commands/init.js +110 -15
  18. package/dist/commands/prove.d.ts.map +1 -1
  19. package/dist/commands/prove.js +10 -18
  20. package/dist/commands/sync/cache.d.ts +20 -1
  21. package/dist/commands/sync/cache.d.ts.map +1 -1
  22. package/dist/commands/sync/cache.js +51 -2
  23. package/dist/commands/sync/discovery.d.ts.map +1 -1
  24. package/dist/commands/sync/discovery.js +1 -1
  25. package/dist/commands/sync/persistence.d.ts.map +1 -1
  26. package/dist/commands/sync/persistence.js +8 -0
  27. package/dist/commands/sync.d.ts.map +1 -1
  28. package/dist/commands/sync.js +11 -3
  29. package/dist/engine.d.ts +26 -0
  30. package/dist/engine.d.ts.map +1 -1
  31. package/dist/engine.js +126 -29
  32. package/dist/extractors/manifest.d.ts.map +1 -1
  33. package/dist/extractors/manifest.js +66 -28
  34. package/dist/extractors/markdown.d.ts +2 -0
  35. package/dist/extractors/markdown.d.ts.map +1 -1
  36. package/dist/extractors/markdown.js +14 -3
  37. package/dist/extractors/relationships.d.ts.map +1 -1
  38. package/dist/extractors/relationships.js +1 -0
  39. package/dist/intent-search.d.ts.map +1 -1
  40. package/dist/intent-search.js +68 -29
  41. package/dist/operations/bootstrap/generate.d.ts.map +1 -1
  42. package/dist/operations/bootstrap/generate.js +4 -3
  43. package/dist/operations/modeling/model-requirement.d.ts +1 -1
  44. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  45. package/dist/operations/modeling/model-requirement.js +176 -10
  46. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  47. package/dist/operations/modeling/predicate-applyplan.js +14 -2
  48. package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
  49. package/dist/operations/modeling/predicate-loader.js +24 -0
  50. package/dist/operations/modeling/predicate-types.d.ts +4 -0
  51. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  52. package/dist/operations/modeling/predicate-vocabulary.d.ts +54 -0
  53. package/dist/operations/modeling/predicate-vocabulary.d.ts.map +1 -0
  54. package/dist/operations/modeling/predicate-vocabulary.js +169 -0
  55. package/dist/operations/modeling/requirement-types.d.ts +5 -0
  56. package/dist/operations/modeling/requirement-types.d.ts.map +1 -1
  57. package/dist/operations/modeling/vocabulary-alignment.d.ts +60 -0
  58. package/dist/operations/modeling/vocabulary-alignment.d.ts.map +1 -0
  59. package/dist/operations/modeling/vocabulary-alignment.js +178 -0
  60. package/dist/operations/mutation/contradictions.d.ts +3 -0
  61. package/dist/operations/mutation/contradictions.d.ts.map +1 -1
  62. package/dist/operations/mutation/contradictions.js +29 -0
  63. package/dist/operations/mutation/delete.d.ts.map +1 -1
  64. package/dist/operations/mutation/delete.js +33 -1
  65. package/dist/operations/mutation/predicate-vocabulary-guard.d.ts +8 -0
  66. package/dist/operations/mutation/predicate-vocabulary-guard.d.ts.map +1 -0
  67. package/dist/operations/mutation/predicate-vocabulary-guard.js +64 -0
  68. package/dist/operations/mutation/relationships.d.ts +1 -1
  69. package/dist/operations/mutation/relationships.d.ts.map +1 -1
  70. package/dist/operations/mutation/relationships.js +1 -0
  71. package/dist/operations/mutation/saga.d.ts +5 -0
  72. package/dist/operations/mutation/saga.d.ts.map +1 -1
  73. package/dist/operations/mutation/saga.js +10 -0
  74. package/dist/operations/mutation/serialization.d.ts.map +1 -1
  75. package/dist/operations/mutation/serialization.js +2 -0
  76. package/dist/operations/mutation/source-authoring.d.ts +6 -0
  77. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  78. package/dist/operations/mutation/source-authoring.js +65 -26
  79. package/dist/operations/mutation/strict-fact.d.ts.map +1 -1
  80. package/dist/operations/mutation/strict-fact.js +5 -0
  81. package/dist/operations/mutation/types.d.ts +17 -0
  82. package/dist/operations/mutation/types.d.ts.map +1 -1
  83. package/dist/operations/mutation/upsert.d.ts +6 -0
  84. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  85. package/dist/operations/mutation/upsert.js +72 -4
  86. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  87. package/dist/operations/mutation/validate-upsert.js +24 -1
  88. package/dist/operations/mutation/validation.d.ts.map +1 -1
  89. package/dist/operations/mutation/validation.js +16 -3
  90. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  91. package/dist/operations/planning/apply-plan.js +61 -17
  92. package/dist/operations/planning/compile-intent.d.ts.map +1 -1
  93. package/dist/operations/planning/compile-intent.js +3 -1
  94. package/dist/operations/proof/code-scope.d.ts +17 -0
  95. package/dist/operations/proof/code-scope.d.ts.map +1 -0
  96. package/dist/operations/proof/code-scope.js +60 -0
  97. package/dist/operations/proof/ingest-proof.d.ts +4 -2
  98. package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
  99. package/dist/operations/proof/ingest-proof.js +207 -74
  100. package/dist/operations/proof/migrate-legacy-receipts.d.ts.map +1 -1
  101. package/dist/operations/proof/migrate-legacy-receipts.js +7 -5
  102. package/dist/operations/proof/prune-receipts.d.ts.map +1 -1
  103. package/dist/operations/proof/prune-receipts.js +7 -5
  104. package/dist/plugins/compose-semantic-classifier.d.ts +6 -1
  105. package/dist/plugins/compose-semantic-classifier.d.ts.map +1 -1
  106. package/dist/plugins/compose-semantic-classifier.js +8 -3
  107. package/dist/plugins/compose-vocabulary-alignment.d.ts +23 -0
  108. package/dist/plugins/compose-vocabulary-alignment.d.ts.map +1 -0
  109. package/dist/plugins/compose-vocabulary-alignment.js +141 -0
  110. package/dist/plugins/external-allowlist.d.ts +7 -0
  111. package/dist/plugins/external-allowlist.d.ts.map +1 -1
  112. package/dist/plugins/external-allowlist.js +13 -0
  113. package/dist/plugins/index.d.ts +2 -0
  114. package/dist/plugins/index.d.ts.map +1 -1
  115. package/dist/plugins/index.js +4 -0
  116. package/dist/plugins/registry.d.ts +2 -1
  117. package/dist/plugins/registry.d.ts.map +1 -1
  118. package/dist/plugins/registry.js +22 -1
  119. package/dist/prolog/codec.d.ts.map +1 -1
  120. package/dist/prolog/codec.js +2 -0
  121. package/dist/prolog.d.ts +36 -0
  122. package/dist/prolog.d.ts.map +1 -1
  123. package/dist/prolog.js +78 -9
  124. package/dist/public/diagnostic-usage.d.ts.map +1 -1
  125. package/dist/public/diagnostic-usage.js +7 -25
  126. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  127. package/dist/public/impact/full-kb-quality.js +4 -12
  128. package/dist/public/operations/catalog.d.ts +36 -7
  129. package/dist/public/operations/catalog.d.ts.map +1 -1
  130. package/dist/public/operations/check-executor.d.ts.map +1 -1
  131. package/dist/public/operations/check-executor.js +10 -2
  132. package/dist/public/operations/check-helpers.d.ts +6 -0
  133. package/dist/public/operations/check-helpers.d.ts.map +1 -1
  134. package/dist/public/operations/check-helpers.js +21 -1
  135. package/dist/public/operations/contracts.d.ts.map +1 -1
  136. package/dist/public/operations/contracts.js +16 -0
  137. package/dist/public/operations/discovery-entities.d.ts +42 -2
  138. package/dist/public/operations/discovery-entities.d.ts.map +1 -1
  139. package/dist/public/operations/discovery-entities.js +195 -22
  140. package/dist/public/operations/discovery-executors.d.ts +1 -0
  141. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  142. package/dist/public/operations/discovery-executors.js +75 -46
  143. package/dist/public/operations/migration-plan.d.ts +2 -0
  144. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  145. package/dist/public/operations/migration-plan.js +50 -0
  146. package/dist/public/operations/predicate-schema-conformance.d.ts +41 -0
  147. package/dist/public/operations/predicate-schema-conformance.d.ts.map +1 -0
  148. package/dist/public/operations/predicate-schema-conformance.js +242 -0
  149. package/dist/public/operations/prolog-json.d.ts.map +1 -1
  150. package/dist/public/operations/prolog-json.js +9 -3
  151. package/dist/public/operations/result-envelope.d.ts +34 -1
  152. package/dist/public/operations/result-envelope.d.ts.map +1 -1
  153. package/dist/public/operations/result-envelope.js +78 -2
  154. package/dist/public/operations/runtime-types.d.ts +1 -1
  155. package/dist/public/operations/runtime-types.d.ts.map +1 -1
  156. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  157. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  158. package/dist/public/operations/specs/check-rules.generated.js +8 -1
  159. package/dist/public/operations/specs/check.d.ts +1 -1
  160. package/dist/public/operations/specs/discovery.d.ts +7 -1
  161. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  162. package/dist/public/operations/specs/discovery.js +7 -1
  163. package/dist/public/operations/specs/mutation-schemas.d.ts +27 -4
  164. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  165. package/dist/public/operations/specs/mutation-schemas.js +21 -3
  166. package/dist/public/operations/specs/reporting.d.ts +8 -0
  167. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  168. package/dist/public/operations/specs/reporting.js +46 -15
  169. package/dist/public/operations/specs/upsert.d.ts +28 -5
  170. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  171. package/dist/public/operations/specs/upsert.js +1 -1
  172. package/dist/public/operations/symbol-repair-plan.d.ts.map +1 -1
  173. package/dist/public/operations/symbol-repair-plan.js +8 -3
  174. package/dist/public/schemas/entity.d.ts.map +1 -1
  175. package/dist/public/schemas/entity.js +22 -0
  176. package/dist/public/schemas/relationship.d.ts.map +1 -1
  177. package/dist/public/schemas/relationship.js +1 -0
  178. package/dist/public/skills/kibi-usage/SKILL.md +7 -3
  179. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +4 -0
  180. package/dist/public/skills/kibi-usage/resources/relationship-directions.md +35 -17
  181. package/dist/public/skills/kibi-usage/resources/source-authoring.md +36 -0
  182. package/dist/public/skills/kibi-usage/resources/workflows.md +52 -1
  183. package/dist/public/telemetry-acceptance.d.ts.map +1 -1
  184. package/dist/public/telemetry-acceptance.js +21 -5
  185. package/dist/relationships/shards.d.ts +6 -0
  186. package/dist/relationships/shards.d.ts.map +1 -1
  187. package/dist/relationships/shards.js +26 -19
  188. package/dist/schemas/entity.schema.json +32 -0
  189. package/dist/traceability/staged-diagnostics.d.ts +1 -1
  190. package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
  191. package/dist/traceability/staged-entity-id-style.d.ts +12 -0
  192. package/dist/traceability/staged-entity-id-style.d.ts.map +1 -0
  193. package/dist/traceability/staged-entity-id-style.js +68 -0
  194. package/dist/traceability/temp-kb.d.ts.map +1 -1
  195. package/dist/traceability/temp-kb.js +8 -0
  196. package/dist/traceability/validate.js +1 -1
  197. package/dist/types/entities.d.ts +2 -0
  198. package/dist/types/entities.d.ts.map +1 -1
  199. package/dist/types/relationships.d.ts +1 -1
  200. package/dist/types/relationships.d.ts.map +1 -1
  201. package/dist/utils/entity-id-style.d.ts +27 -0
  202. package/dist/utils/entity-id-style.d.ts.map +1 -0
  203. package/dist/utils/entity-id-style.js +81 -0
  204. package/dist/utils/git-repository-context.d.ts +50 -0
  205. package/dist/utils/git-repository-context.d.ts.map +1 -0
  206. package/dist/utils/git-repository-context.js +259 -0
  207. package/dist/utils/rule-registry.d.ts +5 -0
  208. package/dist/utils/rule-registry.d.ts.map +1 -1
  209. package/dist/utils/rule-registry.generated.d.ts +37 -0
  210. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  211. package/dist/utils/rule-registry.generated.js +44 -0
  212. package/dist/utils/strict-modeling.d.ts +7 -0
  213. package/dist/utils/strict-modeling.d.ts.map +1 -1
  214. package/dist/utils/strict-modeling.js +11 -0
  215. package/package.json +4 -4
  216. package/schema/entities.pl +60 -1
  217. package/schema/relationships.pl +17 -4
  218. package/schema/validation.pl +118 -20
  219. package/src/public/diagnostic-usage.ts +16 -24
  220. package/src/public/impact/full-kb-quality.ts +4 -16
  221. package/src/public/operations/check-executor.ts +12 -1
  222. package/src/public/operations/check-helpers.ts +27 -1
  223. package/src/public/operations/contracts.ts +19 -0
  224. package/src/public/operations/discovery-entities.ts +292 -23
  225. package/src/public/operations/discovery-executors.ts +91 -55
  226. package/src/public/operations/migration-plan.ts +57 -0
  227. package/src/public/operations/predicate-schema-conformance.ts +362 -0
  228. package/src/public/operations/prolog-json.ts +9 -3
  229. package/src/public/operations/result-envelope.ts +114 -2
  230. package/src/public/operations/runtime-types.ts +1 -1
  231. package/src/public/operations/specs/check-rules.generated.ts +8 -1
  232. package/src/public/operations/specs/discovery.ts +8 -1
  233. package/src/public/operations/specs/mutation-schemas.ts +23 -3
  234. package/src/public/operations/specs/reporting.ts +69 -22
  235. package/src/public/operations/specs/upsert.ts +1 -1
  236. package/src/public/operations/symbol-repair-plan.ts +13 -3
  237. package/src/public/schemas/entity.ts +22 -0
  238. package/src/public/schemas/relationship.ts +1 -0
  239. package/src/public/skills/kibi-usage/SKILL.md +7 -3
  240. package/src/public/skills/kibi-usage/resources/fact-lanes.md +4 -0
  241. package/src/public/skills/kibi-usage/resources/relationship-directions.md +35 -17
  242. package/src/public/skills/kibi-usage/resources/source-authoring.md +36 -0
  243. package/src/public/skills/kibi-usage/resources/workflows.md +52 -1
  244. package/src/public/telemetry-acceptance.ts +37 -6
  245. package/src/schemas/entity.schema.json +32 -0
  246. package/src/schemas/relationship.schema.json +1 -0
@@ -209,12 +209,26 @@ export async function perContractTestBindings(
209
209
  const { removeFrontmatterBlock } = await import(
210
210
  "../../../operations/proof/receipt-document.js"
211
211
  );
212
+ const { loadCoveredBySymbolsByTest, receiptCodeScopeSymbolIds } =
213
+ await import("../../../operations/proof/code-scope.js");
214
+ // Only the contract, scoped code, and authored source feed the binding
215
+ // hash. The paged projection never materializes receipt histories, which
216
+ // grow without bound and previously overflowed the bounded Prolog output
217
+ // on real stores. Engine failures must surface: silently dropping to strict
218
+ // snapshot semantics would hide an unhealthy engine behind stale receipts.
212
219
  let tests: Record<string, unknown>[];
213
220
  try {
214
- tests = await loadEntities(context.prolog as never, { type: "test" });
215
- } catch {
216
- return null;
221
+ tests = await loadEntities(requireProlog(context), {
222
+ type: "test",
223
+ projection: "proof_contract",
224
+ });
225
+ } catch (error) {
226
+ const message = error instanceof Error ? error.message : String(error);
227
+ throw new Error(`Per-contract receipt binding query failed: ${message}`);
217
228
  }
229
+ const coveredBy = tests.length
230
+ ? await loadCoveredBySymbolsByTest(requireProlog(context))
231
+ : new Map<string, readonly string[]>();
218
232
  const entries: string[] = [];
219
233
  const manifestPath = join(context.workspaceRoot, ".kb", "symbols.yaml");
220
234
  for (const test of tests) {
@@ -233,17 +247,14 @@ export async function perContractTestBindings(
233
247
  const absolute = join(context.workspaceRoot, source);
234
248
  const authored = await context.fs.readFile(absolute);
235
249
  const stripped = removeFrontmatterBlock(authored, "proof_receipts");
236
- const rawBindings: ReadonlyArray<{ symbol_id?: unknown }> = Array.isArray(
237
- test.proof_bindings,
238
- )
239
- ? (test.proof_bindings as ReadonlyArray<{ symbol_id?: unknown }>)
240
- : [];
241
- const boundIds = rawBindings
242
- .map((binding) =>
243
- typeof binding.symbol_id === "string" ? binding.symbol_id : "",
244
- )
245
- .filter((id) => id !== "");
246
- const codeScope = resolveBoundSymbolScope(manifestPath, boundIds);
250
+ const codeScope = resolveBoundSymbolScope(
251
+ manifestPath,
252
+ receiptCodeScopeSymbolIds(
253
+ contract,
254
+ test.proof_bindings,
255
+ coveredBy.get(testId) ?? [],
256
+ ),
257
+ );
247
258
  const binding = receiptBindingHash(
248
259
  contract as never,
249
260
  stripped ?? authored,
@@ -258,6 +269,42 @@ export async function perContractTestBindings(
258
269
  return `_{${entries.join(", ")}}`;
259
270
  }
260
271
 
272
+ /**
273
+ * Coverage rows can be large (full proof stages per requirement), so a
274
+ * whole-KB report in one answer approaches the engine's bounded output cap.
275
+ * Fetch it in small row pages instead; the engine memoizes the computed
276
+ * report per store generation, so later pages only paginate and encode.
277
+ */
278
+ export const COVERAGE_ROW_PAGE_SIZE = 10;
279
+
280
+ // implements REQ-kibi-verification-evidence-contract
281
+ export async function readCoveragePages(
282
+ prolog: NonNullable<OperationContext["prolog"]>,
283
+ goalFor: (limit: number, offset: number) => string,
284
+ limit: number,
285
+ offset: number,
286
+ ): Promise<CoveragePayload> {
287
+ const read = (pageLimit: number, pageOffset: number) =>
288
+ runOperationJsonQuery<CoveragePayload>(
289
+ prolog,
290
+ "discovery.pl",
291
+ goalFor(pageLimit, pageOffset),
292
+ "Coverage execution",
293
+ );
294
+ if (limit <= COVERAGE_ROW_PAGE_SIZE) return read(limit, offset);
295
+ const first = await read(COVERAGE_ROW_PAGE_SIZE, offset);
296
+ const rows = [...first.rows];
297
+ let lastPageSize = first.rows.length;
298
+ let lastPageLimit = COVERAGE_ROW_PAGE_SIZE;
299
+ while (lastPageSize === lastPageLimit && rows.length < limit) {
300
+ lastPageLimit = Math.min(COVERAGE_ROW_PAGE_SIZE, limit - rows.length);
301
+ const page = await read(lastPageLimit, offset + rows.length);
302
+ rows.push(...page.rows);
303
+ lastPageSize = page.rows.length;
304
+ }
305
+ return { ...first, rows };
306
+ }
307
+
261
308
  export async function executeCoverage(
262
309
  input: CoverageInput,
263
310
  context: OperationContext,
@@ -275,17 +322,17 @@ export async function executeCoverage(
275
322
  statuses.length === 0 && (input.by ?? "req") === "req"
276
323
  ? await perContractTestBindings(context)
277
324
  : null;
278
- const goal =
325
+ const goalFor = (limit: number, offset: number): string =>
279
326
  bindingsDict !== null
280
- ? `discovery:coverage_report_json('${input.by ?? "req"}', ${toPrologList(input.tags)}, ${input.includePassing ?? false}, per_contract, ${bindingsDict}, ${input.includeTransitive ?? true}, ${input.limit ?? 100}, ${input.offset ?? 0}, ${toPrologAtom(codeSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`
327
+ ? `discovery:coverage_report_json('${input.by ?? "req"}', ${toPrologList(input.tags)}, ${input.includePassing ?? false}, per_contract, ${bindingsDict}, ${input.includeTransitive ?? true}, ${limit}, ${offset}, ${toPrologAtom(codeSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`
281
328
  : statuses.length > 0
282
- ? `discovery:coverage_report_json('${input.by ?? "req"}', ${toPrologList(input.tags)}, ${input.includePassing ?? false}, ${toPrologList(statuses)}, ${input.includeTransitive ?? true}, ${input.limit ?? 100}, ${input.offset ?? 0}, ${toPrologAtom(codeSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`
283
- : `discovery:coverage_report_json('${input.by ?? "req"}', ${toPrologList(input.tags)}, ${input.includePassing ?? false}, ${input.includeTransitive ?? true}, ${input.limit ?? 100}, ${input.offset ?? 0}, ${toPrologAtom(codeSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`;
284
- const payload = await runOperationJsonQuery<CoveragePayload>(
329
+ ? `discovery:coverage_report_json('${input.by ?? "req"}', ${toPrologList(input.tags)}, ${input.includePassing ?? false}, ${toPrologList(statuses)}, ${input.includeTransitive ?? true}, ${limit}, ${offset}, ${toPrologAtom(codeSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`
330
+ : `discovery:coverage_report_json('${input.by ?? "req"}', ${toPrologList(input.tags)}, ${input.includePassing ?? false}, ${input.includeTransitive ?? true}, ${limit}, ${offset}, ${toPrologAtom(codeSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`;
331
+ const payload = await readCoveragePages(
285
332
  requireProlog(context),
286
- "discovery.pl",
287
- goal,
288
- "Coverage execution",
333
+ goalFor,
334
+ input.limit ?? 100,
335
+ input.offset ?? 0,
289
336
  );
290
337
  const rows =
291
338
  (input.by ?? "req") === "req"
@@ -28,7 +28,7 @@ export const upsertSpec = {
28
28
  id: {
29
29
  type: "string",
30
30
  description:
31
- "Unique entity ID (string). Example: 'REQ-123'. Existing ID updates the entity; new ID creates it.",
31
+ "Unique entity ID (string). Example: 'REQ-cli-gc'. Name entities by the behavior they govern (<TYPE>-<area>-<behavior>), never by the next free number. Existing ID updates the entity; new ID creates it.",
32
32
  },
33
33
  properties: ENTITY_PROPERTIES_SCHEMA,
34
34
  relationships: RELATIONSHIPS_SCHEMA,
@@ -7,7 +7,13 @@ import {
7
7
  enrichSymbolCoordinates,
8
8
  } from "../../extractors/symbols-coordinator.js";
9
9
  import { isCoarseGranularityReason } from "../symbol-granularity.js";
10
- import { loadEntities } from "./discovery-entities.js";
10
+ import { loadEntitiesPaged } from "./discovery-entities.js";
11
+
12
+ /**
13
+ * Symbol pages for repair planning. The full symbol inventory is already half
14
+ * of the bounded engine output on large workspaces, so it is read in pages.
15
+ */
16
+ const SYMBOL_PAGE_SIZE = 250;
11
17
  import type { OperationContext } from "./runtime-types.js";
12
18
 
13
19
  export const SYMBOL_REPAIR_PLAN_VERSION = "kibi.symbol-repair-plan.v1" as const;
@@ -131,7 +137,7 @@ export async function addCoordinateRepairEvidence(
131
137
  const ids = new Set(missingByRow.flat());
132
138
  if (ids.size === 0 || !context.prolog) return rows;
133
139
  const symbols = (
134
- await loadEntities(context.prolog, { type: "symbol" })
140
+ await loadEntitiesPaged(context.prolog, "symbol", SYMBOL_PAGE_SIZE)
135
141
  ).filter((symbol) => ids.has(String(symbol.id)));
136
142
  const evidence = await inspectCoordinateRepairs(
137
143
  symbols,
@@ -227,7 +233,11 @@ export async function buildSymbolRepairPlan(
227
233
  context: OperationContext,
228
234
  ): Promise<SymbolRepairPlan | undefined> {
229
235
  if (!context.prolog || rows.length === 0) return undefined;
230
- const symbols = await loadEntities(context.prolog, { type: "symbol" });
236
+ const symbols = await loadEntitiesPaged(
237
+ context.prolog,
238
+ "symbol",
239
+ SYMBOL_PAGE_SIZE,
240
+ );
231
241
  const extractedByPath = new Map<
232
242
  string,
233
243
  readonly { name: string; kind: string }[]
@@ -531,6 +531,26 @@ const entitySchema: Record<string, unknown> = {
531
531
  aliases: { type: "array", items: { type: "string" } },
532
532
  examples: { type: "array", items: { type: "string" } },
533
533
  predicate_args: { type: "array", items: { type: "string" } },
534
+ // predicate_schema only: allowed constants per argument name. Arguments
535
+ // without an entry stay open; predicate facts must use a listed constant.
536
+ argument_constants: {
537
+ type: "object",
538
+ additionalProperties: {
539
+ type: "array",
540
+ minItems: 1,
541
+ uniqueItems: true,
542
+ items: { type: "string", minLength: 1 },
543
+ },
544
+ },
545
+ // predicate_schema only: legacy spellings per argument name, each mapped
546
+ // to one declared constant. kibi migrate rewrites facts that use them.
547
+ argument_aliases: {
548
+ type: "object",
549
+ additionalProperties: {
550
+ type: "object",
551
+ additionalProperties: { type: "string", minLength: 1 },
552
+ },
553
+ },
534
554
  rule_ir: { $ref: "#/$defs/logicRule" },
535
555
  rule_hash: { type: "string", pattern: "^[a-f0-9]{64}$" },
536
556
  rule_schema_id: { type: "string", minLength: 1 },
@@ -594,6 +614,8 @@ const entitySchema: Record<string, unknown> = {
594
614
  { required: ["aliases"] },
595
615
  { required: ["examples"] },
596
616
  { required: ["predicate_args"] },
617
+ { required: ["argument_constants"] },
618
+ { required: ["argument_aliases"] },
597
619
  { required: ["rule_ir"] },
598
620
  { required: ["rule_hash"] },
599
621
  { required: ["rule_schema_id"] },
@@ -40,6 +40,7 @@ const relationshipSchema = {
40
40
  "publishes",
41
41
  "consumes",
42
42
  "supersedes",
43
+ "restates",
43
44
  "relates_to",
44
45
  ],
45
46
  },
@@ -2,7 +2,7 @@
2
2
  id: kibi-usage
3
3
  name: Kibi Usage
4
4
  description: Use Kibi's source-first, exact-Git, migration-aware, proof-aware operations safely across MCP or the trusted local CLI, including partial completion repair.
5
- version: 2.1.3
5
+ version: 2.2.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -59,7 +59,11 @@ mutability, and Prolog requirements of each operation.
59
59
  ## Safe workflow
60
60
 
61
61
  1. Always discover before you mutate: start with `kb_search`, then exact-filter with `kb_query`. Use
62
- `kb_status` when branch or freshness confidence matters.
62
+ `kb_status` when branch or freshness confidence matters. `kb_search` ranks
63
+ lexically unless you pass `rankingMode: "intent-v1"`, `semanticFacets`, or
64
+ `sourceLocations`; use intent mode with grounded facets for conceptual
65
+ questions and before changing a source file, and keep lexical search for
66
+ literal identifiers. See `resources/workflows.md` for the mode recipe.
63
67
  2. Resolve genuine ambiguity with the human; use semantic advisor/modeling
64
68
  operations for deterministic interpretation and typed facts.
65
69
  3. Create endpoints before relationships and upsert small batches sequentially.
@@ -160,7 +164,7 @@ Represent implementation ownership with a `symbol` entity that has
160
164
  `sourceFile`, `implements` to the requirement, and `covered_by` to the test.
161
165
  Prolog `symbol-coverage` also requires the test to `validates` the requirement
162
166
  (or the requirement `verified_by` the test). `covered_by` alone is not
163
- enough. Do not rely on legacy `// implements REQ-xxx` comments as the
167
+ enough. Do not rely on legacy `// implements REQ-<area>-<behavior>` comments as the
164
168
  traceability record.
165
169
 
166
170
  ## Complete Logical Coverage
@@ -55,6 +55,10 @@ relationship: { type: requires_predicate, from: REQ-EDITOR-DRAFT-AUTOSAVE, to: F
55
55
 
56
56
  The project-local schema endpoint must exist before this fact is linked. When ontology extension is explicitly authorized, define it first with `fact_kind: predicate_schema`, `predicate_name`, `predicate_arity`, and equally sized `argument_names` and `argument_types`. Without an authorized stable signature, record `review:ontology-gap` instead of inventing a schema.
57
57
 
58
+ ### Closed argument vocabularies
59
+
60
+ A schema can close an argument with `argument_constants` (allowed values keyed by argument name) and map older spellings with `argument_aliases`. Reuse a declared constant; `kb_upsert` rejects any other value and names the constant to use for an alias. When a genuinely new value is needed, extend `argument_constants` on the schema first. To converge existing facts, declare aliases for their spellings and let `kibi migrate` rewrite them (`predicate_schema_alignment` actions). Close only arguments with a natural shared vocabulary, such as the governed component or a severity; forcing distinct obligations into a few constants makes different requirements share one ground term.
61
+
58
62
  ### Deny predicate
59
63
 
60
64
  Claim: “Suspended users must not publish articles.” When `permission_rule` fits, preserve its positive schema name and encode prohibition as polarity:
@@ -12,6 +12,7 @@
12
12
  | `constrains` | req -> fact(subject) | Requirement constrains a strict-lane domain fact |
13
13
  | `requires_property` | req -> fact(property_value) | Requirement requires a specific property value fact |
14
14
  | `supersedes` | new-req -> old-req | New requirement formally replaces an old requirement |
15
+ | `restates` | req -> req | Requirement intentionally restates another current requirement (for example a product requirement echoed in a platform requirement); suppresses `domain-redundancy` for that pair without retiring either side |
15
16
  | `covered_by` | symbol -> test | Production symbol has test coverage evidence |
16
17
 
17
18
  ## Valid Payload Examples
@@ -20,32 +21,32 @@
20
21
  ```yaml
21
22
  relationships:
22
23
  - type: implements
23
- from: SYM-001
24
- to: REQ-001
24
+ from: SYM-operation-execute-check
25
+ to: REQ-cli-check
25
26
  ```
26
27
 
27
28
  ### specified_by
28
29
  ```yaml
29
30
  relationships:
30
31
  - type: specified_by
31
- from: REQ-001
32
- to: SCEN-001
32
+ from: REQ-mcp-search-discovery
33
+ to: SCEN-mcp-search-discovery
33
34
  ```
34
35
 
35
36
  ### verified_by
36
37
  ```yaml
37
38
  relationships:
38
39
  - type: verified_by
39
- from: REQ-001
40
- to: TEST-001
40
+ from: REQ-mcp-search-discovery
41
+ to: TEST-mcp-search-discovery
41
42
  ```
42
43
 
43
44
  ### validates
44
45
  ```yaml
45
46
  relationships:
46
47
  - type: validates
47
- from: TEST-001
48
- to: SCEN-001
48
+ from: TEST-mcp-search-discovery
49
+ to: SCEN-mcp-search-discovery
49
50
  ```
50
51
 
51
52
  ### executable_for
@@ -53,39 +54,56 @@ relationships:
53
54
  relationships:
54
55
  - type: executable_for
55
56
  from: SYM-test-login
56
- to: TEST-001
57
+ to: TEST-mcp-search-discovery
57
58
  ```
58
59
 
59
60
  ### constrains
60
61
  ```yaml
61
62
  relationships:
62
63
  - type: constrains
63
- from: REQ-019
64
- to: FACT-USER-ROLE
64
+ from: REQ-cli-check
65
+ to: FACT-SUBJECT-KIBI-CHECK
65
66
  ```
66
67
 
67
68
  ### requires_property
68
69
  ```yaml
69
70
  relationships:
70
71
  - type: requires_property
71
- from: REQ-019
72
- to: FACT-LIMIT-3
72
+ from: REQ-cli-check
73
+ to: FACT-PROP-CHECK-FAILURE-EXIT-CODE
73
74
  ```
74
75
 
75
76
  ### supersedes
76
77
  ```yaml
77
78
  relationships:
78
79
  - type: supersedes
79
- from: REQ-001-v2
80
- to: REQ-001
80
+ from: REQ-opencode-kibi-briefing-v2
81
+ to: REQ-opencode-kibi-briefing-v1
81
82
  ```
82
83
 
84
+ A `-v2` suffix is acceptable only for a direct superseding replacement like
85
+ this one; otherwise name the new requirement by the behavior it governs.
86
+
87
+ ### restates
88
+ ```yaml
89
+ # Illustrative: a product requirement echoes a platform requirement verbatim.
90
+ relationships:
91
+ - type: restates
92
+ from: REQ-billing-invoice-retention
93
+ to: REQ-platform-record-retention
94
+ ```
95
+
96
+ Use `restates` only when two current requirements deliberately ground the same
97
+ logical term (same predicate, property tuple, or rule semantic key). It keeps
98
+ both requirements current and tells `domain-redundancy` the duplication is
99
+ intentional. If one requirement replaces the other, use `supersedes` instead.
100
+
83
101
  ### covered_by
84
102
  ```yaml
85
103
  relationships:
86
104
  - type: covered_by
87
- from: SYM-handler
88
- to: TEST-005
105
+ from: SYM-operation-execute-check
106
+ to: TEST-mcp-search-discovery
89
107
  ```
90
108
 
91
109
  ## Invalid Test-Fact Shortcuts
@@ -29,3 +29,39 @@ Cancellation follows the same rule: wait for the terminal journal state. A
29
29
  pre-commit rollback is retryable, a committed source with a failed derivative
30
30
  is repairable, and an indeterminate outcome is non-retryable until its recovery
31
31
  action resolves the journal.
32
+
33
+ ## Naming entities
34
+
35
+ Name every entity by what it governs, the way you would name a source file.
36
+ Never choose "the next number": parallel branches pick the same next number and
37
+ collide even when they touch unrelated behavior.
38
+
39
+ - Use `<TYPE>-<area>-<behavior>` in kebab-case, with the type prefix kept
40
+ uppercase: `REQ-cli-gc`, `SCEN-mcp-search-discovery`,
41
+ `TEST-mcp-search-discovery`, `ADR-capability-plugins-v1`,
42
+ `EVT-exact-branch-store-ensure`, and a flag named after its gate such as
43
+ `FLAG-scip-symbol-extraction`.
44
+ - The filename stem equals the frontmatter `id`
45
+ (`.kb/requirements/REQ-cli-gc.md` has `id: REQ-cli-gc`). Always set `id`
46
+ explicitly; a missing `id` falls back to a path-and-title hash that changes
47
+ when the file is renamed.
48
+ - Before creating an entity, `kb_search` the area. If an existing entity already
49
+ covers the behavior, update it, or create a replacement and link it with
50
+ `supersedes`. A `-v2` suffix is acceptable only for that direct superseding
51
+ replacement.
52
+ - A name collision with another branch usually means the same work was done
53
+ twice. Reconcile the two entities; do not rename one of them to dodge the
54
+ collision.
55
+ - The area prefix is for humans. Whether two requirements are about the same
56
+ thing is decided by their facts: a shared `subject_key` and identical
57
+ predicate or property signatures, which `domain-redundancy` and
58
+ `domain-contradictions` compare deterministically. Reuse existing subject keys
59
+ (dotted `component.aspect[.sub]`, lowercase snake segments such as
60
+ `kibi.cli.check.staged`) instead of deriving a subject from the requirement
61
+ ID.
62
+
63
+ `entity-id-style` reports new purely numeric IDs (`REQ-123`) and IDs whose
64
+ filename stem differs from `id` as non-blocking quality diagnostics. Legacy
65
+ numbered entities created before the naming standard are grandfathered and are
66
+ not renamed, because code comments such as `// implements REQ-003` still
67
+ reference them.
@@ -33,11 +33,62 @@ surfaces are a release defect: require a new package version and label any
33
33
  project override temporary. This skill never selects a package manager or
34
34
  edits dependency configuration.
35
35
 
36
+ ## Choosing a discovery mode
37
+
38
+ `kb_search` is not one behavior. It selects deterministic intent ranking when
39
+ you pass `rankingMode: "intent-v1"`, `semanticFacets`, or `sourceLocations`,
40
+ and legacy lexical ranking otherwise. Naming the tool is not enough; the inputs
41
+ decide the path.
42
+
43
+ - **Exact identity** — you already know the ID. Use `kb_query`, not search.
44
+ - **Literal token** — a symbol, file, or error string that appears verbatim.
45
+ Use lexical `kb_search` and keep the query close to the literal text.
46
+ - **Conceptual or behavioral** — "how does checkout handle a declined card".
47
+ Use intent mode and ground it in what you actually know:
48
+
49
+ ```json
50
+ {
51
+ "query": "declined card during checkout",
52
+ "rankingMode": "intent-v1",
53
+ "semanticFacets": {
54
+ "actors": ["shopper"],
55
+ "actions": ["pay"],
56
+ "objects": ["card"],
57
+ "constraints": ["declined"]
58
+ }
59
+ }
60
+ ```
61
+
62
+ - **Before changing code** — pass the file you are about to edit as a source
63
+ location, so results are matched against source-linked symbols and return
64
+ source evidence:
65
+
66
+ ```json
67
+ {
68
+ "query": "checkout total rounding",
69
+ "rankingMode": "intent-v1",
70
+ "sourceLocations": [{ "path": "src/checkout.ts", "symbol": "computeTotal" }]
71
+ }
72
+ ```
73
+
74
+ Supply facets only from the request, the code, or prior results. Inventing
75
+ facets fabricates evidence and biases ranking. Intent mode abstains below
76
+ `minScore` instead of returning weak matches; an abstention is an explicit
77
+ no-answer, so widen the query or drop to lexical search rather than treating it
78
+ as "nothing exists". There is no target percentage of intent-mode calls — a
79
+ literal identifier lookup is still best served lexically.
80
+
81
+ Results are summaries by default: identifying metadata, score, reasons, and a
82
+ snippet. Read them to pick candidates, then `kb_query` those IDs for full
83
+ bodies, or pass `fields: "full"` when you genuinely need every body. Extract
84
+ the relevant existing constraints and decisions from the response before
85
+ planning a behavior change; a search receipt is not comprehension.
86
+
36
87
  ## Discovery to Validation Sequence
37
88
 
38
89
  The canonical workflow for any KB operation follows this pattern:
39
90
 
40
- 1. **Discover**: `kb_search` with focused probes
91
+ 1. **Discover**: `kb_search` with focused probes; pick the mode above
41
92
  2. **Confirm**: `kb_query` for exact IDs and state
42
93
  3. **Inspect**: `kb_status` when freshness matters
43
94
  4. **Decompose**: `kb_semantic_advisor` on the complete normative prose; verify or supply every atomic clause
@@ -363,11 +363,30 @@ function advisorBeforeRequirementWriteMetric(
363
363
  };
364
364
  }
365
365
 
366
+ function sourceLookupMessage(
367
+ zeroResults: number,
368
+ lookups: number,
369
+ unobservable: number,
370
+ ): string {
371
+ const unreadable =
372
+ unobservable > 0
373
+ ? `${unobservable} source-linked lookups recorded no readable result count.`
374
+ : "";
375
+ if (lookups === 0) {
376
+ return (
377
+ unreadable ||
378
+ "No source-linked query or search calls occurred in the evaluated window."
379
+ );
380
+ }
381
+ const observed = `${zeroResults}/${lookups} source-linked lookups returned zero results.`;
382
+ return unreadable ? `${observed} ${unreadable}` : observed;
383
+ }
384
+
366
385
  function sourceLookupMetric(
367
386
  recent: readonly TimedEvent[],
368
387
  policy: TelemetryAcceptancePolicy,
369
388
  ): MutableMetric {
370
- const lookups = recent.filter(({ event }) => {
389
+ const sourceLinked = recent.filter(({ event }) => {
371
390
  if (
372
391
  (event.tool !== "kb_query" && event.tool !== "kb_search") ||
373
392
  !eventSucceeded(event)
@@ -376,6 +395,14 @@ function sourceLookupMetric(
376
395
  }
377
396
  return typeof eventArgs(event).sourceFile === "string";
378
397
  });
398
+ // A row whose count could not be read is unobservable, not a hit. Counting
399
+ // it as a non-zero result would let a broken logger report a healthy rate.
400
+ const lookups = sourceLinked.filter(
401
+ ({ event }) =>
402
+ typeof event.result_count === "number" ||
403
+ typeof event.zero_results === "boolean",
404
+ );
405
+ const unobservable = sourceLinked.length - lookups.length;
379
406
  const zeroResults = lookups.filter(
380
407
  ({ event }) => event.zero_results === true || event.result_count === 0,
381
408
  );
@@ -387,7 +414,10 @@ function sourceLookupMetric(
387
414
  }
388
415
  return {
389
416
  id: "source_lookup_zero_result_rate",
390
- status: thresholdStatus(actual, "<=", policy.sourceLookupZeroResultMaximum),
417
+ status:
418
+ lookups.length === 0 && unobservable > 0
419
+ ? "insufficient_evidence"
420
+ : thresholdStatus(actual, "<=", policy.sourceLookupZeroResultMaximum),
391
421
  numerator: zeroResults.length,
392
422
  denominator: lookups.length,
393
423
  ...(actual !== undefined ? { rate: actual } : {}),
@@ -395,10 +425,11 @@ function sourceLookupMetric(
395
425
  operator: "<=",
396
426
  value: policy.sourceLookupZeroResultMaximum,
397
427
  },
398
- message:
399
- lookups.length === 0
400
- ? "No source-linked query or search calls occurred in the evaluated window."
401
- : `${zeroResults.length}/${lookups.length} source-linked lookups returned zero results.`,
428
+ message: sourceLookupMessage(
429
+ zeroResults.length,
430
+ lookups.length,
431
+ unobservable,
432
+ ),
402
433
  evidence: {
403
434
  zeroResultSourceFiles: [...sourceCounts.entries()]
404
435
  .sort(
@@ -1173,6 +1173,28 @@
1173
1173
  "type": "string"
1174
1174
  }
1175
1175
  },
1176
+ "argument_constants": {
1177
+ "type": "object",
1178
+ "additionalProperties": {
1179
+ "type": "array",
1180
+ "minItems": 1,
1181
+ "uniqueItems": true,
1182
+ "items": {
1183
+ "type": "string",
1184
+ "minLength": 1
1185
+ }
1186
+ }
1187
+ },
1188
+ "argument_aliases": {
1189
+ "type": "object",
1190
+ "additionalProperties": {
1191
+ "type": "object",
1192
+ "additionalProperties": {
1193
+ "type": "string",
1194
+ "minLength": 1
1195
+ }
1196
+ }
1197
+ },
1176
1198
  "rule_ir": {
1177
1199
  "$ref": "#/$defs/logicRule"
1178
1200
  },
@@ -1373,6 +1395,16 @@
1373
1395
  "predicate_args"
1374
1396
  ]
1375
1397
  },
1398
+ {
1399
+ "required": [
1400
+ "argument_constants"
1401
+ ]
1402
+ },
1403
+ {
1404
+ "required": [
1405
+ "argument_aliases"
1406
+ ]
1407
+ },
1376
1408
  {
1377
1409
  "required": [
1378
1410
  "rule_ir"
@@ -22,6 +22,7 @@
22
22
  "publishes",
23
23
  "consumes",
24
24
  "supersedes",
25
+ "restates",
25
26
  "relates_to"
26
27
  ]
27
28
  },