kibi-cli 2.31.1 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-register-proof.d.ts.map +1 -1
- package/dist/cli-register-proof.js +19 -7
- package/dist/cli-register-reporting.d.ts.map +1 -1
- package/dist/cli-register-reporting.js +2 -0
- package/dist/commands/check.d.ts +1 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +14 -5
- package/dist/commands/discovery-table.d.ts.map +1 -1
- package/dist/commands/discovery-table.js +51 -4
- package/dist/commands/doctor.js +19 -4
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +89 -3
- package/dist/commands/prove.d.ts +48 -1
- package/dist/commands/prove.d.ts.map +1 -1
- package/dist/commands/prove.js +293 -8
- package/dist/commands/report.d.ts +1 -0
- package/dist/commands/report.d.ts.map +1 -1
- package/dist/commands/report.js +70 -5
- package/dist/commands/signoff.d.ts.map +1 -1
- package/dist/commands/signoff.js +20 -1
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +20 -4
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +16 -5
- package/dist/diagnostics.js +1 -1
- package/dist/engine.js +1 -1
- package/dist/extractors/markdown.d.ts +3 -1
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +17 -0
- package/dist/operations/bootstrap/candidate-helpers.d.ts +3 -0
- package/dist/operations/bootstrap/candidate-helpers.d.ts.map +1 -1
- package/dist/operations/bootstrap/candidate-helpers.js +4 -0
- package/dist/operations/migration/evidence-floor.d.ts +41 -0
- package/dist/operations/migration/evidence-floor.d.ts.map +1 -0
- package/dist/operations/migration/evidence-floor.js +230 -0
- package/dist/operations/migration/proof-contract-claims.d.ts +59 -0
- package/dist/operations/migration/proof-contract-claims.d.ts.map +1 -0
- package/dist/operations/migration/proof-contract-claims.js +182 -0
- package/dist/operations/migration/schema6.d.ts +6 -0
- package/dist/operations/migration/schema6.d.ts.map +1 -1
- package/dist/operations/migration/schema6.js +94 -0
- package/dist/operations/mutation/omitted-properties.d.ts +11 -0
- package/dist/operations/mutation/omitted-properties.d.ts.map +1 -0
- package/dist/operations/mutation/omitted-properties.js +76 -0
- package/dist/operations/mutation/serialization.d.ts.map +1 -1
- package/dist/operations/mutation/serialization.js +1 -0
- package/dist/operations/mutation/strict-fact.d.ts.map +1 -1
- package/dist/operations/mutation/strict-fact.js +6 -2
- package/dist/operations/mutation/symbol-granularity.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-granularity.js +5 -3
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +7 -1
- package/dist/operations/mutation/validation.d.ts +2 -0
- package/dist/operations/mutation/validation.d.ts.map +1 -1
- package/dist/operations/mutation/validation.js +4 -0
- package/dist/operations/proof/claim-bindings.d.ts +23 -0
- package/dist/operations/proof/claim-bindings.d.ts.map +1 -0
- package/dist/operations/proof/claim-bindings.js +211 -0
- package/dist/operations/proof/code-certificates.d.ts +135 -0
- package/dist/operations/proof/code-certificates.d.ts.map +1 -0
- package/dist/operations/proof/code-certificates.js +785 -0
- package/dist/operations/proof/code-scope.d.ts +11 -1
- package/dist/operations/proof/code-scope.d.ts.map +1 -1
- package/dist/operations/proof/code-scope.js +20 -2
- package/dist/operations/proof/compact-receipts.d.ts.map +1 -1
- package/dist/operations/proof/compact-receipts.js +4 -3
- package/dist/operations/proof/explain.d.ts +32 -0
- package/dist/operations/proof/explain.d.ts.map +1 -1
- package/dist/operations/proof/explain.js +91 -0
- package/dist/operations/proof/ingest-proof.d.ts +9 -1
- package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
- package/dist/operations/proof/ingest-proof.js +41 -2
- package/dist/operations/proof/receipt-compaction.d.ts +2 -0
- package/dist/operations/proof/receipt-compaction.d.ts.map +1 -1
- package/dist/operations/proof/receipt-compaction.js +6 -2
- package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/shared.js +4 -0
- package/dist/operations/semantic-advisor/strict-rules.js +3 -3
- package/dist/operations/semantic-advisor/types.d.ts +2 -1
- package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
- package/dist/plugins/approved-source-analyzers.generated.d.ts +6 -6
- package/dist/plugins/approved-source-analyzers.generated.js +6 -6
- package/dist/proof/command-test-report.d.ts +4 -3
- package/dist/proof/command-test-report.d.ts.map +1 -1
- package/dist/proof/command-test-report.js +4 -10
- package/dist/proof/evaluate.d.ts.map +1 -1
- package/dist/proof/evaluate.js +18 -2
- package/dist/proof/inspect.d.ts.map +1 -1
- package/dist/proof/inspect.js +58 -0
- package/dist/proof/producers/playwright-reporter.d.ts +3 -0
- package/dist/proof/producers/playwright-reporter.d.ts.map +1 -1
- package/dist/proof/producers/playwright-reporter.js +25 -2
- package/dist/proof/step-runner.d.ts +36 -0
- package/dist/proof/step-runner.d.ts.map +1 -0
- package/dist/proof/step-runner.js +234 -0
- package/dist/public/evidence-levels.d.ts +12 -0
- package/dist/public/evidence-levels.d.ts.map +1 -0
- package/dist/public/evidence-levels.js +19 -0
- package/dist/public/impact/coverage-depth-quality.d.ts +7 -0
- package/dist/public/impact/coverage-depth-quality.d.ts.map +1 -1
- package/dist/public/impact/coverage-depth-quality.js +59 -0
- package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
- package/dist/public/impact/full-kb-quality.js +3 -1
- package/dist/public/operations/catalog.d.ts +167 -6
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.d.ts +2 -0
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +6 -0
- package/dist/public/operations/evidence-floor.d.ts +48 -0
- package/dist/public/operations/evidence-floor.d.ts.map +1 -0
- package/dist/public/operations/evidence-floor.js +143 -0
- package/dist/public/operations/migration-plan.d.ts +19 -5
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +22 -5
- package/dist/public/operations/node-ports.d.ts.map +1 -1
- package/dist/public/operations/node-ports.js +4 -1
- package/dist/public/operations/proof-contract-claims.d.ts +26 -0
- package/dist/public/operations/proof-contract-claims.d.ts.map +1 -0
- package/dist/public/operations/proof-contract-claims.js +71 -0
- package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
- package/dist/public/operations/specs/check-rules.generated.js +3 -1
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/mutation-schemas.d.ts +157 -5
- package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
- package/dist/public/operations/specs/mutation-schemas.js +15 -2
- package/dist/public/operations/specs/proof.d.ts +9 -0
- package/dist/public/operations/specs/proof.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.js +19 -10
- package/dist/public/operations/specs/upsert.d.ts +157 -5
- package/dist/public/operations/specs/upsert.d.ts.map +1 -1
- package/dist/public/proof-certificates.d.ts +89 -0
- package/dist/public/proof-certificates.d.ts.map +1 -0
- package/dist/public/proof-certificates.js +207 -0
- package/dist/public/proof-protocol.d.ts +126 -3
- package/dist/public/proof-protocol.d.ts.map +1 -1
- package/dist/public/proof-protocol.js +239 -3
- package/dist/public/proof-receipt.d.ts +89 -6
- package/dist/public/proof-receipt.d.ts.map +1 -1
- package/dist/public/proof-receipt.js +83 -7
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +18 -1
- package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/dist/public/skills/kibi-commit-review/SKILL.md +1 -1
- package/dist/public/skills/kibi-commit-review/resources/staged-gate.md +6 -1
- package/dist/public/skills/kibi-decisions/SKILL.md +10 -2
- package/dist/public/skills/kibi-decisions/resources/decision-records.md +6 -1
- package/dist/public/skills/kibi-freshness/SKILL.md +5 -1
- package/dist/public/skills/kibi-traceability/SKILL.md +11 -1
- package/dist/public/skills/kibi-usage/SKILL.md +6 -4
- package/dist/public/skills/kibi-usage/resources/proof.md +124 -19
- package/dist/public/skills/kibi-usage/resources/workflows.md +4 -4
- package/dist/report/html-report.css +38 -1
- package/dist/report/html-report.d.ts +8 -0
- package/dist/report/html-report.d.ts.map +1 -1
- package/dist/report/html-report.js +141 -6
- package/dist/report/report-view.d.ts +1 -1
- package/dist/report/report-view.d.ts.map +1 -1
- package/dist/report/report-view.js +7 -0
- package/dist/schemas/entity.schema.json +212 -4
- package/dist/search-answer.d.ts.map +1 -1
- package/dist/search-answer.js +1 -0
- package/dist/signoff/html.d.ts.map +1 -1
- package/dist/signoff/html.js +3 -0
- package/dist/signoff/markdown.d.ts.map +1 -1
- package/dist/signoff/markdown.js +3 -0
- package/dist/signoff/model.d.ts +20 -0
- package/dist/signoff/model.d.ts.map +1 -1
- package/dist/signoff/model.js +20 -1
- package/dist/traceability/impact-knowledge.d.ts.map +1 -1
- package/dist/traceability/impact-knowledge.js +1 -0
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +1 -0
- package/dist/types/entities.d.ts +4 -1
- package/dist/types/entities.d.ts.map +1 -1
- package/dist/utils/merge-driver-setup.d.ts +8 -1
- package/dist/utils/merge-driver-setup.d.ts.map +1 -1
- package/dist/utils/merge-driver-setup.js +23 -2
- package/dist/utils/rule-registry.generated.d.ts +10 -0
- package/dist/utils/rule-registry.generated.d.ts.map +1 -1
- package/dist/utils/rule-registry.generated.js +12 -0
- package/dist/utils/schema-version.d.ts +1 -1
- package/dist/utils/schema-version.d.ts.map +1 -1
- package/dist/utils/schema-version.js +5 -1
- package/package.json +6 -2
- package/schema/entities.pl +6 -0
- package/schema/validation.pl +4 -1
- package/src/public/evidence-levels.ts +23 -0
- package/src/public/impact/coverage-depth-quality.ts +73 -0
- package/src/public/impact/full-kb-quality.ts +6 -1
- package/src/public/operations/check-executor.ts +13 -0
- package/src/public/operations/evidence-floor.ts +203 -0
- package/src/public/operations/migration-plan.ts +39 -6
- package/src/public/operations/node-ports.ts +3 -1
- package/src/public/operations/proof-contract-claims.ts +103 -0
- package/src/public/operations/specs/check-rules.generated.ts +3 -1
- package/src/public/operations/specs/mutation-schemas.ts +17 -2
- package/src/public/operations/specs/reporting.ts +20 -10
- package/src/public/proof-certificates.ts +271 -0
- package/src/public/proof-protocol-types.d.ts +17 -1
- package/src/public/proof-protocol.ts +318 -3
- package/src/public/proof-receipt.ts +127 -7
- package/src/public/schemas/entity.ts +19 -1
- package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/src/public/skills/kibi-commit-review/SKILL.md +1 -1
- package/src/public/skills/kibi-commit-review/resources/staged-gate.md +6 -1
- package/src/public/skills/kibi-decisions/SKILL.md +10 -2
- package/src/public/skills/kibi-decisions/resources/decision-records.md +6 -1
- package/src/public/skills/kibi-freshness/SKILL.md +5 -1
- package/src/public/skills/kibi-traceability/SKILL.md +11 -1
- package/src/public/skills/kibi-usage/SKILL.md +6 -4
- package/src/public/skills/kibi-usage/resources/proof.md +124 -19
- package/src/public/skills/kibi-usage/resources/workflows.md +4 -4
- package/src/schemas/entity.schema.json +212 -4
- package/dist/commands/proof-impact.d.ts +0 -7
- package/dist/commands/proof-impact.d.ts.map +0 -1
- package/dist/commands/proof-impact.js +0 -39
- package/dist/operations/proof/baseline-diff.d.ts +0 -41
- package/dist/operations/proof/baseline-diff.d.ts.map +0 -1
- package/dist/operations/proof/baseline-diff.js +0 -153
- package/dist/operations/proof/impact.d.ts +0 -51
- package/dist/operations/proof/impact.d.ts.map +0 -1
- package/dist/operations/proof/impact.js +0 -138
|
@@ -133,7 +133,7 @@ export async function perContractTestBindings(context) {
|
|
|
133
133
|
if (currentProofBindingMode() !== "per_contract")
|
|
134
134
|
return null;
|
|
135
135
|
const { loadEntities } = await import("../discovery-entities.js");
|
|
136
|
-
const {
|
|
136
|
+
const { currentReceiptBindingHashes, loadCoveredBySymbolsByTest } = await import("../../../operations/proof/code-scope.js");
|
|
137
137
|
// Only the contract, scoped code, and authored source feed the binding
|
|
138
138
|
// hash. The paged projection never materializes receipt histories, which
|
|
139
139
|
// grow without bound and previously overflowed the bounded Prolog output
|
|
@@ -161,14 +161,18 @@ export async function perContractTestBindings(context) {
|
|
|
161
161
|
continue;
|
|
162
162
|
// The same computation ingest uses to write binding_hash. Unreadable or
|
|
163
163
|
// non-markdown documents keep strict snapshot semantics for that test.
|
|
164
|
-
const
|
|
164
|
+
const bindings = await currentReceiptBindingHashes({
|
|
165
165
|
workspaceRoot: context.workspaceRoot,
|
|
166
166
|
readFile: (absolute) => fs.readFile(absolute),
|
|
167
167
|
test,
|
|
168
168
|
coveredBySymbols: coveredBy.get(testId) ?? [],
|
|
169
169
|
});
|
|
170
|
-
if (
|
|
171
|
-
|
|
170
|
+
if (bindings !== undefined) {
|
|
171
|
+
// One hash, or the current and the earlier binding a receipt may carry.
|
|
172
|
+
const value = bindings.length === 1
|
|
173
|
+
? toPrologAtom(bindings[0])
|
|
174
|
+
: `[${bindings.map(toPrologAtom).join(", ")}]`;
|
|
175
|
+
entries.push(`${toPrologAtom(testId)}: ${value}`);
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
178
|
if (entries.length === 0)
|
|
@@ -207,14 +211,19 @@ export async function executeCoverage(input, context) {
|
|
|
207
211
|
: "unknown";
|
|
208
212
|
const checkedAt = context.clock().toISOString();
|
|
209
213
|
const statuses = (input.statuses ?? []).map((status) => validateCoverageStatus(status));
|
|
210
|
-
|
|
214
|
+
// A status filter judges receipts with the same per-contract binding as
|
|
215
|
+
// the unfiltered report; otherwise its rows and summary disagree with
|
|
216
|
+
// every other proof view.
|
|
217
|
+
const bindingsDict = (input.by ?? "req") === "req"
|
|
211
218
|
? await perContractTestBindings(context)
|
|
212
219
|
: null;
|
|
213
|
-
const goalFor = (limit, offset) => bindingsDict !== null
|
|
214
|
-
? `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)`
|
|
215
|
-
:
|
|
216
|
-
? `discovery:coverage_report_json('${input.by ?? "req"}', ${toPrologList(input.tags)}, ${input.includePassing ?? false}, ${
|
|
217
|
-
:
|
|
220
|
+
const goalFor = (limit, offset) => bindingsDict !== null && statuses.length > 0
|
|
221
|
+
? `discovery:coverage_report_json('${input.by ?? "req"}', ${toPrologList(input.tags)}, ${input.includePassing ?? false}, ${toPrologList(statuses)}, per_contract, ${bindingsDict}, ${input.includeTransitive ?? true}, ${limit}, ${offset}, ${toPrologAtom(codeSnapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}, JsonString)`
|
|
222
|
+
: bindingsDict !== null
|
|
223
|
+
? `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)`
|
|
224
|
+
: statuses.length > 0
|
|
225
|
+
? `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)`
|
|
226
|
+
: `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)`;
|
|
218
227
|
const payload = await readCoveragePages(requireProlog(context), goalFor, input.limit ?? 100, input.offset ?? 0);
|
|
219
228
|
const rows = (input.by ?? "req") === "req"
|
|
220
229
|
? await addCoordinateRepairEvidence(payload.rows, context)
|
|
@@ -140,6 +140,11 @@ export declare const upsertSpec: {
|
|
|
140
140
|
readonly enum: readonly ["firm", "default", "exploratory"];
|
|
141
141
|
readonly description: "req/adr only. firm = a human stated it as a rule (needs origin.ref to their words); its title, meaning or firmness changes only with new human words (a new origin.ref) or by supersede. default = delegated or agent choice; exploratory = flagged guess. Absent = default if an agent wrote it, else firm.";
|
|
142
142
|
};
|
|
143
|
+
readonly required_evidence: {
|
|
144
|
+
readonly type: "string";
|
|
145
|
+
readonly enum: readonly ["proved", "tested", "asserted", "unverified"];
|
|
146
|
+
readonly description: "Requirement-only. Weakest evidence level it accepts; the evidence-floor rule fails below it. Absent = tested if firm, else unverified.";
|
|
147
|
+
};
|
|
143
148
|
readonly rationale: {
|
|
144
149
|
readonly type: "string";
|
|
145
150
|
readonly minLength: 1;
|
|
@@ -219,7 +224,87 @@ export declare const upsertSpec: {
|
|
|
219
224
|
};
|
|
220
225
|
readonly success_policy: {
|
|
221
226
|
readonly type: "string";
|
|
222
|
-
readonly enum: readonly ["all_required_first_attempt"];
|
|
227
|
+
readonly enum: readonly ["all_required_first_attempt", "own_results_first_attempt"];
|
|
228
|
+
};
|
|
229
|
+
readonly kind: {
|
|
230
|
+
readonly type: "string";
|
|
231
|
+
readonly enum: readonly ["e2e", "example", "property", "exhaustive", "typecheck", "arithmetic", "smt"];
|
|
232
|
+
readonly description: "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.";
|
|
233
|
+
};
|
|
234
|
+
readonly domain_source: {
|
|
235
|
+
readonly type: "object";
|
|
236
|
+
readonly description: "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.";
|
|
237
|
+
readonly required: readonly ["symbol_id"];
|
|
238
|
+
readonly properties: {
|
|
239
|
+
readonly symbol_id: {
|
|
240
|
+
readonly type: "string";
|
|
241
|
+
readonly minLength: 1;
|
|
242
|
+
};
|
|
243
|
+
readonly export: {
|
|
244
|
+
readonly type: "string";
|
|
245
|
+
readonly minLength: 1;
|
|
246
|
+
readonly description: "The name the domain is exported under in the symbol's source file, when the symbol's title is not that name.";
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
readonly additionalProperties: false;
|
|
250
|
+
};
|
|
251
|
+
readonly witness: {
|
|
252
|
+
readonly type: "object";
|
|
253
|
+
readonly description: "typecheck: the repository-relative witness file Kibi compiles (an exhaustive switch ending in never, or a mapped type over the domain).";
|
|
254
|
+
readonly required: readonly ["path"];
|
|
255
|
+
readonly properties: {
|
|
256
|
+
readonly path: {
|
|
257
|
+
readonly type: "string";
|
|
258
|
+
readonly minLength: 1;
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
readonly additionalProperties: false;
|
|
262
|
+
};
|
|
263
|
+
readonly proves: {
|
|
264
|
+
readonly type: "array";
|
|
265
|
+
readonly description: "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.";
|
|
266
|
+
readonly minItems: 1;
|
|
267
|
+
readonly items: {
|
|
268
|
+
readonly type: "string";
|
|
269
|
+
readonly pattern: "^CLAIM-";
|
|
270
|
+
};
|
|
271
|
+
readonly uniqueItems: true;
|
|
272
|
+
};
|
|
273
|
+
readonly value_source: {
|
|
274
|
+
readonly type: "array";
|
|
275
|
+
readonly description: "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.";
|
|
276
|
+
readonly minItems: 1;
|
|
277
|
+
readonly items: {
|
|
278
|
+
readonly type: "object";
|
|
279
|
+
readonly required: readonly ["symbol_id"];
|
|
280
|
+
readonly properties: {
|
|
281
|
+
readonly symbol_id: {
|
|
282
|
+
readonly type: "string";
|
|
283
|
+
readonly minLength: 1;
|
|
284
|
+
};
|
|
285
|
+
readonly export: {
|
|
286
|
+
readonly type: "string";
|
|
287
|
+
readonly minLength: 1;
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
readonly additionalProperties: false;
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
readonly constraint_source: {
|
|
294
|
+
readonly type: "object";
|
|
295
|
+
readonly description: "arithmetic and smt: the typed numeric property_value facts the certificate's assumptions come from. Kibi checks the assumptions (or the query's assertions) are exactly these facts' constraints, so the proof is about the knowledge base, not numbers the test chose.";
|
|
296
|
+
readonly required: readonly ["fact_ids"];
|
|
297
|
+
readonly properties: {
|
|
298
|
+
readonly fact_ids: {
|
|
299
|
+
readonly type: "array";
|
|
300
|
+
readonly minItems: 1;
|
|
301
|
+
readonly items: {
|
|
302
|
+
readonly type: "string";
|
|
303
|
+
readonly minLength: 1;
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
readonly additionalProperties: false;
|
|
223
308
|
};
|
|
224
309
|
};
|
|
225
310
|
readonly additionalProperties: false;
|
|
@@ -267,12 +352,12 @@ export declare const upsertSpec: {
|
|
|
267
352
|
readonly maxItems: 50;
|
|
268
353
|
readonly items: {
|
|
269
354
|
readonly type: "object";
|
|
270
|
-
readonly description: "Kibi-derived kibi.proof-receipt.v1. Receipts are never caller-authored; kb_ingest_proof derives them from kibi.proof-run.v1 artifacts.";
|
|
355
|
+
readonly description: "Kibi-derived kibi.proof-receipt.v2 (kibi.proof-receipt.v1 entries are still read). Receipts are never caller-authored; kb_ingest_proof derives them from kibi.proof-run.v1 artifacts.";
|
|
271
356
|
readonly required: readonly ["version", "receipt_id", "test_id", "scope", "outcome", "code_snapshot", "environment_hash", "started_at", "finished_at", "artifact_digest", "contract_hash", "fingerprint", "fingerprint_components", "integration_id", "producer", "command_argv", "run_outcome", "proof_results"];
|
|
272
357
|
readonly properties: {
|
|
273
358
|
readonly version: {
|
|
274
359
|
readonly type: "string";
|
|
275
|
-
readonly
|
|
360
|
+
readonly enum: readonly ["kibi.proof-receipt.v1", "kibi.proof-receipt.v2"];
|
|
276
361
|
};
|
|
277
362
|
readonly receipt_id: {
|
|
278
363
|
readonly type: "string";
|
|
@@ -454,6 +539,63 @@ export declare const upsertSpec: {
|
|
|
454
539
|
readonly additionalProperties: false;
|
|
455
540
|
};
|
|
456
541
|
};
|
|
542
|
+
readonly kind: {
|
|
543
|
+
readonly type: "string";
|
|
544
|
+
readonly enum: readonly ["e2e", "example", "property", "exhaustive", "typecheck", "arithmetic", "smt"];
|
|
545
|
+
};
|
|
546
|
+
readonly evidence_level: {
|
|
547
|
+
readonly type: "string";
|
|
548
|
+
readonly enum: readonly ["proved", "tested"];
|
|
549
|
+
};
|
|
550
|
+
readonly certificate: {
|
|
551
|
+
readonly type: "object";
|
|
552
|
+
};
|
|
553
|
+
readonly certificate_digest: {
|
|
554
|
+
readonly type: "string";
|
|
555
|
+
readonly pattern: "^[a-f0-9]{64}$";
|
|
556
|
+
};
|
|
557
|
+
readonly certificate_rejection: {
|
|
558
|
+
readonly type: "string";
|
|
559
|
+
readonly minLength: 1;
|
|
560
|
+
};
|
|
561
|
+
readonly claim_bindings: {
|
|
562
|
+
readonly type: "array";
|
|
563
|
+
readonly description: "Certificate kinds: how the certificate binds to each claim its contract names in proves, judged at ingest. derived means Kibi derived the certificate's obligation from the claim's grounding fact and the certificate satisfies it; attested means no derivation exists and the author vouches for it (never proved); rejected names why.";
|
|
564
|
+
readonly items: {
|
|
565
|
+
readonly type: "object";
|
|
566
|
+
readonly required: readonly ["claim_key", "binding"];
|
|
567
|
+
readonly properties: {
|
|
568
|
+
readonly claim_key: {
|
|
569
|
+
readonly type: "string";
|
|
570
|
+
readonly minLength: 1;
|
|
571
|
+
};
|
|
572
|
+
readonly requirement_id: {
|
|
573
|
+
readonly type: "string";
|
|
574
|
+
readonly minLength: 1;
|
|
575
|
+
};
|
|
576
|
+
readonly fact_id: {
|
|
577
|
+
readonly type: "string";
|
|
578
|
+
readonly minLength: 1;
|
|
579
|
+
};
|
|
580
|
+
readonly binding: {
|
|
581
|
+
readonly type: "string";
|
|
582
|
+
readonly enum: readonly ["derived", "attested", "rejected"];
|
|
583
|
+
};
|
|
584
|
+
readonly scope: {
|
|
585
|
+
readonly type: "string";
|
|
586
|
+
readonly enum: readonly ["code", "intent"];
|
|
587
|
+
};
|
|
588
|
+
readonly obligation: {
|
|
589
|
+
readonly type: "object";
|
|
590
|
+
};
|
|
591
|
+
readonly reason: {
|
|
592
|
+
readonly type: "string";
|
|
593
|
+
readonly minLength: 1;
|
|
594
|
+
};
|
|
595
|
+
};
|
|
596
|
+
readonly additionalProperties: false;
|
|
597
|
+
};
|
|
598
|
+
};
|
|
457
599
|
readonly gaps: {
|
|
458
600
|
readonly type: "array";
|
|
459
601
|
readonly items: {
|
|
@@ -501,8 +643,8 @@ export declare const upsertSpec: {
|
|
|
501
643
|
};
|
|
502
644
|
readonly value_type: {
|
|
503
645
|
readonly type: "string";
|
|
504
|
-
readonly enum: readonly ["string", "int", "number", "bool"];
|
|
505
|
-
readonly description: "Optional typed value discriminator for property_value facts. Pair with exactly one value_string, value_int, value_number, or
|
|
646
|
+
readonly enum: readonly ["string", "int", "number", "bool", "set"];
|
|
647
|
+
readonly description: "Optional typed value discriminator for property_value facts. Pair with exactly one value_string, value_int, value_number, value_bool, or value_set (set: an exact list of allowed values, operator eq or neq); do not use generic value.";
|
|
506
648
|
};
|
|
507
649
|
readonly value_string: {
|
|
508
650
|
readonly type: "string";
|
|
@@ -520,6 +662,16 @@ export declare const upsertSpec: {
|
|
|
520
662
|
readonly type: "boolean";
|
|
521
663
|
readonly description: "Optional boolean value for property_value facts.";
|
|
522
664
|
};
|
|
665
|
+
readonly value_set: {
|
|
666
|
+
readonly type: "array";
|
|
667
|
+
readonly items: {
|
|
668
|
+
readonly type: "string";
|
|
669
|
+
readonly minLength: 1;
|
|
670
|
+
};
|
|
671
|
+
readonly minItems: 1;
|
|
672
|
+
readonly uniqueItems: true;
|
|
673
|
+
readonly description: "Optional exact set of values for property_value facts with value_type set, such as the members a domain must have.";
|
|
674
|
+
};
|
|
523
675
|
readonly unit: {
|
|
524
676
|
readonly type: "string";
|
|
525
677
|
readonly description: "Optional unit for numeric property_value facts.";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../../../../src/public/operations/specs/upsert.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAC;AAQvE,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../../../../src/public/operations/specs/upsert.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAC;AAQvE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DuC,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CODE_CERTIFICATE_VERSION } from "./proof-protocol.js";
|
|
2
|
+
/** Where `kibi prove` asks a run to leave its certificates. */
|
|
3
|
+
export declare const PROOF_CERTIFICATES_ENV = "KIBI_PROOF_CERTIFICATES";
|
|
4
|
+
export type CodeCertificate = Readonly<Record<string, unknown>> & Readonly<{
|
|
5
|
+
version: typeof CODE_CERTIFICATE_VERSION;
|
|
6
|
+
kind: "exhaustive" | "typecheck" | "arithmetic" | "smt";
|
|
7
|
+
test_id: string;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* The canonical JSON of a certificate, byte for byte what
|
|
11
|
+
* code_certificates.pl hashes: keys sorted by code point, no whitespace,
|
|
12
|
+
* non-ASCII and control characters as \u escapes of UTF-16 code units.
|
|
13
|
+
*/
|
|
14
|
+
export declare function canonicalCertificateJson(value: unknown): string;
|
|
15
|
+
/** The sha256 a receipt records for its certificate. */
|
|
16
|
+
export declare function certificateDigest(certificate: unknown): string;
|
|
17
|
+
export type ExhaustiveResult = Readonly<{
|
|
18
|
+
member: string;
|
|
19
|
+
outcome: "passed" | "failed";
|
|
20
|
+
message?: string;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Run `check` on every member of a finite domain and record each outcome.
|
|
24
|
+
* `members` must be the domain as the code defines it (import it from its
|
|
25
|
+
* source, never restate it): Kibi rejects the certificate unless the proof
|
|
26
|
+
* file imports the contract's domain_source symbol.
|
|
27
|
+
*/
|
|
28
|
+
export declare function exhaustiveCertificate<T>(input: {
|
|
29
|
+
testId: string;
|
|
30
|
+
domain: Readonly<{
|
|
31
|
+
symbolId: string;
|
|
32
|
+
members: Iterable<T>;
|
|
33
|
+
}>;
|
|
34
|
+
key?: (member: T) => string;
|
|
35
|
+
check: (member: T) => void | Promise<void>;
|
|
36
|
+
}): Promise<CodeCertificate>;
|
|
37
|
+
/** The members an exhaustive certificate records as failed. */
|
|
38
|
+
export declare function failedMembers(certificate: CodeCertificate): string[];
|
|
39
|
+
/**
|
|
40
|
+
* A type checker accepted `witnessPath`, a file that fails to compile when a
|
|
41
|
+
* member of the domain goes unhandled (an exhaustive switch ending in a
|
|
42
|
+
* `never` assignment, or a mapped type over the domain).
|
|
43
|
+
*/
|
|
44
|
+
export declare function typecheckCertificate(input: {
|
|
45
|
+
testId: string;
|
|
46
|
+
domainSymbolId: string;
|
|
47
|
+
witnessPath: string;
|
|
48
|
+
workspaceRoot?: string;
|
|
49
|
+
compiler: Readonly<{
|
|
50
|
+
name: string;
|
|
51
|
+
version: string;
|
|
52
|
+
}>;
|
|
53
|
+
accepted: boolean;
|
|
54
|
+
}): CodeCertificate;
|
|
55
|
+
export type ArithmeticConstraint = Readonly<{
|
|
56
|
+
operator: "eq" | "neq" | "lt" | "lte" | "gt" | "gte";
|
|
57
|
+
value: number;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* A claim about one numeric variable that Kibi decides exactly: the
|
|
61
|
+
* assumptions must be satisfiable and must entail the claim for every value
|
|
62
|
+
* of the variable, not only the values a test happened to try.
|
|
63
|
+
*/
|
|
64
|
+
export declare function arithmeticCertificate(input: {
|
|
65
|
+
testId: string;
|
|
66
|
+
variable: string;
|
|
67
|
+
valueType: "int" | "real";
|
|
68
|
+
assumptions: readonly ArithmeticConstraint[];
|
|
69
|
+
claim: ArithmeticConstraint;
|
|
70
|
+
}): CodeCertificate;
|
|
71
|
+
/**
|
|
72
|
+
* An SMT-LIB query whose unsat answer proves the claim (the query asserts
|
|
73
|
+
* the claim's negation). Kibi re-runs the named solver on the query.
|
|
74
|
+
*/
|
|
75
|
+
export declare function smtCertificate(input: {
|
|
76
|
+
testId: string;
|
|
77
|
+
solver: Readonly<{
|
|
78
|
+
name: "z3" | "cvc5";
|
|
79
|
+
version: string;
|
|
80
|
+
}>;
|
|
81
|
+
query: string;
|
|
82
|
+
result: string;
|
|
83
|
+
}): CodeCertificate;
|
|
84
|
+
/**
|
|
85
|
+
* Leave the certificate where `kibi prove` reads it. Returns the file written,
|
|
86
|
+
* or null outside a `kibi prove` run.
|
|
87
|
+
*/
|
|
88
|
+
export declare function writeCodeCertificate(certificate: CodeCertificate, env?: NodeJS.ProcessEnv): string | null;
|
|
89
|
+
//# sourceMappingURL=proof-certificates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof-certificates.d.ts","sourceRoot":"","sources":["../../src/public/proof-certificates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Builders for the kibi.code-certificate.v1 a certificate-kind proof contract
|
|
20
|
+
* leaves for `kibi prove`. A test builds its certificate and writes it with
|
|
21
|
+
* writeCodeCertificate; outside `kibi prove` the write is a no-op, so the same
|
|
22
|
+
* test runs unchanged in an ordinary test run. Kibi re-checks every
|
|
23
|
+
* certificate at ingest and again whenever it reports evidence, so a
|
|
24
|
+
* certificate only records what the test did; it never vouches for itself.
|
|
25
|
+
*/
|
|
26
|
+
import { createHash } from "node:crypto";
|
|
27
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import { CODE_CERTIFICATE_VERSION } from "./proof-protocol.js";
|
|
30
|
+
/** Where `kibi prove` asks a run to leave its certificates. */
|
|
31
|
+
// implements REQ-kibi-code-proof-kinds
|
|
32
|
+
export const PROOF_CERTIFICATES_ENV = "KIBI_PROOF_CERTIFICATES";
|
|
33
|
+
function compareCodePoints(left, right) {
|
|
34
|
+
const a = Array.from(left, (char) => char.codePointAt(0) ?? 0);
|
|
35
|
+
const b = Array.from(right, (char) => char.codePointAt(0) ?? 0);
|
|
36
|
+
for (let index = 0; index < Math.min(a.length, b.length); index++) {
|
|
37
|
+
const difference = (a[index] ?? 0) - (b[index] ?? 0);
|
|
38
|
+
if (difference !== 0)
|
|
39
|
+
return difference;
|
|
40
|
+
}
|
|
41
|
+
return a.length - b.length;
|
|
42
|
+
}
|
|
43
|
+
const JSON_ESCAPES = {
|
|
44
|
+
'"': '\\"',
|
|
45
|
+
"\\": "\\\\",
|
|
46
|
+
"\b": "\\b",
|
|
47
|
+
"\f": "\\f",
|
|
48
|
+
"\n": "\\n",
|
|
49
|
+
"\r": "\\r",
|
|
50
|
+
"\t": "\\t",
|
|
51
|
+
};
|
|
52
|
+
function canonicalString(value) {
|
|
53
|
+
let out = '"';
|
|
54
|
+
for (let index = 0; index < value.length; index++) {
|
|
55
|
+
const char = value[index];
|
|
56
|
+
const unit = value.charCodeAt(index);
|
|
57
|
+
const escaped = JSON_ESCAPES[char];
|
|
58
|
+
if (escaped !== undefined)
|
|
59
|
+
out += escaped;
|
|
60
|
+
else if (unit >= 0x20 && unit <= 0x7e)
|
|
61
|
+
out += char;
|
|
62
|
+
else
|
|
63
|
+
out += `\\u${unit.toString(16).padStart(4, "0")}`;
|
|
64
|
+
}
|
|
65
|
+
return `${out}"`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The canonical JSON of a certificate, byte for byte what
|
|
69
|
+
* code_certificates.pl hashes: keys sorted by code point, no whitespace,
|
|
70
|
+
* non-ASCII and control characters as \u escapes of UTF-16 code units.
|
|
71
|
+
*/
|
|
72
|
+
// implements REQ-kibi-code-proof-kinds
|
|
73
|
+
export function canonicalCertificateJson(value) {
|
|
74
|
+
if (value === null || value === undefined)
|
|
75
|
+
return "null";
|
|
76
|
+
if (Array.isArray(value))
|
|
77
|
+
return `[${value.map(canonicalCertificateJson).join(",")}]`;
|
|
78
|
+
if (typeof value === "object")
|
|
79
|
+
return `{${Object.keys(value)
|
|
80
|
+
.sort(compareCodePoints)
|
|
81
|
+
.map((key) => `${canonicalString(key)}:${canonicalCertificateJson(value[key])}`)
|
|
82
|
+
.join(",")}}`;
|
|
83
|
+
if (typeof value === "string")
|
|
84
|
+
return canonicalString(value);
|
|
85
|
+
return JSON.stringify(value);
|
|
86
|
+
}
|
|
87
|
+
/** The sha256 a receipt records for its certificate. */
|
|
88
|
+
// implements REQ-kibi-code-proof-kinds
|
|
89
|
+
export function certificateDigest(certificate) {
|
|
90
|
+
return createHash("sha256")
|
|
91
|
+
.update(canonicalCertificateJson(certificate), "utf8")
|
|
92
|
+
.digest("hex");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Run `check` on every member of a finite domain and record each outcome.
|
|
96
|
+
* `members` must be the domain as the code defines it (import it from its
|
|
97
|
+
* source, never restate it): Kibi rejects the certificate unless the proof
|
|
98
|
+
* file imports the contract's domain_source symbol.
|
|
99
|
+
*/
|
|
100
|
+
// implements REQ-kibi-code-proof-kinds
|
|
101
|
+
export async function exhaustiveCertificate(input) {
|
|
102
|
+
const key = input.key ?? ((member) => String(member));
|
|
103
|
+
const members = [...input.domain.members];
|
|
104
|
+
const results = [];
|
|
105
|
+
for (const member of members) {
|
|
106
|
+
try {
|
|
107
|
+
await input.check(member);
|
|
108
|
+
results.push({ member: key(member), outcome: "passed" });
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
results.push({
|
|
112
|
+
member: key(member),
|
|
113
|
+
outcome: "failed",
|
|
114
|
+
message: error instanceof Error ? error.message : String(error),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
120
|
+
kind: "exhaustive",
|
|
121
|
+
test_id: input.testId,
|
|
122
|
+
domain: {
|
|
123
|
+
symbol_id: input.domain.symbolId,
|
|
124
|
+
members: members.map(key),
|
|
125
|
+
},
|
|
126
|
+
results,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** The members an exhaustive certificate records as failed. */
|
|
130
|
+
// implements REQ-kibi-code-proof-kinds
|
|
131
|
+
export function failedMembers(certificate) {
|
|
132
|
+
const results = Array.isArray(certificate.results)
|
|
133
|
+
? certificate.results
|
|
134
|
+
: [];
|
|
135
|
+
return results
|
|
136
|
+
.filter((result) => result.outcome !== "passed")
|
|
137
|
+
.map((result) => result.message ? `${result.member}: ${result.message}` : result.member);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A type checker accepted `witnessPath`, a file that fails to compile when a
|
|
141
|
+
* member of the domain goes unhandled (an exhaustive switch ending in a
|
|
142
|
+
* `never` assignment, or a mapped type over the domain).
|
|
143
|
+
*/
|
|
144
|
+
// implements REQ-kibi-code-proof-kinds
|
|
145
|
+
export function typecheckCertificate(input) {
|
|
146
|
+
const absolute = path.resolve(input.workspaceRoot ?? process.cwd(), input.witnessPath);
|
|
147
|
+
return {
|
|
148
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
149
|
+
kind: "typecheck",
|
|
150
|
+
test_id: input.testId,
|
|
151
|
+
domain: { symbol_id: input.domainSymbolId },
|
|
152
|
+
witness: {
|
|
153
|
+
path: input.witnessPath,
|
|
154
|
+
sha256: createHash("sha256")
|
|
155
|
+
.update(readFileSync(absolute, "utf8"))
|
|
156
|
+
.digest("hex"),
|
|
157
|
+
},
|
|
158
|
+
compiler: { ...input.compiler },
|
|
159
|
+
outcome: input.accepted ? "passed" : "failed",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* A claim about one numeric variable that Kibi decides exactly: the
|
|
164
|
+
* assumptions must be satisfiable and must entail the claim for every value
|
|
165
|
+
* of the variable, not only the values a test happened to try.
|
|
166
|
+
*/
|
|
167
|
+
// implements REQ-kibi-code-proof-kinds
|
|
168
|
+
export function arithmeticCertificate(input) {
|
|
169
|
+
return {
|
|
170
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
171
|
+
kind: "arithmetic",
|
|
172
|
+
test_id: input.testId,
|
|
173
|
+
variable: input.variable,
|
|
174
|
+
value_type: input.valueType,
|
|
175
|
+
assumptions: input.assumptions.map((row) => ({ ...row })),
|
|
176
|
+
claim: { ...input.claim },
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* An SMT-LIB query whose unsat answer proves the claim (the query asserts
|
|
181
|
+
* the claim's negation). Kibi re-runs the named solver on the query.
|
|
182
|
+
*/
|
|
183
|
+
// implements REQ-kibi-code-proof-kinds
|
|
184
|
+
export function smtCertificate(input) {
|
|
185
|
+
return {
|
|
186
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
187
|
+
kind: "smt",
|
|
188
|
+
test_id: input.testId,
|
|
189
|
+
solver: { ...input.solver },
|
|
190
|
+
query: input.query,
|
|
191
|
+
result: input.result,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Leave the certificate where `kibi prove` reads it. Returns the file written,
|
|
196
|
+
* or null outside a `kibi prove` run.
|
|
197
|
+
*/
|
|
198
|
+
// implements REQ-kibi-code-proof-kinds
|
|
199
|
+
export function writeCodeCertificate(certificate, env = process.env) {
|
|
200
|
+
const directory = env[PROOF_CERTIFICATES_ENV];
|
|
201
|
+
if (directory === undefined || directory.trim() === "")
|
|
202
|
+
return null;
|
|
203
|
+
mkdirSync(directory, { recursive: true });
|
|
204
|
+
const file = path.join(directory, `${certificate.test_id.replace(/[^A-Za-z0-9._-]/g, "_")}.json`);
|
|
205
|
+
writeFileSync(file, `${JSON.stringify(certificate, null, 2)}\n`, "utf8");
|
|
206
|
+
return file;
|
|
207
|
+
}
|