kibi-cli 2.32.0 → 2.36.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 (164) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +5 -1
  3. package/dist/commands/check-generated.d.ts +5 -1
  4. package/dist/commands/check-generated.d.ts.map +1 -1
  5. package/dist/commands/check-generated.js +24 -35
  6. package/dist/commands/init-helpers.d.ts.map +1 -1
  7. package/dist/commands/init-helpers.js +8 -5
  8. package/dist/commands/migrate.d.ts.map +1 -1
  9. package/dist/commands/migrate.js +23 -5
  10. package/dist/commands/sync/manifest.d.ts +10 -1
  11. package/dist/commands/sync/manifest.d.ts.map +1 -1
  12. package/dist/commands/sync/manifest.js +87 -14
  13. package/dist/commands/sync.d.ts.map +1 -1
  14. package/dist/commands/sync.js +19 -0
  15. package/dist/extractors/markdown.d.ts +1 -1
  16. package/dist/extractors/markdown.d.ts.map +1 -1
  17. package/dist/extractors/symbol-coordinates.js +2 -2
  18. package/dist/extractors/symbols-coordinator.d.ts +2 -4
  19. package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
  20. package/dist/extractors/symbols-coordinator.js +1 -2
  21. package/dist/operations/migration/proof-contract-claims.d.ts +23 -10
  22. package/dist/operations/migration/proof-contract-claims.d.ts.map +1 -1
  23. package/dist/operations/migration/proof-contract-claims.js +35 -2
  24. package/dist/operations/migration/schema6.d.ts.map +1 -1
  25. package/dist/operations/migration/schema6.js +2 -4
  26. package/dist/operations/migration/symbol-coordinates-refresh.d.ts +3 -2
  27. package/dist/operations/migration/symbol-coordinates-refresh.d.ts.map +1 -1
  28. package/dist/operations/migration/symbol-coordinates-refresh.js +4 -3
  29. package/dist/operations/migration/untrack-symbol-coordinates.d.ts +23 -0
  30. package/dist/operations/migration/untrack-symbol-coordinates.d.ts.map +1 -0
  31. package/dist/operations/migration/untrack-symbol-coordinates.js +67 -0
  32. package/dist/operations/modeling/requirement-modeler.d.ts.map +1 -1
  33. package/dist/operations/modeling/requirement-modeler.js +48 -1
  34. package/dist/operations/modeling/requirement-types.d.ts +2 -1
  35. package/dist/operations/modeling/requirement-types.d.ts.map +1 -1
  36. package/dist/operations/modeling/requirement-utils.d.ts +1 -1
  37. package/dist/operations/modeling/requirement-utils.d.ts.map +1 -1
  38. package/dist/operations/modeling/requirement-utils.js +8 -1
  39. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  40. package/dist/operations/mutation/source-authoring.js +39 -20
  41. package/dist/operations/proof/claim-bindings.d.ts +18 -0
  42. package/dist/operations/proof/claim-bindings.d.ts.map +1 -1
  43. package/dist/operations/proof/claim-bindings.js +157 -4
  44. package/dist/operations/proof/code-certificates.d.ts +34 -14
  45. package/dist/operations/proof/code-certificates.d.ts.map +1 -1
  46. package/dist/operations/proof/code-certificates.js +266 -157
  47. package/dist/operations/proof/code-models.d.ts +96 -0
  48. package/dist/operations/proof/code-models.d.ts.map +1 -0
  49. package/dist/operations/proof/code-models.js +601 -0
  50. package/dist/operations/proof/code-runner.d.ts +40 -0
  51. package/dist/operations/proof/code-runner.d.ts.map +1 -0
  52. package/dist/operations/proof/code-runner.js +164 -0
  53. package/dist/operations/proof/code-scope.d.ts +3 -0
  54. package/dist/operations/proof/code-scope.d.ts.map +1 -1
  55. package/dist/operations/proof/code-scope.js +26 -2
  56. package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
  57. package/dist/operations/proof/ingest-proof.js +2 -1
  58. package/dist/operations/proof/regular-language.d.ts +89 -0
  59. package/dist/operations/proof/regular-language.d.ts.map +1 -0
  60. package/dist/operations/proof/regular-language.js +943 -0
  61. package/dist/operations/proof/source-code-readers.d.ts +12 -0
  62. package/dist/operations/proof/source-code-readers.d.ts.map +1 -0
  63. package/dist/operations/proof/source-code-readers.js +699 -0
  64. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  65. package/dist/operations/semantic-advisor/analyze-prose.js +2 -1
  66. package/dist/operations/semantic-advisor/strict-count-rules.d.ts.map +1 -1
  67. package/dist/operations/semantic-advisor/strict-count-rules.js +8 -2
  68. package/dist/operations/semantic-advisor/strict-rules.d.ts +8 -0
  69. package/dist/operations/semantic-advisor/strict-rules.d.ts.map +1 -1
  70. package/dist/operations/semantic-advisor/strict-rules.js +231 -1
  71. package/dist/operations/semantic-advisor/types.d.ts +1 -1
  72. package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
  73. package/dist/prolog.d.ts.map +1 -1
  74. package/dist/prolog.js +13 -3
  75. package/dist/public/operations/catalog.d.ts +156 -13
  76. package/dist/public/operations/catalog.d.ts.map +1 -1
  77. package/dist/public/operations/evidence-floor.js +1 -1
  78. package/dist/public/operations/proof-contract-claims.js +1 -1
  79. package/dist/public/operations/specs/composite.d.ts +16 -3
  80. package/dist/public/operations/specs/composite.d.ts.map +1 -1
  81. package/dist/public/operations/specs/modeling.d.ts +16 -3
  82. package/dist/public/operations/specs/modeling.d.ts.map +1 -1
  83. package/dist/public/operations/specs/modeling.js +22 -3
  84. package/dist/public/operations/specs/mutation-schemas.d.ts +140 -10
  85. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  86. package/dist/public/operations/specs/mutation-schemas.js +14 -3
  87. package/dist/public/operations/specs/upsert.d.ts +140 -10
  88. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  89. package/dist/public/proof-certificates.d.ts +75 -1
  90. package/dist/public/proof-certificates.d.ts.map +1 -1
  91. package/dist/public/proof-certificates.js +212 -0
  92. package/dist/public/proof-protocol.d.ts +143 -10
  93. package/dist/public/proof-protocol.d.ts.map +1 -1
  94. package/dist/public/proof-protocol.js +301 -16
  95. package/dist/public/proof-receipt.d.ts +4 -3
  96. package/dist/public/proof-receipt.d.ts.map +1 -1
  97. package/dist/public/proof-receipt.js +6 -1
  98. package/dist/public/proof-sources.d.ts +102 -0
  99. package/dist/public/proof-sources.d.ts.map +1 -0
  100. package/dist/public/proof-sources.js +1292 -0
  101. package/dist/public/schemas/entity.d.ts.map +1 -1
  102. package/dist/public/schemas/entity.js +21 -1
  103. package/dist/public/skills/kibi-commit-review/SKILL.md +8 -8
  104. package/dist/public/skills/kibi-commit-review/resources/staged-gate.md +6 -6
  105. package/dist/public/skills/kibi-decisions/SKILL.md +5 -2
  106. package/dist/public/skills/kibi-decisions/resources/decision-records.md +4 -1
  107. package/dist/public/skills/kibi-freshness/SKILL.md +2 -2
  108. package/dist/public/skills/kibi-traceability/SKILL.md +3 -2
  109. package/dist/public/skills/kibi-usage/SKILL.md +5 -3
  110. package/dist/public/skills/kibi-usage/resources/proof.md +105 -9
  111. package/dist/report/html-report.css +45 -10
  112. package/dist/report/html-report.d.ts.map +1 -1
  113. package/dist/report/html-report.js +74 -17
  114. package/dist/report/report-view.d.ts +1 -1
  115. package/dist/report/report-view.d.ts.map +1 -1
  116. package/dist/report/report-view.js +1 -0
  117. package/dist/schemas/entity.schema.json +199 -8
  118. package/dist/traceability/evidence-model.d.ts +13 -12
  119. package/dist/traceability/evidence-model.d.ts.map +1 -1
  120. package/dist/traceability/evidence-model.js +1 -1
  121. package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
  122. package/dist/traceability/staged-diagnostics.js +24 -20
  123. package/dist/traceability/staged-impact-contract.d.ts +4 -4
  124. package/dist/traceability/staged-impact-contract.js +9 -9
  125. package/dist/traceability/staged-symbols-manifest.d.ts +11 -3
  126. package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
  127. package/dist/traceability/staged-symbols-manifest.js +47 -140
  128. package/dist/types/entities.d.ts +1 -1
  129. package/dist/types/entities.d.ts.map +1 -1
  130. package/dist/utils/rule-registry.generated.d.ts +1 -1
  131. package/dist/utils/rule-registry.generated.js +1 -1
  132. package/dist/utils/schema-version.d.ts +1 -1
  133. package/dist/utils/schema-version.d.ts.map +1 -1
  134. package/dist/utils/schema-version.js +3 -1
  135. package/dist/utils/strict-modeling.d.ts +3 -2
  136. package/dist/utils/strict-modeling.d.ts.map +1 -1
  137. package/dist/utils/strict-modeling.js +37 -4
  138. package/dist/workspace-cwd.d.ts +15 -0
  139. package/dist/workspace-cwd.d.ts.map +1 -0
  140. package/dist/workspace-cwd.js +86 -0
  141. package/package.json +2 -2
  142. package/schema/validation.pl +3 -1
  143. package/src/public/operations/evidence-floor.ts +1 -1
  144. package/src/public/operations/proof-contract-claims.ts +1 -1
  145. package/src/public/operations/specs/modeling.ts +24 -5
  146. package/src/public/operations/specs/mutation-schemas.ts +14 -3
  147. package/src/public/proof-certificates.ts +296 -1
  148. package/src/public/proof-protocol-types.d.ts +54 -2
  149. package/src/public/proof-protocol.ts +396 -20
  150. package/src/public/proof-receipt.ts +8 -2
  151. package/src/public/proof-sources.ts +1656 -0
  152. package/src/public/schemas/entity.ts +32 -2
  153. package/src/public/skills/kibi-commit-review/SKILL.md +8 -8
  154. package/src/public/skills/kibi-commit-review/resources/staged-gate.md +6 -6
  155. package/src/public/skills/kibi-decisions/SKILL.md +5 -2
  156. package/src/public/skills/kibi-decisions/resources/decision-records.md +4 -1
  157. package/src/public/skills/kibi-freshness/SKILL.md +2 -2
  158. package/src/public/skills/kibi-traceability/SKILL.md +3 -2
  159. package/src/public/skills/kibi-usage/SKILL.md +5 -3
  160. package/src/public/skills/kibi-usage/resources/proof.md +105 -9
  161. package/src/schemas/entity.schema.json +199 -8
  162. package/dist/traceability/reviewed-coordinate-refresh.d.ts +0 -10
  163. package/dist/traceability/reviewed-coordinate-refresh.d.ts.map +0 -1
  164. package/dist/traceability/reviewed-coordinate-refresh.js +0 -49
