kibi-cli 2.0.0 → 2.1.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 +25 -1
- package/dist/cli-register-reporting.d.ts.map +1 -1
- package/dist/cli-register-reporting.js +6 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/commands/check-generated.d.ts +7 -0
- package/dist/commands/check-generated.d.ts.map +1 -0
- package/dist/commands/check-generated.js +192 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +3 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +122 -1
- package/dist/commands/engine.d.ts.map +1 -1
- package/dist/commands/engine.js +3 -1
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +13 -7
- package/dist/commands/proof-explain.d.ts +10 -0
- package/dist/commands/proof-explain.d.ts.map +1 -0
- package/dist/commands/proof-explain.js +45 -0
- package/dist/commands/proof-impact.d.ts +7 -0
- package/dist/commands/proof-impact.d.ts.map +1 -0
- package/dist/commands/proof-impact.js +39 -0
- package/dist/commands/proof-migrate-legacy.js +1 -1
- package/dist/commands/proof-prune.js +1 -1
- package/dist/commands/prove.js +4 -4
- package/dist/commands/report.d.ts.map +1 -1
- package/dist/commands/report.js +17 -2
- package/dist/commands/sync/manifest.d.ts +1 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +18 -16
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +6 -0
- package/dist/engine.d.ts +3 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +134 -61
- package/dist/env/bootstrap.d.ts +84 -0
- package/dist/env/bootstrap.d.ts.map +1 -0
- package/dist/env/bootstrap.js +310 -0
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +1 -1
- package/dist/extractors/markdown.d.ts +2 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +19 -0
- package/dist/extractors/symbols-coordinator.d.ts +8 -0
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +40 -0
- package/dist/extractors/symbols-ts.d.ts +8 -19
- package/dist/extractors/symbols-ts.d.ts.map +1 -1
- package/dist/extractors/symbols-ts.js +33 -503
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +4 -5
- package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
- package/dist/operations/modeling/model-requirement.js +4 -0
- package/dist/operations/modeling/suggest-predicates.d.ts +1 -1
- package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
- package/dist/operations/modeling/suggest-predicates.js +30 -3
- package/dist/operations/mutation/symbol-granularity.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-granularity.js +9 -87
- package/dist/operations/planning/compile-intent.d.ts +49 -0
- package/dist/operations/planning/compile-intent.d.ts.map +1 -1
- package/dist/operations/planning/compile-intent.js +63 -3
- package/dist/operations/proof/baseline-diff.d.ts +41 -0
- package/dist/operations/proof/baseline-diff.d.ts.map +1 -0
- package/dist/operations/proof/baseline-diff.js +153 -0
- package/dist/operations/proof/explain.d.ts +75 -0
- package/dist/operations/proof/explain.d.ts.map +1 -0
- package/dist/operations/proof/explain.js +294 -0
- package/dist/operations/proof/impact.d.ts +45 -0
- package/dist/operations/proof/impact.d.ts.map +1 -0
- package/dist/operations/proof/impact.js +118 -0
- package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
- package/dist/operations/proof/ingest-proof.js +44 -3
- package/dist/operations/proof/migrate-legacy-receipts.d.ts.map +1 -1
- package/dist/operations/proof/migrate-legacy-receipts.js +21 -17
- package/dist/operations/proof/prune-receipts.js +1 -1
- package/dist/operations/proof/receipt-document.js +2 -2
- package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analysis-receipt.js +3 -9
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +18 -67
- package/dist/operations/semantic-advisor/plugin-orchestration.d.ts +63 -0
- package/dist/operations/semantic-advisor/plugin-orchestration.d.ts.map +1 -0
- package/dist/operations/semantic-advisor/plugin-orchestration.js +489 -0
- package/dist/operations/semantic-advisor/predicate-rules-core.d.ts +15 -2
- package/dist/operations/semantic-advisor/predicate-rules-core.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-core.js +7 -140
- package/dist/operations/semantic-advisor/predicate-rules-launcher.d.ts +11 -12
- package/dist/operations/semantic-advisor/predicate-rules-launcher.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-launcher.js +6 -94
- package/dist/operations/semantic-advisor/predicate-rules-policy.d.ts +16 -0
- package/dist/operations/semantic-advisor/predicate-rules-policy.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-policy.js +7 -150
- package/dist/operations/semantic-advisor/predicate-rules-product-tail.d.ts +9 -0
- package/dist/operations/semantic-advisor/predicate-rules-product-tail.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-product-tail.js +7 -102
- package/dist/operations/semantic-advisor/predicate-rules-product.d.ts +13 -0
- package/dist/operations/semantic-advisor/predicate-rules-product.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-product.js +7 -133
- package/dist/operations/semantic-advisor/types.d.ts +1 -0
- package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
- package/dist/plugins/compose-ontology-packs.d.ts +51 -0
- package/dist/plugins/compose-ontology-packs.d.ts.map +1 -0
- package/dist/plugins/compose-ontology-packs.js +257 -0
- package/dist/plugins/compose-semantic-classifier.d.ts +42 -0
- package/dist/plugins/compose-semantic-classifier.d.ts.map +1 -0
- package/dist/plugins/compose-semantic-classifier.js +211 -0
- package/dist/plugins/external-allowlist.d.ts +10 -0
- package/dist/plugins/external-allowlist.d.ts.map +1 -0
- package/dist/plugins/external-allowlist.js +16 -0
- package/dist/plugins/index.d.ts +9 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +33 -0
- package/dist/plugins/load-plugin.d.ts +19 -0
- package/dist/plugins/load-plugin.d.ts.map +1 -0
- package/dist/plugins/load-plugin.js +59 -0
- package/dist/plugins/project-config.d.ts +28 -0
- package/dist/plugins/project-config.d.ts.map +1 -0
- package/dist/plugins/project-config.js +90 -0
- package/dist/plugins/registry.d.ts +93 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +338 -0
- package/dist/plugins/resolve-package.d.ts +47 -0
- package/dist/plugins/resolve-package.d.ts.map +1 -0
- package/dist/plugins/resolve-package.js +262 -0
- package/dist/plugins/source-analysis-service.d.ts +44 -0
- package/dist/plugins/source-analysis-service.d.ts.map +1 -0
- package/dist/plugins/source-analysis-service.js +232 -0
- package/dist/prolog/janitor.d.ts.map +1 -1
- package/dist/prolog/janitor.js +1 -1
- package/dist/prolog/store-lock.d.ts +1 -1
- package/dist/prolog/store-lock.d.ts.map +1 -1
- package/dist/proof/inspect.js +1 -1
- package/dist/proof/producers/playwright-case-id.js +2 -2
- package/dist/proof/producers/playwright-reporter.js +4 -4
- package/dist/public/extractors/symbols-coordinator.d.ts +1 -1
- package/dist/public/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/public/extractors/symbols-coordinator.js +1 -1
- package/dist/public/impact/analyzer.d.ts +1 -1
- package/dist/public/impact/analyzer.d.ts.map +1 -1
- package/dist/public/impact/analyzer.js +6 -4
- package/dist/public/operations/catalog.d.ts +4 -1
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +1 -1
- package/dist/public/operations/check-helpers.d.ts +1 -1
- package/dist/public/operations/check-helpers.d.ts.map +1 -1
- package/dist/public/operations/check-helpers.js +1 -1
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +35 -12
- package/dist/public/operations/discovery-entities.d.ts +12 -0
- package/dist/public/operations/discovery-entities.d.ts.map +1 -1
- package/dist/public/operations/discovery-entities.js +34 -0
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +23 -18
- package/dist/public/operations/prolog-json.d.ts +1 -1
- package/dist/public/operations/prolog-json.d.ts.map +1 -1
- package/dist/public/operations/prolog-json.js +7 -7
- package/dist/public/operations/runtime-types.d.ts +16 -1
- package/dist/public/operations/runtime-types.d.ts.map +1 -1
- 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 +2 -1
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/discovery.d.ts +3 -0
- package/dist/public/operations/specs/discovery.d.ts.map +1 -1
- package/dist/public/operations/specs/discovery.js +3 -0
- package/dist/public/operations/specs/proof.js +1 -1
- package/dist/public/operations/specs/reporting.d.ts +1 -0
- package/dist/public/operations/specs/reporting.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.js +2 -2
- package/dist/public/operations/specs/semantic.d.ts +1 -1
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +54 -4
- package/dist/public/operations/symbol-repair-plan.d.ts.map +1 -1
- package/dist/public/operations/symbol-repair-plan.js +3 -1
- package/dist/public/operations/types.d.ts +5 -0
- package/dist/public/operations/types.d.ts.map +1 -1
- package/dist/public/proof-protocol.js +4 -4
- package/dist/public/skills/kibi-usage/SKILL.md +4 -1
- package/dist/public/skills/kibi-usage/resources/proof.md +45 -0
- package/dist/public/symbol-granularity.d.ts +1 -1
- package/dist/public/symbol-granularity.d.ts.map +1 -1
- package/dist/public/symbol-granularity.generated.d.ts.map +1 -1
- package/dist/public/symbol-granularity.generated.js +38 -5
- package/dist/report/html-report.css +1239 -281
- package/dist/runtime/cli-runtime.d.ts.map +1 -1
- package/dist/runtime/cli-runtime.js +18 -0
- package/dist/traceability/symbol-extract.d.ts +24 -7
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +64 -208
- package/dist/utils/rule-registry.generated.d.ts +5 -0
- package/dist/utils/rule-registry.generated.d.ts.map +1 -1
- package/dist/utils/rule-registry.generated.js +6 -0
- package/package.json +14 -11
- package/src/public/extractors/symbols-coordinator.ts +1 -0
- package/src/public/impact/analyzer.ts +15 -12
- package/src/public/operations/check-executor.ts +1 -1
- package/src/public/operations/check-helpers.ts +2 -2
- package/src/public/operations/contracts.ts +40 -12
- package/src/public/operations/discovery-entities.ts +46 -0
- package/src/public/operations/discovery-executors.ts +39 -26
- package/src/public/operations/prolog-json.ts +13 -5
- package/src/public/operations/runtime-types.ts +21 -2
- package/src/public/operations/specs/check-rules.generated.ts +2 -1
- package/src/public/operations/specs/discovery.ts +3 -0
- package/src/public/operations/specs/proof.ts +1 -1
- package/src/public/operations/specs/reporting.ts +2 -2
- package/src/public/operations/specs/semantic.ts +79 -17
- package/src/public/operations/symbol-repair-plan.ts +3 -4
- package/src/public/operations/types.ts +5 -0
- package/src/public/proof-fingerprint.ts +1 -1
- package/src/public/proof-protocol-types.d.ts +1 -1
- package/src/public/proof-protocol.ts +4 -4
- package/src/public/skills/kibi-usage/SKILL.md +4 -1
- package/src/public/skills/kibi-usage/resources/proof.md +45 -0
- package/src/public/symbol-granularity.generated.ts +45 -12
- package/src/public/symbol-granularity.ts +3 -3
|
@@ -0,0 +1,294 @@
|
|
|
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
|
+
import { runOperationJsonQuery, toPrologAtom, } from "../../public/operations/prolog-json.js";
|
|
11
|
+
import { currentProofBindingMode, perContractTestBindings, } from "../../public/operations/specs/reporting.js";
|
|
12
|
+
import { readWorkspaceSnapshot } from "../../public/operations/workspace-snapshot.js";
|
|
13
|
+
import { PROOF_RECEIPT_MAX_AGE_SECONDS } from "../../public/proof-receipt.js";
|
|
14
|
+
function requireProlog(context) {
|
|
15
|
+
if (context.prolog === undefined) {
|
|
16
|
+
throw new Error("Proof explain requires a Prolog runtime");
|
|
17
|
+
}
|
|
18
|
+
return context.prolog;
|
|
19
|
+
}
|
|
20
|
+
function asRecord(value) {
|
|
21
|
+
return value !== null && typeof value === "object"
|
|
22
|
+
? value
|
|
23
|
+
: {};
|
|
24
|
+
}
|
|
25
|
+
function asString(value, fallback = "") {
|
|
26
|
+
return typeof value === "string" ? value : fallback;
|
|
27
|
+
}
|
|
28
|
+
function asStringArray(value) {
|
|
29
|
+
if (!Array.isArray(value))
|
|
30
|
+
return [];
|
|
31
|
+
return value.map((item) => asString(item)).filter((item) => item !== "");
|
|
32
|
+
}
|
|
33
|
+
function asBoolean(value) {
|
|
34
|
+
return value === true;
|
|
35
|
+
}
|
|
36
|
+
function asRef(value) {
|
|
37
|
+
const row = asRecord(value);
|
|
38
|
+
return {
|
|
39
|
+
...(typeof row.id === "string" ? { id: row.id } : {}),
|
|
40
|
+
...(typeof row.sourceFile === "string"
|
|
41
|
+
? { sourceFile: row.sourceFile }
|
|
42
|
+
: {}),
|
|
43
|
+
...(typeof row.path === "string" ? { path: row.path } : {}),
|
|
44
|
+
...(typeof row.role === "string" ? { role: row.role } : {}),
|
|
45
|
+
...(row.exists === true || row.exists === false
|
|
46
|
+
? { exists: row.exists }
|
|
47
|
+
: {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function asCandidate(value) {
|
|
51
|
+
const row = asRecord(value);
|
|
52
|
+
return {
|
|
53
|
+
testId: asString(row.testId),
|
|
54
|
+
relationship: asString(row.relationship, "covered_by"),
|
|
55
|
+
qualifies: asBoolean(row.qualifies),
|
|
56
|
+
reason: asString(row.reason),
|
|
57
|
+
...(typeof row.reasonText === "string"
|
|
58
|
+
? { reasonText: row.reasonText }
|
|
59
|
+
: {}),
|
|
60
|
+
secondaryReasons: asStringArray(row.secondaryReasons),
|
|
61
|
+
...(typeof row.scope === "string" ? { scope: row.scope } : {}),
|
|
62
|
+
...(typeof row.receiptState === "string"
|
|
63
|
+
? { receiptState: row.receiptState }
|
|
64
|
+
: {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function asSymbolBlock(value) {
|
|
68
|
+
const row = asRecord(value);
|
|
69
|
+
return {
|
|
70
|
+
symbolId: asString(row.symbolId),
|
|
71
|
+
classification: asString(row.classification, "unknown"),
|
|
72
|
+
status: asString(row.status),
|
|
73
|
+
reason: asString(row.reason),
|
|
74
|
+
...(typeof row.reasonText === "string"
|
|
75
|
+
? { reasonText: row.reasonText }
|
|
76
|
+
: {}),
|
|
77
|
+
coverageCandidates: Array.isArray(row.coverageCandidates)
|
|
78
|
+
? row.coverageCandidates.map(asCandidate)
|
|
79
|
+
: [],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
83
|
+
export function projectRequirementExplain(proof) {
|
|
84
|
+
const stages = asRecord(proof.proofStages);
|
|
85
|
+
const scenarioStage = asRecord(stages.scenarios);
|
|
86
|
+
const scenarioTestStage = asRecord(stages.scenarioTests);
|
|
87
|
+
const production = asRecord(stages.productionSymbols);
|
|
88
|
+
const obligations = Array.isArray(scenarioTestStage.obligations)
|
|
89
|
+
? scenarioTestStage.obligations.map(asRecord)
|
|
90
|
+
: [];
|
|
91
|
+
const resolutions = Array.isArray(proof.testResolutions ?? scenarioTestStage.resolutions)
|
|
92
|
+
? (proof.testResolutions ?? scenarioTestStage.resolutions)
|
|
93
|
+
: [];
|
|
94
|
+
const resolutionByTest = new Map();
|
|
95
|
+
for (const item of resolutions) {
|
|
96
|
+
const row = asRecord(item);
|
|
97
|
+
const testId = asString(row.testId);
|
|
98
|
+
if (testId === "")
|
|
99
|
+
continue;
|
|
100
|
+
resolutionByTest.set(testId, {
|
|
101
|
+
testId,
|
|
102
|
+
requiredProofs: Array.isArray(row.requiredProofs)
|
|
103
|
+
? row.requiredProofs.map(asRef)
|
|
104
|
+
: [],
|
|
105
|
+
executableFor: Array.isArray(row.executableFor)
|
|
106
|
+
? row.executableFor.map(asRef)
|
|
107
|
+
: [],
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const tests = asStringArray(scenarioTestStage.tests).map((testId) => {
|
|
111
|
+
return (resolutionByTest.get(testId) ?? {
|
|
112
|
+
testId,
|
|
113
|
+
requiredProofs: [],
|
|
114
|
+
executableFor: [],
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
const scenarios = (Array.isArray(scenarioStage.scenarios)
|
|
118
|
+
? scenarioStage.scenarios
|
|
119
|
+
: asStringArray(scenarioStage.scenarios)).map((scenarioId) => {
|
|
120
|
+
const id = asString(scenarioId);
|
|
121
|
+
const obligation = obligations.find((item) => asString(item.scenarioId) === id);
|
|
122
|
+
return {
|
|
123
|
+
id,
|
|
124
|
+
tests: asStringArray(obligation?.tests),
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
const explanations = Array.isArray(production.explanations)
|
|
128
|
+
? production.explanations.map(asSymbolBlock)
|
|
129
|
+
: [];
|
|
130
|
+
const structuralIds = new Set(asStringArray(production.structuralSymbols));
|
|
131
|
+
return {
|
|
132
|
+
kind: "requirement",
|
|
133
|
+
id: asString(proof.id),
|
|
134
|
+
proofVersion: asString(proof.proofVersion),
|
|
135
|
+
proofStatus: asString(proof.proofStatus),
|
|
136
|
+
proofGaps: asStringArray(proof.proofGaps),
|
|
137
|
+
scenarios,
|
|
138
|
+
tests,
|
|
139
|
+
productionSymbols: explanations.filter((item) => !structuralIds.has(item.symbolId)),
|
|
140
|
+
structuralSymbols: explanations.filter((item) => structuralIds.has(item.symbolId)),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
144
|
+
export function projectSymbolExplain(payload) {
|
|
145
|
+
const proofs = Array.isArray(payload.proofs)
|
|
146
|
+
? payload.proofs.map((item) => projectRequirementExplain(asRecord(item)))
|
|
147
|
+
: [];
|
|
148
|
+
return {
|
|
149
|
+
kind: "symbol",
|
|
150
|
+
id: asString(payload.symbolId ?? payload.id),
|
|
151
|
+
role: asString(payload.role, "unknown"),
|
|
152
|
+
executableTest: asBoolean(payload.executableTest),
|
|
153
|
+
implementingRequirements: asStringArray(payload.implementingRequirements),
|
|
154
|
+
executableFor: asStringArray(payload.executableFor),
|
|
155
|
+
coveredBy: asStringArray(payload.coveredBy),
|
|
156
|
+
proofs,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
160
|
+
export function renderProofExplain(view) {
|
|
161
|
+
if (view.kind === "symbol")
|
|
162
|
+
return renderSymbolExplain(view);
|
|
163
|
+
return renderRequirementExplain(view);
|
|
164
|
+
}
|
|
165
|
+
function renderRequirementExplain(view) {
|
|
166
|
+
const lines = [
|
|
167
|
+
`Proof explain for ${view.id}`,
|
|
168
|
+
`proofVersion: ${view.proofVersion}`,
|
|
169
|
+
`proofStatus: ${view.proofStatus}`,
|
|
170
|
+
`proofGaps: ${view.proofGaps.join(", ") || "(none)"}`,
|
|
171
|
+
"",
|
|
172
|
+
"Scenarios",
|
|
173
|
+
];
|
|
174
|
+
if (view.scenarios.length === 0)
|
|
175
|
+
lines.push(" (none)");
|
|
176
|
+
for (const scenario of view.scenarios) {
|
|
177
|
+
lines.push(` ${scenario.id} -> ${scenario.tests.join(", ") || "(no tests)"}`);
|
|
178
|
+
}
|
|
179
|
+
lines.push("");
|
|
180
|
+
for (const test of view.tests) {
|
|
181
|
+
lines.push(`TEST ${test.testId}`);
|
|
182
|
+
lines.push(" required_proofs");
|
|
183
|
+
if (test.requiredProofs.length === 0)
|
|
184
|
+
lines.push(" (none)");
|
|
185
|
+
for (const ref of test.requiredProofs) {
|
|
186
|
+
lines.push(` ${formatRef(ref)}`);
|
|
187
|
+
}
|
|
188
|
+
lines.push(" executable_for");
|
|
189
|
+
if (test.executableFor.length === 0)
|
|
190
|
+
lines.push(" (none)");
|
|
191
|
+
for (const ref of test.executableFor) {
|
|
192
|
+
lines.push(` ${formatRef(ref)}`);
|
|
193
|
+
}
|
|
194
|
+
lines.push("");
|
|
195
|
+
}
|
|
196
|
+
lines.push("Production symbols");
|
|
197
|
+
if (view.productionSymbols.length === 0)
|
|
198
|
+
lines.push(" (none)");
|
|
199
|
+
for (const symbol of view.productionSymbols) {
|
|
200
|
+
lines.push(renderSymbolCoverage(symbol));
|
|
201
|
+
}
|
|
202
|
+
if (view.structuralSymbols.length > 0) {
|
|
203
|
+
lines.push("");
|
|
204
|
+
lines.push("Structural symbols");
|
|
205
|
+
for (const symbol of view.structuralSymbols) {
|
|
206
|
+
lines.push(renderSymbolCoverage(symbol));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return `${lines.join("\n")}\n`;
|
|
210
|
+
}
|
|
211
|
+
function renderSymbolExplain(view) {
|
|
212
|
+
const lines = [
|
|
213
|
+
`Proof explain for ${view.id}`,
|
|
214
|
+
`role: ${view.role}`,
|
|
215
|
+
`executable_test: ${view.executableTest ? "true" : "false"}`,
|
|
216
|
+
`implementing requirements: ${view.implementingRequirements.join(", ") || "(none)"}`,
|
|
217
|
+
"executable_for",
|
|
218
|
+
...(view.executableFor.length === 0
|
|
219
|
+
? [" (none)"]
|
|
220
|
+
: view.executableFor.map((id) => ` ${id}`)),
|
|
221
|
+
"covered_by",
|
|
222
|
+
...(view.coveredBy.length === 0
|
|
223
|
+
? [" (none)"]
|
|
224
|
+
: view.coveredBy.map((id) => ` ${id}`)),
|
|
225
|
+
];
|
|
226
|
+
for (const proof of view.proofs) {
|
|
227
|
+
lines.push("");
|
|
228
|
+
lines.push(`Requirement ${proof.id} (${proof.proofStatus})`);
|
|
229
|
+
lines.push(renderRequirementExplain(proof).trimEnd());
|
|
230
|
+
}
|
|
231
|
+
return `${lines.join("\n")}\n`;
|
|
232
|
+
}
|
|
233
|
+
function renderSymbolCoverage(symbol) {
|
|
234
|
+
const lines = [
|
|
235
|
+
` ${symbol.symbolId} [${symbol.classification}] ${symbol.status} ${symbol.reason}`,
|
|
236
|
+
" covered_by",
|
|
237
|
+
];
|
|
238
|
+
if (symbol.coverageCandidates.length === 0)
|
|
239
|
+
lines.push(" (none)");
|
|
240
|
+
for (const candidate of symbol.coverageCandidates) {
|
|
241
|
+
const secondaries = candidate.secondaryReasons.length > 0
|
|
242
|
+
? ` secondary=${candidate.secondaryReasons.join(",")}`
|
|
243
|
+
: "";
|
|
244
|
+
lines.push(` ${candidate.testId} qualifies=${candidate.qualifies} reason=${candidate.reason}${secondaries}`);
|
|
245
|
+
}
|
|
246
|
+
return lines.join("\n");
|
|
247
|
+
}
|
|
248
|
+
function formatRef(ref) {
|
|
249
|
+
const id = ref.id ?? "(unresolved)";
|
|
250
|
+
const path = ref.path ?? ref.sourceFile ?? "";
|
|
251
|
+
const role = ref.role ? ` role=${ref.role}` : "";
|
|
252
|
+
return path === "" ? `${id}${role}` : `${id} ${path}${role}`;
|
|
253
|
+
}
|
|
254
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
255
|
+
export function classifyExplainTarget(id, options) {
|
|
256
|
+
if (options.requirement && options.symbol) {
|
|
257
|
+
throw new Error("proof explain: choose either --requirement or --symbol, not both");
|
|
258
|
+
}
|
|
259
|
+
if (options.requirement)
|
|
260
|
+
return { kind: "requirement", id: options.requirement };
|
|
261
|
+
if (options.symbol)
|
|
262
|
+
return { kind: "symbol", id: options.symbol };
|
|
263
|
+
if (id?.startsWith("REQ-"))
|
|
264
|
+
return { kind: "requirement", id };
|
|
265
|
+
if (id?.startsWith("SYM-"))
|
|
266
|
+
return { kind: "symbol", id };
|
|
267
|
+
throw new Error("proof explain: pass REQ-* or SYM-* (or --requirement / --symbol)");
|
|
268
|
+
}
|
|
269
|
+
async function proofQueryContext(context) {
|
|
270
|
+
const snapshotEvidence = await readWorkspaceSnapshot(context);
|
|
271
|
+
const snapshot = snapshotEvidence.available
|
|
272
|
+
? snapshotEvidence.snapshot.hash
|
|
273
|
+
: "unknown";
|
|
274
|
+
const checkedAt = context.clock().toISOString();
|
|
275
|
+
const bindingsDict = await perContractTestBindings(context);
|
|
276
|
+
const bindingMode = bindingsDict !== null ? "per_contract" : currentProofBindingMode();
|
|
277
|
+
const bindings = bindingsDict ?? "_{}";
|
|
278
|
+
return {
|
|
279
|
+
snapshot,
|
|
280
|
+
checkedAt,
|
|
281
|
+
bindingsGoal: `${bindingMode}, ${bindings}, ${toPrologAtom(snapshot)}, ${toPrologAtom(checkedAt)}, ${PROOF_RECEIPT_MAX_AGE_SECONDS}`,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
285
|
+
export async function executeProofExplain(target, context) {
|
|
286
|
+
const query = await proofQueryContext(context);
|
|
287
|
+
if (target.kind === "requirement") {
|
|
288
|
+
const payload = await runOperationJsonQuery(requireProlog(context), "discovery.pl", `discovery:requirement_proof_json(${toPrologAtom(target.id)}, ${query.bindingsGoal}, JsonString)`, "Proof explain");
|
|
289
|
+
const withId = { id: target.id, ...payload };
|
|
290
|
+
return { view: projectRequirementExplain(withId), payload: withId };
|
|
291
|
+
}
|
|
292
|
+
const payload = await runOperationJsonQuery(requireProlog(context), "discovery.pl", `discovery:symbol_proof_json(${toPrologAtom(target.id)}, ${query.bindingsGoal}, JsonString)`, "Proof explain");
|
|
293
|
+
return { view: projectSymbolExplain(payload), payload };
|
|
294
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { OperationContext } from "../../public/operations/runtime-types.js";
|
|
2
|
+
import { type CoverageRowLike, type RequirementFingerprints, diffFingerprints } from "./baseline-diff.js";
|
|
3
|
+
export declare const COMMITTED_BASELINE_PATH = "proof/baseline.json";
|
|
4
|
+
export type ProofBaselineFile = {
|
|
5
|
+
readonly version?: string;
|
|
6
|
+
readonly mode?: string;
|
|
7
|
+
readonly currentRequirements?: number;
|
|
8
|
+
readonly proofProven?: number;
|
|
9
|
+
readonly currentUnproven?: number;
|
|
10
|
+
readonly trackedGaps?: Record<string, number>;
|
|
11
|
+
readonly requirements?: RequirementFingerprints;
|
|
12
|
+
};
|
|
13
|
+
export type ProofImpactResult = {
|
|
14
|
+
readonly comparisonTarget: typeof COMMITTED_BASELINE_PATH;
|
|
15
|
+
readonly baselineVersion: string;
|
|
16
|
+
readonly currentRequirements: number;
|
|
17
|
+
readonly proofProven: number;
|
|
18
|
+
readonly currentUnproven: number;
|
|
19
|
+
readonly changes: ReturnType<typeof diffFingerprints>;
|
|
20
|
+
};
|
|
21
|
+
export type ReadCommittedFile = (workspaceRoot: string, relativePath: string) => string | Promise<string>;
|
|
22
|
+
export type ProofImpactExecuteOptions = {
|
|
23
|
+
readonly readCommittedFile?: ReadCommittedFile;
|
|
24
|
+
readonly loadCoverage?: (context: OperationContext) => Promise<{
|
|
25
|
+
structuredContent: unknown;
|
|
26
|
+
}>;
|
|
27
|
+
};
|
|
28
|
+
export declare function readCommittedHeadFile(workspaceRoot: string, relativePath: string): string;
|
|
29
|
+
export declare function loadCommittedBaseline(workspaceRoot: string, readCommittedFile?: ReadCommittedFile): Promise<ProofBaselineFile>;
|
|
30
|
+
/** Diagnostic only: successful evaluation always exits 0. Ratchet lives in check-proof-baseline.mjs. */
|
|
31
|
+
export declare function proofImpactExitCode(_result: ProofImpactResult): 0;
|
|
32
|
+
export declare function buildProofImpact(baseline: ProofBaselineFile, coverage: {
|
|
33
|
+
summary?: Record<string, unknown>;
|
|
34
|
+
rows?: unknown;
|
|
35
|
+
}): {
|
|
36
|
+
result: ProofImpactResult;
|
|
37
|
+
rows: CoverageRowLike[];
|
|
38
|
+
};
|
|
39
|
+
export declare function renderProofImpact(result: ProofImpactResult, rows: readonly CoverageRowLike[]): string;
|
|
40
|
+
export declare function executeProofImpact(context: OperationContext, options?: ProofImpactExecuteOptions): Promise<{
|
|
41
|
+
result: ProofImpactResult;
|
|
42
|
+
rows: CoverageRowLike[];
|
|
43
|
+
text: string;
|
|
44
|
+
}>;
|
|
45
|
+
//# sourceMappingURL=impact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.d.ts","sourceRoot":"","sources":["../../../src/operations/proof/impact.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,gBAAgB,EAGjB,MAAM,oBAAoB,CAAC;AAG5B,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,YAAY,CAAC,EAAE,uBAAuB,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,OAAO,uBAAuB,CAAC;IAC1D,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,KACjB,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAE9B,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,CACtB,OAAO,EAAE,gBAAgB,KACtB,OAAO,CAAC;QAAE,iBAAiB,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9C,CAAC;AAgDF,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,GACnB,MAAM,CAcR;AAGD,wBAAsB,qBAAqB,CACzC,aAAa,EAAE,MAAM,EACrB,iBAAiB,GAAE,iBAAyC,GAC3D,OAAO,CAAC,iBAAiB,CAAC,CAS5B;AAED,wGAAwG;AACxG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,CAAC,CAEjE;AAGD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9D;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,eAAe,EAAE,CAAA;CAAE,CAuBxD;AAGD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,SAAS,eAAe,EAAE,GAC/B,MAAM,CAYR;AAGD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,iBAAiB,CAAC;IAC1B,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CAmBD"}
|
|
@@ -0,0 +1,118 @@
|
|
|
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
|
+
import { execFileSync } from "node:child_process";
|
|
11
|
+
import { executeCoverage } from "../../public/operations/specs/reporting.js";
|
|
12
|
+
import { diffFingerprints, fingerprintRequirements, renderRequirementDiffs, } from "./baseline-diff.js";
|
|
13
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
14
|
+
export const COMMITTED_BASELINE_PATH = "proof/baseline.json";
|
|
15
|
+
function asNumber(value) {
|
|
16
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
17
|
+
}
|
|
18
|
+
function gitErrorText(error) {
|
|
19
|
+
if (error && typeof error === "object" && "stderr" in error) {
|
|
20
|
+
const stderr = error.stderr;
|
|
21
|
+
if (typeof stderr === "string" && stderr.trim() !== "")
|
|
22
|
+
return stderr;
|
|
23
|
+
if (Buffer.isBuffer(stderr) && stderr.length > 0) {
|
|
24
|
+
return stderr.toString("utf8");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return error instanceof Error ? error.message : String(error);
|
|
28
|
+
}
|
|
29
|
+
function committedBaselineReadError(relativePath, error) {
|
|
30
|
+
const text = gitErrorText(error);
|
|
31
|
+
if (/not a git repository/i.test(text) ||
|
|
32
|
+
/ambiguous argument 'HEAD'/i.test(text) ||
|
|
33
|
+
/unknown revision/i.test(text) ||
|
|
34
|
+
/needed a single revision/i.test(text) ||
|
|
35
|
+
/bad revision 'HEAD'/i.test(text)) {
|
|
36
|
+
return new Error(`proof impact: repository is not in a suitable Git state to read HEAD:${relativePath}`);
|
|
37
|
+
}
|
|
38
|
+
if (/does not exist in 'HEAD'/i.test(text) ||
|
|
39
|
+
/exists on disk, but not in 'HEAD'/i.test(text) ||
|
|
40
|
+
/path '.*' does not exist/i.test(text)) {
|
|
41
|
+
return new Error(`proof impact: HEAD:${relativePath} is missing or unreadable`);
|
|
42
|
+
}
|
|
43
|
+
return new Error(`proof impact: could not read HEAD:${relativePath}: ${text.trim()}`);
|
|
44
|
+
}
|
|
45
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
46
|
+
export function readCommittedHeadFile(workspaceRoot, relativePath) {
|
|
47
|
+
try {
|
|
48
|
+
return execFileSync("git", ["-C", workspaceRoot, "show", `HEAD:${relativePath}`], {
|
|
49
|
+
encoding: "utf8",
|
|
50
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
51
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw committedBaselineReadError(relativePath, error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
59
|
+
export async function loadCommittedBaseline(workspaceRoot, readCommittedFile = readCommittedHeadFile) {
|
|
60
|
+
const raw = await readCommittedFile(workspaceRoot, COMMITTED_BASELINE_PATH);
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(raw);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new Error(`proof impact: HEAD:${COMMITTED_BASELINE_PATH} is not valid JSON`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Diagnostic only: successful evaluation always exits 0. Ratchet lives in check-proof-baseline.mjs. */
|
|
69
|
+
export function proofImpactExitCode(_result) {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
73
|
+
export function buildProofImpact(baseline, coverage) {
|
|
74
|
+
const rows = Array.isArray(coverage.rows)
|
|
75
|
+
? coverage.rows
|
|
76
|
+
: [];
|
|
77
|
+
const summary = coverage.summary ?? {};
|
|
78
|
+
const proofNotApplicable = asNumber(summary.proofNotApplicable);
|
|
79
|
+
const total = asNumber(summary.total);
|
|
80
|
+
const proofProven = asNumber(summary.proofProven);
|
|
81
|
+
const currentUnproven = asNumber(summary.proofMissing) + asNumber(summary.proofUnresolved);
|
|
82
|
+
const current = fingerprintRequirements(rows);
|
|
83
|
+
const changes = diffFingerprints(baseline.requirements, current);
|
|
84
|
+
return {
|
|
85
|
+
rows,
|
|
86
|
+
result: {
|
|
87
|
+
comparisonTarget: COMMITTED_BASELINE_PATH,
|
|
88
|
+
baselineVersion: baseline.version ?? "unknown",
|
|
89
|
+
currentRequirements: total - proofNotApplicable,
|
|
90
|
+
proofProven,
|
|
91
|
+
currentUnproven,
|
|
92
|
+
changes,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
97
|
+
export function renderProofImpact(result, rows) {
|
|
98
|
+
const header = [
|
|
99
|
+
`Proof impact compared to committed ${result.comparisonTarget}`,
|
|
100
|
+
`baseline version: ${result.baselineVersion}`,
|
|
101
|
+
`current requirements: ${result.currentRequirements}`,
|
|
102
|
+
`proofProven: ${result.proofProven}`,
|
|
103
|
+
`currentUnproven: ${result.currentUnproven}`,
|
|
104
|
+
`changed requirements: ${result.changes.length}`,
|
|
105
|
+
"",
|
|
106
|
+
];
|
|
107
|
+
const diff = renderRequirementDiffs(result.changes, rows);
|
|
108
|
+
return `${header.join("\n")}${diff || "No requirement fingerprint changes versus the committed baseline.\n"}`;
|
|
109
|
+
}
|
|
110
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
111
|
+
export async function executeProofImpact(context, options = {}) {
|
|
112
|
+
const baseline = await loadCommittedBaseline(context.workspaceRoot, options.readCommittedFile ?? readCommittedHeadFile);
|
|
113
|
+
const coverage = options.loadCoverage
|
|
114
|
+
? await options.loadCoverage(context)
|
|
115
|
+
: await executeCoverage({ by: "req", includePassing: true, limit: 100_000, offset: 0 }, context);
|
|
116
|
+
const { result, rows } = buildProofImpact(baseline, coverage.structuredContent);
|
|
117
|
+
return { result, rows, text: renderProofImpact(result, rows) };
|
|
118
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingest-proof.d.ts","sourceRoot":"","sources":["../../../src/operations/proof/ingest-proof.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ingest-proof.d.ts","sourceRoot":"","sources":["../../../src/operations/proof/ingest-proof.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,QAAQ,EAEd,MAAM,yBAAyB,CAAC;AAOjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAkBjF,OAAO,EAEL,KAAK,YAAY,EAGlB,MAAM,+BAA+B,CAAC;AAUvC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B,CAAC,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;CAC3B,CAAC,CAAC;AAGH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC3C,CAAC,CAAC;AAoKH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,iBAAiB,CAAC;CACtC,CAAC,CAgSD"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { resolveBoundSymbolScope } from "../../extractors/manifest.js";
|
|
1
3
|
import { evaluateContractAgainstRun, } from "../../proof/evaluate.js";
|
|
2
4
|
import { loadProofIntegrations, resolveIntegration, toExecution, } from "../../proof/integrations.js";
|
|
3
5
|
import { loadEntities } from "../../public/operations/discovery-entities.js";
|
|
@@ -6,8 +8,6 @@ import { environmentHash as canonicalEnvironmentHash, effectiveProofFingerprint,
|
|
|
6
8
|
import { PROOF_CONTRACT_VERSION, PROOF_RECEIPT_VERSION, proofBindingsErrors, proofRunArtifactErrors, } from "../../public/proof-protocol.js";
|
|
7
9
|
import { MAX_PROOF_RECEIPTS, proofReceiptCurrentBindingErrors, proofReceiptHistoryErrors, } from "../../public/proof-receipt.js";
|
|
8
10
|
import { projectEntityProperties } from "../mutation/entity-projection.js";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import { resolveBoundSymbolScope } from "../../extractors/manifest.js";
|
|
11
11
|
import { resolveContainedSourcePath } from "../mutation/source-authoring.js";
|
|
12
12
|
import { executeUpsert } from "../mutation/upsert.js";
|
|
13
13
|
import { patchReceiptsIntoDocument, removeFrontmatterBlock, } from "./receipt-document.js";
|
|
@@ -74,12 +74,52 @@ function parseBindings(test) {
|
|
|
74
74
|
};
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
+
function enforceNativeCaseBindings(testId, contract, bindings, artifact) {
|
|
78
|
+
const requiredKeys = new Set(contract.required_proofs.map((obligation) => `${obligation.target}\0${obligation.symbol_id}`));
|
|
79
|
+
const nativeResults = artifact.proof_results.filter((result) => result.binding === "native_case" &&
|
|
80
|
+
requiredKeys.has(`${result.target}\0${result.symbol_id}`));
|
|
81
|
+
const seenNativeIds = new Set();
|
|
82
|
+
for (const result of nativeResults) {
|
|
83
|
+
const nativeId = typeof result.native_id === "string" ? result.native_id : "";
|
|
84
|
+
const resultLabel = `${result.symbol_id}/${result.target}`;
|
|
85
|
+
if (!nativeId) {
|
|
86
|
+
throw new Error(`Proof ingest failed: test ${testId} native_case result ${resultLabel} has no native_id`);
|
|
87
|
+
}
|
|
88
|
+
if (seenNativeIds.has(nativeId)) {
|
|
89
|
+
throw new Error(`Proof ingest failed: test ${testId} duplicate native result for native_id '${nativeId}'`);
|
|
90
|
+
}
|
|
91
|
+
seenNativeIds.add(nativeId);
|
|
92
|
+
const matchingBindings = bindings.filter((binding) => binding.symbol_id === result.symbol_id &&
|
|
93
|
+
binding.target === result.target);
|
|
94
|
+
if (matchingBindings.length === 0) {
|
|
95
|
+
throw new Error(`Proof ingest failed: test ${testId} native_case result ${resultLabel} has no matching proof_binding`);
|
|
96
|
+
}
|
|
97
|
+
if (matchingBindings.length !== 1) {
|
|
98
|
+
throw new Error(`Proof ingest failed: test ${testId} native_case result ${resultLabel} has an ambiguous proof_binding`);
|
|
99
|
+
}
|
|
100
|
+
const binding = matchingBindings[0];
|
|
101
|
+
if (binding === undefined) {
|
|
102
|
+
throw new Error(`Proof ingest failed: test ${testId} native_case result ${resultLabel} has an ambiguous proof_binding`);
|
|
103
|
+
}
|
|
104
|
+
const boundNativeIds = [
|
|
105
|
+
binding.native_id,
|
|
106
|
+
...(binding.aliases ?? []),
|
|
107
|
+
].filter((value) => typeof value === "string" && value !== "");
|
|
108
|
+
if (!boundNativeIds.includes(nativeId)) {
|
|
109
|
+
throw new Error(`Proof ingest failed: test ${testId} native_case result ${resultLabel} native_id '${nativeId}' does not match its proof_binding native_id or aliases`);
|
|
110
|
+
}
|
|
111
|
+
const ambiguousOwners = bindings.filter((candidate) => [candidate.native_id, ...(candidate.aliases ?? [])].includes(nativeId));
|
|
112
|
+
if (ambiguousOwners.length !== 1) {
|
|
113
|
+
throw new Error(`Proof ingest failed: test ${testId} native_id '${nativeId}' has an ambiguous proof_binding`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
77
117
|
function existingReceipts(test) {
|
|
78
118
|
return Array.isArray(test.proof_receipts)
|
|
79
119
|
? test.proof_receipts.filter(record).map((value) => ({ ...value }))
|
|
80
120
|
: [];
|
|
81
121
|
}
|
|
82
|
-
// implements REQ-kibi-
|
|
122
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
83
123
|
export async function executeIngestProof(args, context) {
|
|
84
124
|
if (!context.prolog)
|
|
85
125
|
throw new Error("Proof ingest requires a Prolog runtime");
|
|
@@ -147,6 +187,7 @@ export async function executeIngestProof(args, context) {
|
|
|
147
187
|
if (jsonDigest(artifact.command_argv) !== jsonDigest([...integration.command]))
|
|
148
188
|
throw new Error(`Proof ingest failed: artifact command_argv does not match the configured command for integration '${effectiveIntegrationId}'`);
|
|
149
189
|
const bindings = parseBindings(test);
|
|
190
|
+
enforceNativeCaseBindings(testId, contract, bindings, artifact);
|
|
150
191
|
const { fingerprint, components } = effectiveProofFingerprint({
|
|
151
192
|
contract,
|
|
152
193
|
integration: toExecution(integration),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate-legacy-receipts.d.ts","sourceRoot":"","sources":["../../../src/operations/proof/migrate-legacy-receipts.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAOjF,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,yCAAyC;IAEzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAGH,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IAEjD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/C,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AAEH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,yBAAyB,EAC/B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,2BAA2B,CAAC;CAChD,CAAC,
|
|
1
|
+
{"version":3,"file":"migrate-legacy-receipts.d.ts","sourceRoot":"","sources":["../../../src/operations/proof/migrate-legacy-receipts.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAOjF,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,yCAAyC;IAEzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAGH,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IAEjD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/C,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AAEH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,yBAAyB,EAC/B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,2BAA2B,CAAC;CAChD,CAAC,CAkFD"}
|
|
@@ -31,7 +31,7 @@ import { removeFrontmatterBlock } from "./receipt-document.js";
|
|
|
31
31
|
* the authored document and the compiled entity — one block splice, no
|
|
32
32
|
* canonical re-render.
|
|
33
33
|
*/
|
|
34
|
-
// implements REQ-kibi-
|
|
34
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
35
35
|
export async function executeMigrateLegacyReceipts(args, context) {
|
|
36
36
|
if (!context.prolog)
|
|
37
37
|
throw new Error("Legacy receipt migration requires a Prolog runtime");
|
|
@@ -47,35 +47,39 @@ export async function executeMigrateLegacyReceipts(args, context) {
|
|
|
47
47
|
const migratedTests = [];
|
|
48
48
|
for (const test of tests) {
|
|
49
49
|
const testId = String(test.id);
|
|
50
|
-
|
|
50
|
+
// The compiled lane only survives in stores compiled by older versions:
|
|
51
|
+
// current sync does not extract verification_receipts, so the authored
|
|
52
|
+
// document is the durable carrier of the legacy block. Both signals must
|
|
53
|
+
// trigger the migration.
|
|
54
|
+
const hasCompiledLegacy = Array.isArray(test.verification_receipts);
|
|
51
55
|
const hasProofContract = test.proof_contract !== null && typeof test.proof_contract === "object";
|
|
52
|
-
if (!
|
|
56
|
+
if (!hasProofContract)
|
|
53
57
|
continue;
|
|
54
58
|
const source = typeof test.source === "string" ? test.source : "";
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
patchedContent =
|
|
60
|
-
removeFrontmatterBlock(before, "verification_receipts") ?? undefined;
|
|
61
|
-
if (patchedContent === undefined) {
|
|
62
|
-
throw new Error(`Legacy receipt migration failed for ${testId}: ${source} is not a patchable frontmatter document`);
|
|
59
|
+
const patchableSource = source !== "" && /\.(md|mdx)$/i.test(source) && context.fs !== undefined;
|
|
60
|
+
if (!patchableSource) {
|
|
61
|
+
if (hasCompiledLegacy) {
|
|
62
|
+
throw new Error(`Legacy receipt migration failed for ${testId}: test source '${source || "unknown"}' is not an authored markdown document`);
|
|
63
63
|
}
|
|
64
|
+
continue;
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
const absolute = resolveContainedSourcePath(context.workspaceRoot, source);
|
|
67
|
+
const before = await context.fs.readFile(absolute);
|
|
68
|
+
const patchedContent = removeFrontmatterBlock(before, "verification_receipts");
|
|
69
|
+
if (patchedContent === null) {
|
|
70
|
+
if (hasCompiledLegacy) {
|
|
71
|
+
throw new Error(`Legacy receipt migration failed for ${testId}: ${source} is not a patchable frontmatter document`);
|
|
72
|
+
}
|
|
73
|
+
continue;
|
|
67
74
|
}
|
|
68
75
|
const properties = Object.fromEntries(Object.entries(projectEntityProperties(test)).filter(([key]) => key !== "verification_receipts"));
|
|
69
76
|
// Drop the legacy lane from the compiled entity; proof_receipts stay
|
|
70
77
|
// untouched so append-only validation keeps holding.
|
|
71
|
-
const upsertOptions = patchedContent === undefined
|
|
72
|
-
? {}
|
|
73
|
-
: { sourceDocumentOverride: patchedContent };
|
|
74
78
|
await executeUpsert({
|
|
75
79
|
type: "test",
|
|
76
80
|
id: testId,
|
|
77
81
|
properties: { ...properties },
|
|
78
|
-
}, context,
|
|
82
|
+
}, context, { sourceDocumentOverride: patchedContent });
|
|
79
83
|
migratedTests.push({ testId });
|
|
80
84
|
}
|
|
81
85
|
const summary = migratedTests.length === 0
|
|
@@ -37,7 +37,7 @@ function parseKeep(value) {
|
|
|
37
37
|
* with the prune carve-out (append-only validation is skipped for exactly
|
|
38
38
|
* this maintenance action).
|
|
39
39
|
*/
|
|
40
|
-
// implements REQ-kibi-
|
|
40
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
41
41
|
export async function executePruneReceipts(args, context) {
|
|
42
42
|
if (!context.prolog)
|
|
43
43
|
throw new Error("Proof prune requires a Prolog runtime");
|
|
@@ -25,7 +25,7 @@ import { dump as dumpYaml } from "js-yaml";
|
|
|
25
25
|
* Used by proof maintenance to drop legacy `verification_receipts` blocks
|
|
26
26
|
* once a `proof_contract` exists, without canonicalizing the document.
|
|
27
27
|
*/
|
|
28
|
-
// implements REQ-kibi-
|
|
28
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
29
29
|
export function removeFrontmatterBlock(content, key) {
|
|
30
30
|
const lines = content.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
31
31
|
if (lines.length === 0 || lines[0]?.trim() !== "---")
|
|
@@ -75,7 +75,7 @@ export function removeFrontmatterBlock(content, key) {
|
|
|
75
75
|
* patcher can safely splice; the caller then falls back to the canonical
|
|
76
76
|
* render.
|
|
77
77
|
*/
|
|
78
|
-
// implements REQ-kibi-
|
|
78
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
79
79
|
export function patchReceiptsIntoDocument(content, receipts) {
|
|
80
80
|
const lines = content.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
81
81
|
if (lines.length === 0 || lines[0]?.trim() !== "---")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analysis-receipt.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/analysis-receipt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analysis-receipt.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/analysis-receipt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EACL,KAAK,OAAO,EAMb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,6BAA6B,EAI7B,4BAA4B,EAC5B,0BAA0B,EAC1B,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAyBpB,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,SAAS,0BAA0B,EAAE,EAClD,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,YAAY,EAAE,SAAS,mBAAmB,EAAE,EAC5C,eAAe,EAAE,SAAS,4BAA4B,EAAE,EACxD,cAAc,EAAE,SAAS,iBAAiB,EAAE,GAC3C,6BAA6B,CAkH/B"}
|