kibi-cli 2.31.1 → 2.35.1
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/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -1
- package/dist/commands/check-generated.d.ts +5 -1
- package/dist/commands/check-generated.d.ts.map +1 -1
- package/dist/commands/check-generated.js +24 -35
- 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/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +8 -5
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +109 -5
- 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 +10 -1
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +106 -17
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +16 -5
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +19 -0
- package/dist/diagnostics.js +1 -1
- package/dist/engine.js +1 -1
- package/dist/extractors/markdown.d.ts +4 -2
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +17 -0
- package/dist/extractors/symbol-coordinates.js +2 -2
- package/dist/extractors/symbols-coordinator.d.ts +2 -4
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +1 -2
- 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 +72 -0
- package/dist/operations/migration/proof-contract-claims.d.ts.map +1 -0
- package/dist/operations/migration/proof-contract-claims.js +215 -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 +92 -0
- package/dist/operations/migration/symbol-coordinates-refresh.d.ts +3 -2
- package/dist/operations/migration/symbol-coordinates-refresh.d.ts.map +1 -1
- package/dist/operations/migration/symbol-coordinates-refresh.js +4 -3
- package/dist/operations/migration/untrack-symbol-coordinates.d.ts +23 -0
- package/dist/operations/migration/untrack-symbol-coordinates.d.ts.map +1 -0
- package/dist/operations/migration/untrack-symbol-coordinates.js +67 -0
- package/dist/operations/modeling/requirement-modeler.d.ts.map +1 -1
- package/dist/operations/modeling/requirement-modeler.js +48 -1
- package/dist/operations/modeling/requirement-types.d.ts +2 -1
- package/dist/operations/modeling/requirement-types.d.ts.map +1 -1
- package/dist/operations/modeling/requirement-utils.d.ts +1 -1
- package/dist/operations/modeling/requirement-utils.d.ts.map +1 -1
- package/dist/operations/modeling/requirement-utils.js +8 -1
- 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/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +39 -20
- 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 +41 -0
- package/dist/operations/proof/claim-bindings.d.ts.map +1 -0
- package/dist/operations/proof/claim-bindings.js +364 -0
- package/dist/operations/proof/code-certificates.d.ts +155 -0
- package/dist/operations/proof/code-certificates.d.ts.map +1 -0
- package/dist/operations/proof/code-certificates.js +894 -0
- package/dist/operations/proof/code-models.d.ts +96 -0
- package/dist/operations/proof/code-models.d.ts.map +1 -0
- package/dist/operations/proof/code-models.js +601 -0
- package/dist/operations/proof/code-runner.d.ts +40 -0
- package/dist/operations/proof/code-runner.d.ts.map +1 -0
- package/dist/operations/proof/code-runner.js +164 -0
- package/dist/operations/proof/code-scope.d.ts +14 -1
- package/dist/operations/proof/code-scope.d.ts.map +1 -1
- package/dist/operations/proof/code-scope.js +44 -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 +42 -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/proof/regular-language.d.ts +89 -0
- package/dist/operations/proof/regular-language.d.ts.map +1 -0
- package/dist/operations/proof/regular-language.js +943 -0
- package/dist/operations/proof/source-code-readers.d.ts +12 -0
- package/dist/operations/proof/source-code-readers.d.ts.map +1 -0
- package/dist/operations/proof/source-code-readers.js +699 -0
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +2 -1
- 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-count-rules.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/strict-count-rules.js +8 -2
- package/dist/operations/semantic-advisor/strict-rules.d.ts +8 -0
- package/dist/operations/semantic-advisor/strict-rules.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/strict-rules.js +234 -4
- package/dist/operations/semantic-advisor/types.d.ts +3 -2
- 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/prolog.d.ts.map +1 -1
- package/dist/prolog.js +13 -3
- 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 +315 -11
- 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/composite.d.ts +16 -3
- package/dist/public/operations/specs/composite.d.ts.map +1 -1
- package/dist/public/operations/specs/modeling.d.ts +16 -3
- package/dist/public/operations/specs/modeling.d.ts.map +1 -1
- package/dist/public/operations/specs/modeling.js +22 -3
- package/dist/public/operations/specs/mutation-schemas.d.ts +289 -7
- package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
- package/dist/public/operations/specs/mutation-schemas.js +28 -4
- 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 +289 -7
- package/dist/public/operations/specs/upsert.d.ts.map +1 -1
- package/dist/public/proof-certificates.d.ts +163 -0
- package/dist/public/proof-certificates.d.ts.map +1 -0
- package/dist/public/proof-certificates.js +419 -0
- package/dist/public/proof-protocol.d.ts +259 -3
- package/dist/public/proof-protocol.d.ts.map +1 -1
- package/dist/public/proof-protocol.js +524 -3
- package/dist/public/proof-receipt.d.ts +90 -6
- package/dist/public/proof-receipt.d.ts.map +1 -1
- package/dist/public/proof-receipt.js +88 -7
- package/dist/public/proof-sources.d.ts +102 -0
- package/dist/public/proof-sources.d.ts.map +1 -0
- package/dist/public/proof-sources.js +1292 -0
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +39 -2
- package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/dist/public/skills/kibi-commit-review/SKILL.md +8 -8
- package/dist/public/skills/kibi-commit-review/resources/staged-gate.md +12 -7
- package/dist/public/skills/kibi-decisions/SKILL.md +13 -2
- package/dist/public/skills/kibi-decisions/resources/decision-records.md +9 -1
- package/dist/public/skills/kibi-freshness/SKILL.md +5 -1
- package/dist/public/skills/kibi-traceability/SKILL.md +12 -1
- package/dist/public/skills/kibi-usage/SKILL.md +8 -4
- package/dist/public/skills/kibi-usage/resources/proof.md +221 -20
- package/dist/public/skills/kibi-usage/resources/workflows.md +4 -4
- package/dist/report/html-report.css +83 -11
- 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 +214 -22
- package/dist/report/report-view.d.ts +2 -2
- package/dist/report/report-view.d.ts.map +1 -1
- package/dist/report/report-view.js +8 -0
- package/dist/schemas/entity.schema.json +404 -5
- 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/evidence-model.d.ts +13 -12
- package/dist/traceability/evidence-model.d.ts.map +1 -1
- package/dist/traceability/evidence-model.js +1 -1
- package/dist/traceability/impact-knowledge.d.ts.map +1 -1
- package/dist/traceability/impact-knowledge.js +1 -0
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
- package/dist/traceability/staged-diagnostics.js +24 -20
- package/dist/traceability/staged-impact-contract.d.ts +4 -4
- package/dist/traceability/staged-impact-contract.js +9 -9
- package/dist/traceability/staged-symbols-manifest.d.ts +11 -3
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +47 -140
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +1 -0
- package/dist/types/entities.d.ts +5 -2
- 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 +7 -1
- package/dist/utils/strict-modeling.d.ts +3 -2
- package/dist/utils/strict-modeling.d.ts.map +1 -1
- package/dist/utils/strict-modeling.js +37 -4
- package/dist/workspace-cwd.d.ts +15 -0
- package/dist/workspace-cwd.d.ts.map +1 -0
- package/dist/workspace-cwd.js +86 -0
- package/package.json +6 -2
- package/schema/entities.pl +6 -0
- package/schema/validation.pl +7 -2
- 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/modeling.ts +24 -5
- package/src/public/operations/specs/mutation-schemas.ts +30 -4
- package/src/public/operations/specs/reporting.ts +20 -10
- package/src/public/proof-certificates.ts +566 -0
- package/src/public/proof-protocol-types.d.ts +69 -1
- package/src/public/proof-protocol.ts +694 -3
- package/src/public/proof-receipt.ts +133 -7
- package/src/public/proof-sources.ts +1656 -0
- package/src/public/schemas/entity.ts +51 -3
- package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/src/public/skills/kibi-commit-review/SKILL.md +8 -8
- package/src/public/skills/kibi-commit-review/resources/staged-gate.md +12 -7
- package/src/public/skills/kibi-decisions/SKILL.md +13 -2
- package/src/public/skills/kibi-decisions/resources/decision-records.md +9 -1
- package/src/public/skills/kibi-freshness/SKILL.md +5 -1
- package/src/public/skills/kibi-traceability/SKILL.md +12 -1
- package/src/public/skills/kibi-usage/SKILL.md +8 -4
- package/src/public/skills/kibi-usage/resources/proof.md +221 -20
- package/src/public/skills/kibi-usage/resources/workflows.md +4 -4
- package/src/schemas/entity.schema.json +404 -5
- 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
- package/dist/traceability/reviewed-coordinate-refresh.d.ts +0 -10
- package/dist/traceability/reviewed-coordinate-refresh.d.ts.map +0 -1
- package/dist/traceability/reviewed-coordinate-refresh.js +0 -49
|
@@ -0,0 +1,566 @@
|
|
|
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
|
+
/**
|
|
20
|
+
* Builders for the kibi.code-certificate.v1 a certificate-kind proof contract
|
|
21
|
+
* leaves for `kibi prove`. A test builds its certificate and writes it with
|
|
22
|
+
* writeCodeCertificate; outside `kibi prove` the write is a no-op, so the same
|
|
23
|
+
* test runs unchanged in an ordinary test run. Kibi re-checks every
|
|
24
|
+
* certificate at ingest and again whenever it reports evidence, so a
|
|
25
|
+
* certificate only records what the test did; it never vouches for itself.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { createHash } from "node:crypto";
|
|
29
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
30
|
+
import path from "node:path";
|
|
31
|
+
import { evaluateSourceCode } from "../operations/proof/source-code-readers.js";
|
|
32
|
+
import { CODE_CERTIFICATE_VERSION } from "./proof-protocol.js";
|
|
33
|
+
import {
|
|
34
|
+
type SourceModel,
|
|
35
|
+
type SourceSpec,
|
|
36
|
+
describeSource,
|
|
37
|
+
readSource,
|
|
38
|
+
} from "./proof-sources.js";
|
|
39
|
+
|
|
40
|
+
export type { SourceModel, SourceSpec } from "./proof-sources.js";
|
|
41
|
+
|
|
42
|
+
/** Where `kibi prove` asks a run to leave its certificates. */
|
|
43
|
+
// implements REQ-kibi-code-proof-kinds
|
|
44
|
+
export const PROOF_CERTIFICATES_ENV = "KIBI_PROOF_CERTIFICATES";
|
|
45
|
+
|
|
46
|
+
// implements REQ-kibi-code-proof-kinds
|
|
47
|
+
export type CodeCertificate = Readonly<Record<string, unknown>> &
|
|
48
|
+
Readonly<{
|
|
49
|
+
version: typeof CODE_CERTIFICATE_VERSION;
|
|
50
|
+
kind:
|
|
51
|
+
| "exhaustive"
|
|
52
|
+
| "typecheck"
|
|
53
|
+
| "arithmetic"
|
|
54
|
+
| "smt"
|
|
55
|
+
| "rule"
|
|
56
|
+
| "state_machine"
|
|
57
|
+
| "regex"
|
|
58
|
+
| "source";
|
|
59
|
+
test_id: string;
|
|
60
|
+
}>;
|
|
61
|
+
|
|
62
|
+
function compareCodePoints(left: string, right: string): number {
|
|
63
|
+
const a = Array.from(left, (char) => char.codePointAt(0) ?? 0);
|
|
64
|
+
const b = Array.from(right, (char) => char.codePointAt(0) ?? 0);
|
|
65
|
+
for (let index = 0; index < Math.min(a.length, b.length); index++) {
|
|
66
|
+
const difference = (a[index] ?? 0) - (b[index] ?? 0);
|
|
67
|
+
if (difference !== 0) return difference;
|
|
68
|
+
}
|
|
69
|
+
return a.length - b.length;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const JSON_ESCAPES: Readonly<Record<string, string>> = {
|
|
73
|
+
'"': '\\"',
|
|
74
|
+
"\\": "\\\\",
|
|
75
|
+
"\b": "\\b",
|
|
76
|
+
"\f": "\\f",
|
|
77
|
+
"\n": "\\n",
|
|
78
|
+
"\r": "\\r",
|
|
79
|
+
"\t": "\\t",
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
function canonicalString(value: string): string {
|
|
83
|
+
let out = '"';
|
|
84
|
+
for (let index = 0; index < value.length; index++) {
|
|
85
|
+
const char = value[index] as string;
|
|
86
|
+
const unit = value.charCodeAt(index);
|
|
87
|
+
const escaped = JSON_ESCAPES[char];
|
|
88
|
+
if (escaped !== undefined) out += escaped;
|
|
89
|
+
else if (unit >= 0x20 && unit <= 0x7e) out += char;
|
|
90
|
+
else out += `\\u${unit.toString(16).padStart(4, "0")}`;
|
|
91
|
+
}
|
|
92
|
+
return `${out}"`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The canonical JSON of a certificate, byte for byte what
|
|
97
|
+
* code_certificates.pl hashes: keys sorted by code point, no whitespace,
|
|
98
|
+
* non-ASCII and control characters as \u escapes of UTF-16 code units.
|
|
99
|
+
*/
|
|
100
|
+
// implements REQ-kibi-code-proof-kinds
|
|
101
|
+
export function canonicalCertificateJson(value: unknown): string {
|
|
102
|
+
if (value === null || value === undefined) return "null";
|
|
103
|
+
if (Array.isArray(value))
|
|
104
|
+
return `[${value.map(canonicalCertificateJson).join(",")}]`;
|
|
105
|
+
if (typeof value === "object")
|
|
106
|
+
return `{${Object.keys(value)
|
|
107
|
+
.sort(compareCodePoints)
|
|
108
|
+
.map(
|
|
109
|
+
(key) =>
|
|
110
|
+
`${canonicalString(key)}:${canonicalCertificateJson((value as Record<string, unknown>)[key])}`,
|
|
111
|
+
)
|
|
112
|
+
.join(",")}}`;
|
|
113
|
+
if (typeof value === "string") return canonicalString(value);
|
|
114
|
+
return JSON.stringify(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The sha256 a receipt records for its certificate. */
|
|
118
|
+
// implements REQ-kibi-code-proof-kinds
|
|
119
|
+
export function certificateDigest(certificate: unknown): string {
|
|
120
|
+
return createHash("sha256")
|
|
121
|
+
.update(canonicalCertificateJson(certificate), "utf8")
|
|
122
|
+
.digest("hex");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// implements REQ-kibi-code-proof-kinds
|
|
126
|
+
export type ExhaustiveResult = Readonly<{
|
|
127
|
+
member: string;
|
|
128
|
+
outcome: "passed" | "failed";
|
|
129
|
+
message?: string;
|
|
130
|
+
}>;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Run `check` on every member of a finite domain and record each outcome.
|
|
134
|
+
* `members` must be the domain as the code defines it (import it from its
|
|
135
|
+
* source, never restate it): Kibi rejects the certificate unless the proof
|
|
136
|
+
* file imports the contract's domain_source symbol.
|
|
137
|
+
*/
|
|
138
|
+
// implements REQ-kibi-code-proof-kinds
|
|
139
|
+
export async function exhaustiveCertificate<T>(input: {
|
|
140
|
+
testId: string;
|
|
141
|
+
domain: Readonly<{ symbolId: string; members: Iterable<T> }>;
|
|
142
|
+
key?: (member: T) => string;
|
|
143
|
+
check: (member: T) => void | Promise<void>;
|
|
144
|
+
}): Promise<CodeCertificate> {
|
|
145
|
+
const key = input.key ?? ((member: T) => String(member));
|
|
146
|
+
const members = [...input.domain.members];
|
|
147
|
+
const results: ExhaustiveResult[] = [];
|
|
148
|
+
for (const member of members) {
|
|
149
|
+
try {
|
|
150
|
+
await input.check(member);
|
|
151
|
+
results.push({ member: key(member), outcome: "passed" });
|
|
152
|
+
} catch (error) {
|
|
153
|
+
results.push({
|
|
154
|
+
member: key(member),
|
|
155
|
+
outcome: "failed",
|
|
156
|
+
message: error instanceof Error ? error.message : String(error),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
162
|
+
kind: "exhaustive",
|
|
163
|
+
test_id: input.testId,
|
|
164
|
+
domain: {
|
|
165
|
+
symbol_id: input.domain.symbolId,
|
|
166
|
+
members: members.map(key),
|
|
167
|
+
},
|
|
168
|
+
results,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** The members an exhaustive certificate records as failed. */
|
|
173
|
+
// implements REQ-kibi-code-proof-kinds
|
|
174
|
+
export function failedMembers(certificate: CodeCertificate): string[] {
|
|
175
|
+
const results = Array.isArray(certificate.results)
|
|
176
|
+
? (certificate.results as ExhaustiveResult[])
|
|
177
|
+
: [];
|
|
178
|
+
return results
|
|
179
|
+
.filter((result) => result.outcome !== "passed")
|
|
180
|
+
.map((result) =>
|
|
181
|
+
result.message ? `${result.member}: ${result.message}` : result.member,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* A type checker accepted `witnessPath`, a file that fails to compile when a
|
|
187
|
+
* member of the domain goes unhandled (an exhaustive switch ending in a
|
|
188
|
+
* `never` assignment, or a mapped type over the domain).
|
|
189
|
+
*/
|
|
190
|
+
// implements REQ-kibi-code-proof-kinds
|
|
191
|
+
export function typecheckCertificate(input: {
|
|
192
|
+
testId: string;
|
|
193
|
+
domainSymbolId: string;
|
|
194
|
+
witnessPath: string;
|
|
195
|
+
workspaceRoot?: string;
|
|
196
|
+
compiler: Readonly<{ name: string; version: string }>;
|
|
197
|
+
accepted: boolean;
|
|
198
|
+
}): CodeCertificate {
|
|
199
|
+
const absolute = path.resolve(
|
|
200
|
+
input.workspaceRoot ?? process.cwd(),
|
|
201
|
+
input.witnessPath,
|
|
202
|
+
);
|
|
203
|
+
return {
|
|
204
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
205
|
+
kind: "typecheck",
|
|
206
|
+
test_id: input.testId,
|
|
207
|
+
domain: { symbol_id: input.domainSymbolId },
|
|
208
|
+
witness: {
|
|
209
|
+
path: input.witnessPath,
|
|
210
|
+
sha256: createHash("sha256")
|
|
211
|
+
.update(readFileSync(absolute, "utf8"))
|
|
212
|
+
.digest("hex"),
|
|
213
|
+
},
|
|
214
|
+
compiler: { ...input.compiler },
|
|
215
|
+
outcome: input.accepted ? "passed" : "failed",
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// implements REQ-kibi-code-proof-kinds
|
|
220
|
+
export type ArithmeticConstraint = Readonly<{
|
|
221
|
+
operator: "eq" | "neq" | "lt" | "lte" | "gt" | "gte";
|
|
222
|
+
value: number;
|
|
223
|
+
}>;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* A claim about one numeric variable that Kibi decides exactly: the
|
|
227
|
+
* assumptions must be satisfiable and must entail the claim for every value
|
|
228
|
+
* of the variable, not only the values a test happened to try.
|
|
229
|
+
*/
|
|
230
|
+
// implements REQ-kibi-code-proof-kinds
|
|
231
|
+
export function arithmeticCertificate(input: {
|
|
232
|
+
testId: string;
|
|
233
|
+
variable: string;
|
|
234
|
+
valueType: "int" | "real";
|
|
235
|
+
assumptions: readonly ArithmeticConstraint[];
|
|
236
|
+
claim: ArithmeticConstraint;
|
|
237
|
+
}): CodeCertificate {
|
|
238
|
+
return {
|
|
239
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
240
|
+
kind: "arithmetic",
|
|
241
|
+
test_id: input.testId,
|
|
242
|
+
variable: input.variable,
|
|
243
|
+
value_type: input.valueType,
|
|
244
|
+
assumptions: input.assumptions.map((row) => ({ ...row })),
|
|
245
|
+
claim: { ...input.claim },
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* An SMT-LIB query whose unsat answer proves the claim (the query asserts
|
|
251
|
+
* the claim's negation). Kibi re-runs the named solver on the query.
|
|
252
|
+
*/
|
|
253
|
+
// implements REQ-kibi-code-proof-kinds
|
|
254
|
+
export function smtCertificate(input: {
|
|
255
|
+
testId: string;
|
|
256
|
+
solver: Readonly<{ name: "z3" | "cvc5"; version: string }>;
|
|
257
|
+
query: string;
|
|
258
|
+
result: string;
|
|
259
|
+
}): CodeCertificate {
|
|
260
|
+
return {
|
|
261
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
262
|
+
kind: "smt",
|
|
263
|
+
test_id: input.testId,
|
|
264
|
+
solver: { ...input.solver },
|
|
265
|
+
query: input.query,
|
|
266
|
+
result: input.result,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const certificateText = (value: unknown) =>
|
|
271
|
+
typeof value === "string" ? value : String(value);
|
|
272
|
+
|
|
273
|
+
/** A finite domain a rule certificate ranges over. */
|
|
274
|
+
// implements REQ-kibi-non-numeric-proof-kinds
|
|
275
|
+
export type RuleDomain = Readonly<{
|
|
276
|
+
/** The domain's symbol; omit it for a domain the contract reads from a source. */
|
|
277
|
+
symbolId?: string;
|
|
278
|
+
members: Iterable<unknown>;
|
|
279
|
+
}>;
|
|
280
|
+
|
|
281
|
+
/** A predicate the code decides, and the argument types it takes. */
|
|
282
|
+
// implements REQ-kibi-non-numeric-proof-kinds
|
|
283
|
+
export type RulePredicate = Readonly<{
|
|
284
|
+
symbolId: string;
|
|
285
|
+
argTypes: readonly string[];
|
|
286
|
+
decide: (...args: never[]) => boolean | Promise<boolean>;
|
|
287
|
+
}>;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Call every predicate on every combination of its argument domains and
|
|
291
|
+
* record what the code decides. Import the domains and functions from their
|
|
292
|
+
* source (never restate them): Kibi evaluates the same exports at ingest and
|
|
293
|
+
* rejects a certificate whose tables differ, then model checks each claim's
|
|
294
|
+
* rule, predicate or set fact against the tables.
|
|
295
|
+
*/
|
|
296
|
+
// implements REQ-kibi-non-numeric-proof-kinds
|
|
297
|
+
export async function ruleCertificate(input: {
|
|
298
|
+
testId: string;
|
|
299
|
+
domains: Readonly<Record<string, RuleDomain>>;
|
|
300
|
+
predicates: Readonly<Record<string, RulePredicate>>;
|
|
301
|
+
}): Promise<CodeCertificate> {
|
|
302
|
+
const values: Record<string, unknown[]> = {};
|
|
303
|
+
const domains: Record<string, { symbol_id?: string; members: string[] }> = {};
|
|
304
|
+
for (const [type, domain] of Object.entries(input.domains)) {
|
|
305
|
+
values[type] = [...domain.members];
|
|
306
|
+
domains[type] = {
|
|
307
|
+
...(domain.symbolId !== undefined ? { symbol_id: domain.symbolId } : {}),
|
|
308
|
+
members: values[type].map(certificateText),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const predicates: Record<
|
|
312
|
+
string,
|
|
313
|
+
{
|
|
314
|
+
symbol_id: string;
|
|
315
|
+
arg_types: string[];
|
|
316
|
+
rows: { args: string[]; value?: boolean; error?: string }[];
|
|
317
|
+
}
|
|
318
|
+
> = {};
|
|
319
|
+
for (const [name, predicate] of Object.entries(input.predicates)) {
|
|
320
|
+
const lists = predicate.argTypes.map((type) => {
|
|
321
|
+
const list = values[type];
|
|
322
|
+
if (list === undefined)
|
|
323
|
+
throw new Error(`no domain for argument type ${type} of ${name}`);
|
|
324
|
+
return list;
|
|
325
|
+
});
|
|
326
|
+
const rows: { args: string[]; value?: boolean; error?: string }[] = [];
|
|
327
|
+
const count = lists.reduce((product, list) => product * list.length, 1);
|
|
328
|
+
for (let index = 0; index < count; index++) {
|
|
329
|
+
let rest = index;
|
|
330
|
+
const tuple: unknown[] = [];
|
|
331
|
+
for (let position = lists.length - 1; position >= 0; position--) {
|
|
332
|
+
const list = lists[position] as unknown[];
|
|
333
|
+
tuple[position] = list[rest % list.length];
|
|
334
|
+
rest = Math.floor(rest / list.length);
|
|
335
|
+
}
|
|
336
|
+
const args = tuple.map(certificateText);
|
|
337
|
+
try {
|
|
338
|
+
const value = await (
|
|
339
|
+
predicate.decide as (...args: unknown[]) => unknown
|
|
340
|
+
)(...tuple);
|
|
341
|
+
rows.push(
|
|
342
|
+
typeof value === "boolean"
|
|
343
|
+
? { args, value }
|
|
344
|
+
: { args, error: `returned ${typeof value}, not a boolean` },
|
|
345
|
+
);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
rows.push({
|
|
348
|
+
args,
|
|
349
|
+
error: error instanceof Error ? error.message : String(error),
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
predicates[name] = {
|
|
354
|
+
symbol_id: predicate.symbolId,
|
|
355
|
+
arg_types: [...predicate.argTypes],
|
|
356
|
+
rows,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
361
|
+
kind: "rule",
|
|
362
|
+
test_id: input.testId,
|
|
363
|
+
domains,
|
|
364
|
+
predicates,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** The calls of a rule certificate that threw or returned a non-boolean. */
|
|
369
|
+
// implements REQ-kibi-non-numeric-proof-kinds
|
|
370
|
+
export function failedCalls(certificate: CodeCertificate): string[] {
|
|
371
|
+
const predicates = (certificate.predicates ?? {}) as Record<
|
|
372
|
+
string,
|
|
373
|
+
{ rows?: { args: string[]; error?: string }[] }
|
|
374
|
+
>;
|
|
375
|
+
return Object.entries(predicates).flatMap(([name, table]) =>
|
|
376
|
+
(table.rows ?? [])
|
|
377
|
+
.filter((row) => row.error !== undefined)
|
|
378
|
+
.map((row) => `${name}(${row.args.join(", ")}): ${row.error}`),
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
type Transitions =
|
|
383
|
+
| ReadonlyMap<unknown, unknown>
|
|
384
|
+
| Readonly<Record<string, unknown>>
|
|
385
|
+
| readonly unknown[]
|
|
386
|
+
| ((state: never, event: never) => unknown);
|
|
387
|
+
|
|
388
|
+
function nextStates(next: unknown): unknown[] {
|
|
389
|
+
if (next === null || next === undefined || next === false) return [];
|
|
390
|
+
if (Array.isArray(next)) return next;
|
|
391
|
+
if (next instanceof Set) return [...next];
|
|
392
|
+
if (next instanceof Map) return [...next.values()];
|
|
393
|
+
if (typeof next === "object") return Object.values(next);
|
|
394
|
+
return [next];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Record a state machine as the code defines it: a transition table (an
|
|
399
|
+
* object or Map from each state to its next state, states, or event map; or
|
|
400
|
+
* a list of [from, to] pairs) or a next-state function called on every state
|
|
401
|
+
* and event (a throw, null or undefined means no transition). Kibi reads the
|
|
402
|
+
* same exports at ingest and checks each claim's transitions, reachability,
|
|
403
|
+
* initial and terminal states against the machine.
|
|
404
|
+
*/
|
|
405
|
+
// implements REQ-kibi-non-numeric-proof-kinds
|
|
406
|
+
export async function stateMachineCertificate(input: {
|
|
407
|
+
testId: string;
|
|
408
|
+
symbolId: string;
|
|
409
|
+
transitions: Transitions;
|
|
410
|
+
/** For a next-state function: the states and events it is called on. */
|
|
411
|
+
states?: Iterable<unknown>;
|
|
412
|
+
events?: Iterable<unknown>;
|
|
413
|
+
initial?: unknown;
|
|
414
|
+
}): Promise<CodeCertificate> {
|
|
415
|
+
const states = new Set<string>();
|
|
416
|
+
const edges = new Map<string, [string, string]>();
|
|
417
|
+
const edge = (from: unknown, to: unknown) => {
|
|
418
|
+
const pair: [string, string] = [certificateText(from), certificateText(to)];
|
|
419
|
+
states.add(pair[0]);
|
|
420
|
+
states.add(pair[1]);
|
|
421
|
+
edges.set(pair.join("\u0000"), pair);
|
|
422
|
+
};
|
|
423
|
+
const table = input.transitions;
|
|
424
|
+
if (typeof table === "function") {
|
|
425
|
+
const events = [...(input.events ?? [])];
|
|
426
|
+
for (const state of input.states ?? []) {
|
|
427
|
+
states.add(certificateText(state));
|
|
428
|
+
for (const event of events) {
|
|
429
|
+
let next: unknown;
|
|
430
|
+
try {
|
|
431
|
+
next = await (table as (state: unknown, event: unknown) => unknown)(
|
|
432
|
+
state,
|
|
433
|
+
event,
|
|
434
|
+
);
|
|
435
|
+
} catch {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
for (const target of nextStates(next)) edge(state, target);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
} else if (Array.isArray(table)) {
|
|
442
|
+
for (const entry of table as unknown[]) {
|
|
443
|
+
if (Array.isArray(entry) && entry.length === 2) edge(entry[0], entry[1]);
|
|
444
|
+
else if (entry !== null && typeof entry === "object")
|
|
445
|
+
edge((entry as { from: unknown }).from, (entry as { to: unknown }).to);
|
|
446
|
+
}
|
|
447
|
+
} else {
|
|
448
|
+
const entries =
|
|
449
|
+
table instanceof Map
|
|
450
|
+
? [...table.entries()]
|
|
451
|
+
: Object.entries(table as Record<string, unknown>);
|
|
452
|
+
for (const [from, next] of entries) {
|
|
453
|
+
states.add(certificateText(from));
|
|
454
|
+
for (const target of nextStates(next)) edge(from, target);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const initial =
|
|
458
|
+
input.initial === undefined
|
|
459
|
+
? undefined
|
|
460
|
+
: (typeof input.initial === "object" && input.initial !== null
|
|
461
|
+
? [...(input.initial as Iterable<unknown>)]
|
|
462
|
+
: [input.initial]
|
|
463
|
+
)
|
|
464
|
+
.map(certificateText)
|
|
465
|
+
.sort();
|
|
466
|
+
return {
|
|
467
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
468
|
+
kind: "state_machine",
|
|
469
|
+
test_id: input.testId,
|
|
470
|
+
machine: {
|
|
471
|
+
symbol_id: input.symbolId,
|
|
472
|
+
states: [...states].sort(),
|
|
473
|
+
transitions: [...edges.values()].sort((a, b) =>
|
|
474
|
+
a.join("\u0000") < b.join("\u0000") ? -1 : 1,
|
|
475
|
+
),
|
|
476
|
+
...(initial !== undefined ? { initial } : {}),
|
|
477
|
+
},
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Record the pattern (a RegExp) or string constant a string claim is about.
|
|
483
|
+
* Kibi reads the same export at ingest and decides the claim exactly: a
|
|
484
|
+
* pattern's language against the fact's, or the string against the fact.
|
|
485
|
+
*/
|
|
486
|
+
// implements REQ-kibi-non-numeric-proof-kinds
|
|
487
|
+
export function regexCertificate(input: {
|
|
488
|
+
testId: string;
|
|
489
|
+
symbolId: string;
|
|
490
|
+
value: RegExp | string;
|
|
491
|
+
}): CodeCertificate {
|
|
492
|
+
return {
|
|
493
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
494
|
+
kind: "regex",
|
|
495
|
+
test_id: input.testId,
|
|
496
|
+
pattern:
|
|
497
|
+
typeof input.value === "string"
|
|
498
|
+
? { symbol_id: input.symbolId, type: "string", value: input.value }
|
|
499
|
+
: {
|
|
500
|
+
symbol_id: input.symbolId,
|
|
501
|
+
type: "regexp",
|
|
502
|
+
source: input.value.source,
|
|
503
|
+
flags: input.value.flags,
|
|
504
|
+
flavor: "js",
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* The certificate of a source proof. Kibi reads the contract's source
|
|
511
|
+
* itself at ingest and stores its own read, so the run only names the test
|
|
512
|
+
* and, optionally, the source (which must be the contract's). A run may also
|
|
513
|
+
* leave nothing: a passed source proof needs no certificate from the test.
|
|
514
|
+
*/
|
|
515
|
+
// implements REQ-kibi-source-proof-kinds
|
|
516
|
+
export function sourceCertificate(input: {
|
|
517
|
+
testId: string;
|
|
518
|
+
source?: SourceSpec;
|
|
519
|
+
}): CodeCertificate {
|
|
520
|
+
return {
|
|
521
|
+
version: CODE_CERTIFICATE_VERSION,
|
|
522
|
+
kind: "source",
|
|
523
|
+
test_id: input.testId,
|
|
524
|
+
...(input.source !== undefined ? { source: input.source } : {}),
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Read a project source the way Kibi does at ingest: the members (and the
|
|
530
|
+
* tables, objects or routes) of a JSON or YAML file, a glob, an OpenAPI
|
|
531
|
+
* document, the references the code makes, a data schema, a route table or
|
|
532
|
+
* a validator. A rule proof whose contract reads a domain from a source
|
|
533
|
+
* uses this to range its predicates over the same members.
|
|
534
|
+
*/
|
|
535
|
+
// implements REQ-kibi-source-proof-kinds
|
|
536
|
+
export async function readProjectSource(
|
|
537
|
+
source: SourceSpec,
|
|
538
|
+
workspaceRoot: string = process.cwd(),
|
|
539
|
+
): Promise<SourceModel> {
|
|
540
|
+
const read = await readSource(workspaceRoot, source, evaluateSourceCode);
|
|
541
|
+
if (!read.ok)
|
|
542
|
+
throw new Error(
|
|
543
|
+
`Kibi could not read ${describeSource(source)}: ${read.reason}`,
|
|
544
|
+
);
|
|
545
|
+
return read.model;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Leave the certificate where `kibi prove` reads it. Returns the file written,
|
|
550
|
+
* or null outside a `kibi prove` run.
|
|
551
|
+
*/
|
|
552
|
+
// implements REQ-kibi-code-proof-kinds
|
|
553
|
+
export function writeCodeCertificate(
|
|
554
|
+
certificate: CodeCertificate,
|
|
555
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
556
|
+
): string | null {
|
|
557
|
+
const directory = env[PROOF_CERTIFICATES_ENV];
|
|
558
|
+
if (directory === undefined || directory.trim() === "") return null;
|
|
559
|
+
mkdirSync(directory, { recursive: true });
|
|
560
|
+
const file = path.join(
|
|
561
|
+
directory,
|
|
562
|
+
`${certificate.test_id.replace(/[^A-Za-z0-9._-]/g, "_")}.json`,
|
|
563
|
+
);
|
|
564
|
+
writeFileSync(file, `${JSON.stringify(certificate, null, 2)}\n`, "utf8");
|
|
565
|
+
return file;
|
|
566
|
+
}
|
|
@@ -24,7 +24,9 @@ export type RunFailurePhase =
|
|
|
24
24
|
| "execution"
|
|
25
25
|
| "teardown"
|
|
26
26
|
| "infrastructure";
|
|
27
|
-
export type SuccessPolicy =
|
|
27
|
+
export type SuccessPolicy =
|
|
28
|
+
| "all_required_first_attempt"
|
|
29
|
+
| "own_results_first_attempt";
|
|
28
30
|
|
|
29
31
|
export type ProofAttempt = Readonly<{
|
|
30
32
|
outcome: ProofResultOutcome;
|
|
@@ -72,6 +74,7 @@ export type ProofRunArtifact = Readonly<{
|
|
|
72
74
|
proof_results: readonly ProofResult[];
|
|
73
75
|
integration?: string;
|
|
74
76
|
diagnostics?: readonly string[];
|
|
77
|
+
certificates?: readonly Readonly<Record<string, unknown>>[];
|
|
75
78
|
}>;
|
|
76
79
|
|
|
77
80
|
export type ProofObligation = Readonly<{
|
|
@@ -84,8 +87,73 @@ export type ProofContract = Readonly<{
|
|
|
84
87
|
integration: string;
|
|
85
88
|
required_proofs: readonly ProofObligation[];
|
|
86
89
|
success_policy: SuccessPolicy;
|
|
90
|
+
kind?:
|
|
91
|
+
| "e2e"
|
|
92
|
+
| "example"
|
|
93
|
+
| "property"
|
|
94
|
+
| "exhaustive"
|
|
95
|
+
| "typecheck"
|
|
96
|
+
| "arithmetic"
|
|
97
|
+
| "smt"
|
|
98
|
+
| "rule"
|
|
99
|
+
| "state_machine"
|
|
100
|
+
| "regex"
|
|
101
|
+
| "source";
|
|
102
|
+
domain_source?: Readonly<{ symbol_id: string; export?: string }>;
|
|
103
|
+
witness?: Readonly<{ path: string }>;
|
|
104
|
+
constraint_source?: Readonly<{ fact_ids: readonly 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
|
+
)[];
|
|
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;
|
|
87
141
|
}>;
|
|
88
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
|
+
|
|
89
157
|
export type ProofBinding = Readonly<{
|
|
90
158
|
symbol_id: string;
|
|
91
159
|
target: string;
|