@@ -94,14 +94,66 @@ export type ProofContract = Readonly<{
94
94
  | "exhaustive"
95
95
  | "typecheck"
96
96
  | "arithmetic"
97
- | "smt";
97
+ | "smt"
98
+ | "rule"
99
+ | "state_machine"
100
+ | "regex"
101
+ | "source";
98
102
  domain_source?: Readonly<{ symbol_id: string; export?: string }>;
99
103
  witness?: Readonly<{ path: string }>;
100
104
  constraint_source?: Readonly<{ fact_ids: readonly string[] }>;
101
- value_source?: readonly Readonly<{ symbol_id: string; export?: string }>[];
105
+ value_source?: readonly (
106
+ | Readonly<{
107
+ symbol_id: string;
108
+ export?: string;
109
+ /** value (default): the export is a number; count: the size of a list, set, map or object. */
110
+ measure?: "value" | "count";
111
+ }>
112
+ | Readonly<{
113
+ /** A project source whose members Kibi counts. */
114
+ source: ProofSourceSpec;
115
+ measure: "count";
116
+ }>
117
+ )[];
102
118
  proves?: readonly string[];
119
+ predicate_source?: Readonly<
120
+ Record<
121
+ string,
122
+ Readonly<{
123
+ symbol_id: string;
124
+ export?: string;
125
+ /** The predicate is a test harness driving these code symbols. */
126
+ harness_for?: readonly string[];
127
+ }>
128
+ >
129
+ >;
130
+ domains?: Readonly<
131
+ Record<
132
+ string,
133
+ | Readonly<{ symbol_id: string; export?: string }>
134
+ | Readonly<{ source: ProofSourceSpec }>
135
+ >
136
+ >;
137
+ initial_source?: Readonly<{ symbol_id: string; export?: string }>;
138
+ pattern_source?: Readonly<{ symbol_id: string; export?: string }>;
139
+ /** kind source: the project source Kibi reads. */
140
+ source?: ProofSourceSpec;
103
141
  }>;
104
142
 
143
+ /** A project source a source proof, a rule domain or a count reads; see proof-sources. */
144
+ export type ProofSourceSpec = Readonly<Record<string, unknown>> &
145
+ Readonly<{
146
+ reader:
147
+ | "json"
148
+ | "yaml"
149
+ | "glob"
150
+ | "openapi"
151
+ | "references"
152
+ | "schema"
153
+ | "routes"
154
+ | "validator";
155
+ }>;
156
+
105
157
  export type ProofBinding = Readonly<{
106
158
  symbol_id: string;
107
159
  target: string;
@@ -70,8 +70,11 @@ export const SUCCESS_POLICIES = [
70
70
  /**
71
71
  * What a proof contract's checker establishes. Runner kinds (e2e, example,
72
72
  * property) sample cases and support tested evidence; certificate kinds
73
- * (exhaustive, typecheck, arithmetic, smt) cover every case and support
74
- * proved evidence once Kibi re-checks the certificate the run left.
73
+ * (exhaustive, typecheck, arithmetic, smt, rule, state_machine, regex,
74
+ * source) cover every case and support proved evidence once Kibi re-checks
75
+ * the certificate the run left. A source certificate is Kibi's own read of a
76
+ * project source (a JSON or YAML file, a glob, an OpenAPI document, the
77
+ * references the code makes, a data schema, a route table, a validator).
75
78
  */
76
79
  // implements REQ-kibi-code-proof-kinds
77
80
  export const PROOF_KINDS = [
@@ -82,14 +85,22 @@ export const PROOF_KINDS = [
82
85
  "typecheck",
83
86
  "arithmetic",
84
87
  "smt",
88
+ "rule",
89
+ "state_machine",
90
+ "regex",
91
+ "source",
85
92
  ] as const;
86
93
 
87
- // implements REQ-kibi-code-proof-kinds
94
+ // implements REQ-kibi-code-proof-kinds, REQ-kibi-non-numeric-proof-kinds, REQ-kibi-source-proof-kinds
88
95
  export const CERTIFICATE_PROOF_KINDS = [
89
96
  "exhaustive",
90
97
  "typecheck",
91
98
  "arithmetic",
92
99
  "smt",
100
+ "rule",
101
+ "state_machine",
102
+ "regex",
103
+ "source",
93
104
  ] as const;
94
105
 
95
106
  // implements REQ-kibi-code-proof-kinds
@@ -127,6 +138,12 @@ import type {
127
138
  ProofResult,
128
139
  ProofRunArtifact,
129
140
  } from "./proof-protocol-types.js";
141
+ import {
142
+ SOURCE_READERS,
143
+ type SourceSpec,
144
+ normalizeSourceSpec,
145
+ sourceSpecErrors,
146
+ } from "./proof-sources.js";
130
147
 
131
148
  export const PROOF_RESULT_SCHEMA = {
132
149
  type: "object",
@@ -262,6 +279,57 @@ export const PROOF_RUN_ARTIFACT_SCHEMA = {
262
279
  additionalProperties: false,
263
280
  } as const;
264
281
 
282
+ const SOURCE_REFERENCE_SCHEMA = {
283
+ type: "object",
284
+ required: ["symbol_id"],
285
+ properties: {
286
+ symbol_id: { type: "string", minLength: 1 },
287
+ export: { type: "string", minLength: 1 },
288
+ },
289
+ additionalProperties: false,
290
+ } as const;
291
+
292
+ // implements REQ-kibi-source-proof-kinds
293
+ const SOURCE_SPEC_SCHEMA = {
294
+ type: "object",
295
+ description:
296
+ "A project source Kibi reads by itself. reader json or yaml: {file, path?, select?: keys|values|items}. glob: {pattern, capture?, ignore?} (capture true reads what each * matched). openapi: {file, select?: operations|unsecured_operations|security_schemes|responses}. references: {files, extract: env|imports|calls|string_args|thrown|property, callee?, key?, ignore?}. schema: {format: drizzle|prisma|sql, file (drizzle, prisma) or files (sql migrations), exports?, table?, select?: tables|columns|required_columns|nullable_columns|foreign_keys}. routes: {file, export, framework?: hono|express, path_prefix?, names_from?, select?: routes|middleware}. validator: {file, export, select?: fields|required_fields|optional_fields}. Paths are repository-relative. A member Kibi cannot read statically fails the read with its location.",
297
+ required: ["reader"],
298
+ properties: {
299
+ reader: { type: "string", enum: [...SOURCE_READERS] },
300
+ },
301
+ additionalProperties: true,
302
+ } as const;
303
+
304
+ const SOURCE_DOMAIN_SCHEMA = {
305
+ type: "object",
306
+ required: ["source"],
307
+ properties: { source: SOURCE_SPEC_SCHEMA },
308
+ additionalProperties: false,
309
+ } as const;
310
+
311
+ const PREDICATE_SOURCE_SCHEMA = {
312
+ type: "object",
313
+ required: ["symbol_id"],
314
+ properties: {
315
+ symbol_id: { type: "string", minLength: 1 },
316
+ export: {
317
+ type: "string",
318
+ minLength: 1,
319
+ description:
320
+ "The exported function's name in the symbol's source file. Without it Kibi reads the export named by the symbol's title.",
321
+ },
322
+ harness_for: {
323
+ type: "array",
324
+ minItems: 1,
325
+ items: { type: "string", minLength: 1 },
326
+ description:
327
+ "The predicate is a test harness that decides the predicate by driving these code symbols (for example by calling the handler with each role). The symbols must be covered_by the test; a claim the harness proves is bound with scope harness.",
328
+ },
329
+ },
330
+ additionalProperties: false,
331
+ } as const;
332
+
265
333
  export const PROOF_CONTRACT_SCHEMA = {
266
334
  type: "object",
267
335
  description:
@@ -289,12 +357,16 @@ export const PROOF_CONTRACT_SCHEMA = {
289
357
  type: "string",
290
358
  enum: [...PROOF_KINDS],
291
359
  description:
292
- "What the checker establishes; absent means e2e. exhaustive, typecheck, arithmetic and smt must leave a kibi.code-certificate.v1 that Kibi re-checks before the receipt counts as proved.",
360
+ "What the checker establishes; absent means e2e. exhaustive, typecheck, arithmetic, smt, rule, state_machine and regex must leave a kibi.code-certificate.v1 that Kibi re-checks before the receipt counts as proved. source reads the contract's source itself: the run may leave {version, kind: source, test_id, source} or nothing, and Kibi stores its own read as the certificate.",
361
+ },
362
+ source: {
363
+ ...SOURCE_SPEC_SCHEMA,
364
+ description: `source: the project source a source proof reads. ${SOURCE_SPEC_SCHEMA.description}`,
293
365
  },
294
366
  domain_source: {
295
367
  type: "object",
296
368
  description:
297
- "exhaustive and typecheck: the symbol that defines the finite domain. The proof must import it from its source file; a hand-listed domain downgrades the receipt to example. Kibi reads the export named `export`, or the symbol's title when `export` is absent.",
369
+ "exhaustive and typecheck: the symbol that defines the finite domain (typecheck: also the type whose fields a required_fields fact names). state_machine: the transition table or next-state function. The proof must import it from its source file; a hand-listed domain downgrades the receipt to example. Kibi reads the export named `export`, or the symbol's title when `export` is absent.",
298
370
  required: ["symbol_id"],
299
371
  properties: {
300
372
  symbol_id: { type: "string", minLength: 1 },
@@ -318,7 +390,7 @@ export const PROOF_CONTRACT_SCHEMA = {
318
390
  proves: {
319
391
  type: "array",
320
392
  description:
321
- "Certificate kinds: the requirement claim keys (CLAIM-...) this certificate proves. Each must be a modeled claim of a current requirement the test reaches through its scenarios. Kibi derives what the certificate must establish from the claim's grounding fact (a set fact for exhaustive or typecheck, a numeric fact for arithmetic or smt); a claim with no such derivation is only attested and never counts as proved.",
393
+ "Certificate kinds: the requirement claim keys (CLAIM-...) this certificate proves. Each must be a modeled claim of a current requirement the test reaches through its scenarios. Kibi derives what the certificate must establish from the claim's grounding fact (a set fact for exhaustive or typecheck, a numeric fact for arithmetic or smt, a rule, predicate or set fact for rule and state_machine, a string fact for regex); a claim with no such derivation is only attested and never counts as proved.",
322
394
  minItems: 1,
323
395
  items: { type: "string", pattern: "^CLAIM-" },
324
396
  uniqueItems: true,
@@ -326,18 +398,50 @@ export const PROOF_CONTRACT_SCHEMA = {
326
398
  value_source: {
327
399
  type: "array",
328
400
  description:
329
- "arithmetic and smt: code constants the assumptions read. Kibi evaluates each export at ingest and requires the certificate to assume the variable equals that value, so the proof is about the code and not only the knowledge base.",
401
+ "arithmetic and smt: code constants the assumptions read. Kibi evaluates each export at ingest and requires the certificate to assume the variable equals that value, so the proof is about the code and not only the knowledge base. With measure count the value is the size of a list, set, map or object export, so a count claim reads the collection itself. An entry {source, measure: count} counts the members Kibi reads from a project source (routes, tables, environment variables).",
330
402
  minItems: 1,
331
403
  items: {
332
404
  type: "object",
333
- required: ["symbol_id"],
334
405
  properties: {
335
406
  symbol_id: { type: "string", minLength: 1 },
336
407
  export: { type: "string", minLength: 1 },
408
+ source: SOURCE_SPEC_SCHEMA,
409
+ measure: {
410
+ type: "string",
411
+ enum: ["value", "count"],
412
+ description:
413
+ "value (default for an export): the export is a number. count (the only measure of a source): the number of members of a list, set, map or object export, or of a source.",
414
+ },
337
415
  },
338
416
  additionalProperties: false,
339
417
  },
340
418
  },
419
+ predicate_source: {
420
+ type: "object",
421
+ description:
422
+ "rule: the code function behind each predicate a claim's rule, predicate or set fact names, keyed by predicate name. Each export takes the predicate's arguments in order and returns a boolean; Kibi calls it on every combination of its argument domains.",
423
+ minProperties: 1,
424
+ additionalProperties: PREDICATE_SOURCE_SCHEMA,
425
+ },
426
+ domains: {
427
+ type: "object",
428
+ description:
429
+ "rule: the finite domain of each argument type, keyed by type name (an array, Set, Map or object export, or a Python list, set, dict or Enum), or {source} for the members Kibi reads from a project source (every route, every table, every environment variable). state_machine with a next-state function: the `state` and `event` domains it is called on.",
430
+ minProperties: 1,
431
+ additionalProperties: {
432
+ anyOf: [SOURCE_REFERENCE_SCHEMA, SOURCE_DOMAIN_SCHEMA],
433
+ },
434
+ },
435
+ initial_source: {
436
+ ...SOURCE_REFERENCE_SCHEMA,
437
+ description:
438
+ "state_machine: the export holding the initial state (or states), needed for initial_states and reachable_states facts.",
439
+ },
440
+ pattern_source: {
441
+ ...SOURCE_REFERENCE_SCHEMA,
442
+ description:
443
+ "regex: the export holding the code's pattern (a RegExp, or a Python re.Pattern) or string constant that a string fact is about.",
444
+ },
341
445
  constraint_source: {
342
446
  type: "object",
343
447
  description:
@@ -751,29 +855,55 @@ function proofContractKindErrors(contract: Record<string, unknown>): string[] {
751
855
  );
752
856
  const valueSource = contract.value_source;
753
857
  if (valueSource !== undefined) {
858
+ if (Array.isArray(valueSource))
859
+ valueSource.forEach((entry, index) => {
860
+ const row = record(entry);
861
+ if (row === null || row.source === undefined) return;
862
+ errors.push(
863
+ ...sourceSpecErrors(
864
+ row.source,
865
+ `proof_contract.value_source[${index}].source`,
866
+ ),
867
+ );
868
+ if (row.symbol_id !== undefined || row.export !== undefined)
869
+ errors.push(
870
+ `proof_contract.value_source[${index}] names a source, so it takes no symbol_id or export`,
871
+ );
872
+ if (row.measure !== undefined && row.measure !== "count")
873
+ errors.push(
874
+ `proof_contract.value_source[${index}].measure must be count for a source: Kibi counts the members it reads`,
875
+ );
876
+ });
754
877
  if (
755
878
  !Array.isArray(valueSource) ||
756
879
  valueSource.length === 0 ||
757
880
  !valueSource.every((entry) => {
758
881
  const row = record(entry);
882
+ if (row !== null && row.source !== undefined) return true;
759
883
  return (
760
884
  row !== null &&
761
885
  nonEmptyString(row.symbol_id) &&
762
886
  (row.export === undefined || nonEmptyString(row.export)) &&
887
+ (row.measure === undefined ||
888
+ row.measure === "value" ||
889
+ row.measure === "count") &&
763
890
  Object.keys(row).every(
764
- (key) => key === "symbol_id" || key === "export",
891
+ (key) =>
892
+ key === "symbol_id" || key === "export" || key === "measure",
765
893
  )
766
894
  );
767
895
  })
768
896
  )
769
897
  errors.push(
770
- "proof_contract.value_source must be [{symbol_id, export?}] naming the code constants the assumptions read",
898
+ "proof_contract.value_source must be [{symbol_id, export?, measure?: value|count} or {source, measure?: count}] naming the code constants or project sources the assumptions read",
771
899
  );
772
900
  else if (kind !== "arithmetic" && kind !== "smt")
773
901
  errors.push(
774
902
  `proof_contract.value_source applies only to kind arithmetic or smt, not ${String(kind ?? "e2e")}`,
775
903
  );
776
904
  }
905
+ errors.push(...nonNumericContractErrors(contract));
906
+ errors.push(...sourceContractErrors(contract));
777
907
  const proves = contract.proves;
778
908
  if (proves !== undefined) {
779
909
  if (
@@ -789,12 +919,164 @@ function proofContractKindErrors(contract: Record<string, unknown>): string[] {
789
919
  );
790
920
  else if (!isCertificateProofKind(kind))
791
921
  errors.push(
792
- `proof_contract.proves applies only to certificate kinds (exhaustive, typecheck, arithmetic, smt), not ${String(kind ?? "e2e")}`,
922
+ `proof_contract.proves applies only to certificate kinds (${CERTIFICATE_PROOF_KINDS.join(", ")}), not ${String(kind ?? "e2e")}`,
793
923
  );
794
924
  }
795
925
  return errors;
796
926
  }
797
927
 
928
+ function isSourceReference(value: unknown): boolean {
929
+ const row = record(value);
930
+ return (
931
+ row !== null &&
932
+ nonEmptyString(row.symbol_id) &&
933
+ (row.export === undefined || nonEmptyString(row.export)) &&
934
+ Object.keys(row).every((key) => key === "symbol_id" || key === "export")
935
+ );
936
+ }
937
+
938
+ function isSourceMap(
939
+ value: unknown,
940
+ accepts: (entry: unknown) => boolean = isSourceReference,
941
+ ): boolean {
942
+ const row = record(value);
943
+ return (
944
+ row !== null &&
945
+ Object.keys(row).length > 0 &&
946
+ Object.entries(row).every(
947
+ ([key, entry]) => key.trim() !== "" && accepts(entry),
948
+ )
949
+ );
950
+ }
951
+
952
+ function isPredicateReference(value: unknown): boolean {
953
+ const row = record(value);
954
+ if (row === null || row.harness_for === undefined)
955
+ return isSourceReference(value);
956
+ const { harness_for: harness, ...reference } = row;
957
+ return (
958
+ isSourceReference(reference) &&
959
+ Array.isArray(harness) &&
960
+ harness.length > 0 &&
961
+ harness.every(nonEmptyString)
962
+ );
963
+ }
964
+
965
+ function isSourceDomain(value: unknown): boolean {
966
+ const row = record(value);
967
+ return (
968
+ row !== null && Object.keys(row).length === 1 && row.source !== undefined
969
+ );
970
+ }
971
+
972
+ /**
973
+ * The source kind and the source-backed rule domains: a well-formed source
974
+ * spec, required for kind source and only there.
975
+ */
976
+ // implements REQ-kibi-source-proof-kinds
977
+ function sourceContractErrors(contract: Record<string, unknown>): string[] {
978
+ const errors: string[] = [];
979
+ const kind = contract.kind;
980
+ if (contract.source !== undefined) {
981
+ errors.push(...sourceSpecErrors(contract.source, "proof_contract.source"));
982
+ if (kind !== "source")
983
+ errors.push(
984
+ `proof_contract.source applies only to kind source, not ${String(kind ?? "e2e")}`,
985
+ );
986
+ } else if (kind === "source")
987
+ errors.push(
988
+ "proof_contract.source is required for kind source: name the project source Kibi reads (reader json, yaml, glob, openapi, references, schema, routes or validator)",
989
+ );
990
+ const domains = record(contract.domains);
991
+ for (const [type, entry] of Object.entries(domains ?? {}))
992
+ if (isSourceDomain(entry)) {
993
+ errors.push(
994
+ ...sourceSpecErrors(
995
+ record(entry)?.source,
996
+ `proof_contract.domains.${type}.source`,
997
+ ),
998
+ );
999
+ if (kind !== "rule")
1000
+ errors.push(
1001
+ `proof_contract.domains.${type}.source applies only to kind rule: a state machine's domains are the code's`,
1002
+ );
1003
+ }
1004
+ return errors;
1005
+ }
1006
+
1007
+ /**
1008
+ * The contract fields of the rule, state_machine and regex kinds: where the
1009
+ * code Kibi evaluates lives, and only on the kinds that read it.
1010
+ */
1011
+ // implements REQ-kibi-non-numeric-proof-kinds
1012
+ function nonNumericContractErrors(contract: Record<string, unknown>): string[] {
1013
+ const errors: string[] = [];
1014
+ const kind = contract.kind;
1015
+ const only = (field: string, kinds: readonly string[]) => {
1016
+ if (contract[field] !== undefined && !kinds.includes(String(kind)))
1017
+ errors.push(
1018
+ `proof_contract.${field} applies only to kind ${kinds.join(" or ")}, not ${String(kind ?? "e2e")}`,
1019
+ );
1020
+ };
1021
+ if (
1022
+ contract.predicate_source !== undefined &&
1023
+ !isSourceMap(contract.predicate_source, isPredicateReference)
1024
+ )
1025
+ errors.push(
1026
+ "proof_contract.predicate_source must map each predicate name to {symbol_id, export?, harness_for?} naming the function that decides it",
1027
+ );
1028
+ if (
1029
+ contract.domains !== undefined &&
1030
+ !isSourceMap(
1031
+ contract.domains,
1032
+ (entry) => isSourceReference(entry) || isSourceDomain(entry),
1033
+ )
1034
+ )
1035
+ errors.push(
1036
+ "proof_contract.domains must map each argument type to {symbol_id, export?} naming the export that lists its values, or {source} naming the project source Kibi reads them from",
1037
+ );
1038
+ for (const field of ["initial_source", "pattern_source"])
1039
+ if (contract[field] !== undefined && !isSourceReference(contract[field]))
1040
+ errors.push(
1041
+ `proof_contract.${field} must be {symbol_id, export?} naming the export Kibi reads`,
1042
+ );
1043
+ only("predicate_source", ["rule"]);
1044
+ only("domains", ["rule", "state_machine"]);
1045
+ only("initial_source", ["state_machine"]);
1046
+ only("pattern_source", ["regex"]);
1047
+ if (kind === "rule") {
1048
+ if (contract.predicate_source === undefined)
1049
+ errors.push(
1050
+ "proof_contract.predicate_source is required for kind rule: name the function behind each predicate the claims use",
1051
+ );
1052
+ if (contract.domains === undefined)
1053
+ errors.push(
1054
+ "proof_contract.domains is required for kind rule: name the export listing each argument type's values",
1055
+ );
1056
+ }
1057
+ if (kind === "state_machine") {
1058
+ if (contract.domain_source === undefined)
1059
+ errors.push(
1060
+ "proof_contract.domain_source is required for kind state_machine: name the transition table or next-state function",
1061
+ );
1062
+ const domains = record(contract.domains);
1063
+ if (
1064
+ domains !== null &&
1065
+ (Object.keys(domains).length !== 2 ||
1066
+ domains.state === undefined ||
1067
+ domains.event === undefined)
1068
+ )
1069
+ errors.push(
1070
+ "proof_contract.domains for kind state_machine must name exactly the state and event domains a next-state function is called on",
1071
+ );
1072
+ }
1073
+ if (kind === "regex" && contract.pattern_source === undefined)
1074
+ errors.push(
1075
+ "proof_contract.pattern_source is required for kind regex: name the export holding the pattern or string",
1076
+ );
1077
+ return errors;
1078
+ }
1079
+
798
1080
  /**
799
1081
  * The optional contract fields beyond the v1 core, copied only when present
800
1082
  * so existing contracts keep their hash.
@@ -810,14 +1092,24 @@ export function optionalProofContractFields(
810
1092
  | "constraint_source"
811
1093
  | "value_source"
812
1094
  | "proves"
1095
+ | "predicate_source"
1096
+ | "domains"
1097
+ | "initial_source"
1098
+ | "pattern_source"
1099
+ | "source"
813
1100
  > {
814
1101
  const fields: {
815
1102
  kind?: ProofKind;
816
1103
  domain_source?: { symbol_id: string; export?: string };
817
1104
  witness?: { path: string };
818
1105
  constraint_source?: { fact_ids: string[] };
819
- value_source?: { symbol_id: string; export?: string }[];
1106
+ value_source?: ValueSourceEntry[];
820
1107
  proves?: string[];
1108
+ predicate_source?: Record<string, PredicateReference>;
1109
+ domains?: Record<string, SourceReference | { source: SourceSpec }>;
1110
+ initial_source?: SourceReference;
1111
+ pattern_source?: SourceReference;
1112
+ source?: SourceSpec;
821
1113
  } = {};
822
1114
  if (typeof contract.kind === "string")
823
1115
  fields.kind = contract.kind as ProofKind;
@@ -840,21 +1132,105 @@ export function optionalProofContractFields(
840
1132
  if (Array.isArray(contract.value_source))
841
1133
  fields.value_source = contract.value_source
842
1134
  .map((entry) => record(entry))
843
- .filter(
844
- (row): row is Record<string, unknown> =>
845
- row !== null && typeof row.symbol_id === "string",
846
- )
847
- .map((row) => ({
848
- symbol_id: String(row.symbol_id),
849
- ...(typeof row.export === "string" ? { export: row.export } : {}),
850
- }));
1135
+ .map((row): ValueSourceEntry | null => {
1136
+ if (row === null) return null;
1137
+ const spec = normalizeSourceSpec(row.source);
1138
+ if (spec !== undefined) return { source: spec, measure: "count" };
1139
+ if (typeof row.symbol_id !== "string") return null;
1140
+ return {
1141
+ symbol_id: row.symbol_id,
1142
+ ...(typeof row.export === "string" ? { export: row.export } : {}),
1143
+ ...(row.measure === "value" || row.measure === "count"
1144
+ ? { measure: row.measure }
1145
+ : {}),
1146
+ };
1147
+ })
1148
+ .filter((row): row is ValueSourceEntry => row !== null);
851
1149
  if (Array.isArray(contract.proves))
852
1150
  fields.proves = contract.proves.filter(
853
1151
  (key): key is string => typeof key === "string",
854
1152
  );
1153
+ const predicates = predicateMap(contract.predicate_source);
1154
+ if (predicates !== undefined) fields.predicate_source = predicates;
1155
+ const domains = domainMap(contract.domains);
1156
+ if (domains !== undefined) fields.domains = domains;
1157
+ const spec = normalizeSourceSpec(contract.source);
1158
+ if (spec !== undefined) fields.source = spec;
1159
+ const initial = sourceReference(contract.initial_source);
1160
+ if (initial !== undefined) fields.initial_source = initial;
1161
+ const pattern = sourceReference(contract.pattern_source);
1162
+ if (pattern !== undefined) fields.pattern_source = pattern;
855
1163
  return fields;
856
1164
  }
857
1165
 
1166
+ type SourceReference = { symbol_id: string; export?: string };
1167
+ type PredicateReference = SourceReference & { harness_for?: string[] };
1168
+ type ValueSourceEntry =
1169
+ | { symbol_id: string; export?: string; measure?: "value" | "count" }
1170
+ | { source: SourceSpec; measure: "count" };
1171
+
1172
+ function predicateMap(
1173
+ value: unknown,
1174
+ ): Record<string, PredicateReference> | undefined {
1175
+ const row = record(value);
1176
+ if (!row) return undefined;
1177
+ const out: Record<string, PredicateReference> = {};
1178
+ for (const [key, entry] of Object.entries(row)) {
1179
+ const reference = sourceReference(entry);
1180
+ if (reference === undefined) continue;
1181
+ const harness = record(entry)?.harness_for;
1182
+ out[key] = Array.isArray(harness)
1183
+ ? {
1184
+ ...reference,
1185
+ harness_for: harness.filter(
1186
+ (id): id is string => typeof id === "string",
1187
+ ),
1188
+ }
1189
+ : reference;
1190
+ }
1191
+ return Object.keys(out).length > 0 ? out : undefined;
1192
+ }
1193
+
1194
+ function domainMap(
1195
+ value: unknown,
1196
+ ): Record<string, SourceReference | { source: SourceSpec }> | undefined {
1197
+ const row = record(value);
1198
+ if (!row) return undefined;
1199
+ const out: Record<string, SourceReference | { source: SourceSpec }> = {};
1200
+ for (const [key, entry] of Object.entries(row)) {
1201
+ const spec = normalizeSourceSpec(record(entry)?.source);
1202
+ if (spec !== undefined) {
1203
+ out[key] = { source: spec };
1204
+ continue;
1205
+ }
1206
+ const reference = sourceReference(entry);
1207
+ if (reference !== undefined) out[key] = reference;
1208
+ }
1209
+ return Object.keys(out).length > 0 ? out : undefined;
1210
+ }
1211
+
1212
+ function sourceReference(value: unknown): SourceReference | undefined {
1213
+ const row = record(value);
1214
+ if (!row || typeof row.symbol_id !== "string") return undefined;
1215
+ return {
1216
+ symbol_id: row.symbol_id,
1217
+ ...(typeof row.export === "string" ? { export: row.export } : {}),
1218
+ };
1219
+ }
1220
+
1221
+ function sourceMap(
1222
+ value: unknown,
1223
+ ): Record<string, SourceReference> | undefined {
1224
+ const row = record(value);
1225
+ if (!row) return undefined;
1226
+ const out: Record<string, SourceReference> = {};
1227
+ for (const [key, entry] of Object.entries(row)) {
1228
+ const reference = sourceReference(entry);
1229
+ if (reference !== undefined) out[key] = reference;
1230
+ }
1231
+ return Object.keys(out).length > 0 ? out : undefined;
1232
+ }
1233
+
858
1234
  export function proofBindingsErrors(value: unknown): string[] {
859
1235
  if (!Array.isArray(value))
860
1236
  return ["proof_bindings must be an array of binding objects"];
@@ -183,7 +183,10 @@ export const PROOF_RECEIPT_SCHEMA = {
183
183
  type: "string",
184
184
  enum: [...CLAIM_BINDING_VERDICTS],
185
185
  },
186
- scope: { type: "string", enum: ["code", "intent"] },
186
+ scope: {
187
+ type: "string",
188
+ enum: ["code", "source", "harness", "intent"],
189
+ },
187
190
  obligation: { type: "object" },
188
191
  reason: { type: "string", minLength: 1 },
189
192
  },
@@ -265,7 +268,8 @@ export type ClaimBinding = Readonly<{
265
268
  requirement_id?: string;
266
269
  fact_id?: string;
267
270
  binding: ClaimBindingVerdict;
268
- scope?: "code" | "intent";
271
+ /** What the proof rests on: evaluated code, project files Kibi read (source), a test harness, or the knowledge base alone (intent). */
272
+ scope?: "code" | "source" | "harness" | "intent";
269
273
  obligation?: Readonly<Record<string, unknown>>;
270
274
  reason?: string;
271
275
  }>;
@@ -471,6 +475,8 @@ function validClaimBindings(value: unknown): boolean {
471
475
  ) &&
472
476
  (entry.scope === undefined ||
473
477
  entry.scope === "code" ||
478
+ entry.scope === "source" ||
479
+ entry.scope === "harness" ||
474
480
  entry.scope === "intent") &&
475
481
  (entry.obligation === undefined || isRecord(entry.obligation)) &&
476
482
  (entry.reason === undefined || nonEmptyString(entry.reason)),