kibi-cli 2.15.0 → 2.17.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-protocol.d.ts.map +1 -1
- package/dist/cli-protocol.js +13 -0
- package/dist/cli-register-proof.d.ts +6 -0
- package/dist/cli-register-proof.d.ts.map +1 -1
- package/dist/cli-register-proof.js +52 -2
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +5 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +83 -4
- package/dist/commands/prove.js +1 -1
- package/dist/operations/modeling/model-requirement.d.ts +5 -0
- package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
- package/dist/operations/modeling/model-requirement.js +17 -5
- package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applicability.js +7 -1
- package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applyplan.js +16 -6
- package/dist/operations/modeling/predicate-utils.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-utils.js +2 -3
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +38 -7
- package/dist/operations/planning/migration-refusal.d.ts +14 -0
- package/dist/operations/planning/migration-refusal.d.ts.map +1 -0
- package/dist/operations/planning/migration-refusal.js +17 -0
- package/dist/operations/semantic-advisor/shared.d.ts +7 -0
- package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/shared.js +22 -3
- package/dist/proof/inspect.d.ts +20 -1
- package/dist/proof/inspect.d.ts.map +1 -1
- package/dist/proof/inspect.js +17 -8
- package/dist/proof/integration-plan.d.ts +58 -0
- package/dist/proof/integration-plan.d.ts.map +1 -0
- package/dist/proof/integration-plan.js +348 -0
- package/dist/proof/integrations.js +1 -1
- package/dist/public/impact/coverage-depth-quality.d.ts.map +1 -1
- package/dist/public/impact/coverage-depth-quality.js +29 -1
- package/dist/public/operations/catalog.d.ts +2 -2
- package/dist/public/operations/check-executor.d.ts +16 -0
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +80 -0
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +13 -3
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +28 -0
- package/dist/public/operations/result-envelope.d.ts.map +1 -1
- package/dist/public/operations/result-envelope.js +23 -3
- 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/composite.d.ts +16 -3
- package/dist/public/operations/specs/composite.d.ts.map +1 -1
- package/dist/public/operations/specs/composite.js +79 -2
- package/dist/public/operations/specs/modeling.d.ts +1 -1
- package/dist/public/operations/specs/modeling.js +1 -1
- package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -1
- package/dist/public/skills/kibi-usage/SKILL.md +21 -7
- package/dist/public/skills/kibi-usage/resources/fact-lanes.md +3 -3
- package/dist/public/skills/kibi-usage/resources/proof.md +34 -4
- package/dist/utils/branch-store.d.ts +32 -0
- package/dist/utils/branch-store.d.ts.map +1 -1
- package/dist/utils/branch-store.js +122 -1
- 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/dist/utils/strict-modeling.d.ts +12 -0
- package/dist/utils/strict-modeling.d.ts.map +1 -1
- package/dist/utils/strict-modeling.js +12 -1
- package/package.json +2 -2
- package/src/public/impact/coverage-depth-quality.ts +46 -1
- package/src/public/operations/check-executor.ts +105 -0
- package/src/public/operations/discovery-executors.ts +19 -2
- package/src/public/operations/migration-plan.ts +35 -0
- package/src/public/operations/result-envelope.ts +31 -3
- package/src/public/operations/specs/check-rules.generated.ts +2 -1
- package/src/public/operations/specs/composite.ts +110 -11
- package/src/public/operations/specs/modeling.ts +1 -1
- package/src/public/skills/kibi-bootstrap/SKILL.md +2 -1
- package/src/public/skills/kibi-usage/SKILL.md +21 -7
- package/src/public/skills/kibi-usage/resources/fact-lanes.md +3 -3
- package/src/public/skills/kibi-usage/resources/proof.md +34 -4
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A migration action executor throws this when it refuses to run: a
|
|
3
|
+
* precondition failed before it changed anything (the planned file already
|
|
4
|
+
* exists, a source changed since planning, the action is malformed). An
|
|
5
|
+
* application whose only failures are refusals and that applied nothing
|
|
6
|
+
* reports `outcome: "refused"`, not `reconciliation_required`: there is
|
|
7
|
+
* nothing to reconcile.
|
|
8
|
+
*/
|
|
9
|
+
export declare class MigrationActionRefusedError extends Error {
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
12
|
+
/** Error code of the envelope for a migration plan that was refused. */
|
|
13
|
+
export declare const MIGRATION_PLAN_REFUSED: "MIGRATION_PLAN_REFUSED";
|
|
14
|
+
//# sourceMappingURL=migration-refusal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-refusal.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/migration-refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,EAAG,wBAAiC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A migration action executor throws this when it refuses to run: a
|
|
3
|
+
* precondition failed before it changed anything (the planned file already
|
|
4
|
+
* exists, a source changed since planning, the action is malformed). An
|
|
5
|
+
* application whose only failures are refusals and that applied nothing
|
|
6
|
+
* reports `outcome: "refused"`, not `reconciliation_required`: there is
|
|
7
|
+
* nothing to reconcile.
|
|
8
|
+
*/
|
|
9
|
+
// implements REQ-kibi-change-to-proof-plan-compiler-v2
|
|
10
|
+
export class MigrationActionRefusedError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "MigrationActionRefusedError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Error code of the envelope for a migration plan that was refused. */
|
|
17
|
+
export const MIGRATION_PLAN_REFUSED = "MIGRATION_PLAN_REFUSED";
|
|
@@ -19,6 +19,13 @@ export declare function statementOf(payload: Payload): string;
|
|
|
19
19
|
export declare function sourceOf(payload: Payload): string;
|
|
20
20
|
export declare function normalizeKey(value: string): string;
|
|
21
21
|
export declare function normalizePredicateToken(value: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Singularize one lowercase key token: `-ches`/`-shes`/`-sses`/`-xes` drop
|
|
24
|
+
* "es", `-ies` becomes "y", and any other trailing "s" is dropped unless the
|
|
25
|
+
* token ends in "ss", "us" or "is" ("access", "status", "analysis"). Tokens of
|
|
26
|
+
* three letters or fewer and the `keep` exceptions are returned unchanged.
|
|
27
|
+
*/
|
|
28
|
+
export declare function singularizeToken(value: string, keep?: readonly string[]): string;
|
|
22
29
|
export declare function singularize(value: string): string;
|
|
23
30
|
export declare function normalizeSubjectKey(value: string): string;
|
|
24
31
|
export declare function relationship(from: string, to: string, type: string): Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/shared.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAE1B,2BAA2B,EAC5B,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AACxD,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AACvE,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AACvE,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,UAAU,GAAG,OAAO,CAAC;AAGzE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASnD;AAGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAKpD;AAGD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG;IAClD,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAmBA;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,GACf,SAAS,MAAM,EAAE,GAAG,SAAS,CAO/B;AAGD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAGD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEtE;AAGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEpD;AAGD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAIjD;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAGD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/shared.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAE1B,2BAA2B,EAC5B,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AACxD,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AACvE,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AACvE,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,UAAU,GAAG,OAAO,CAAC;AAGzE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASnD;AAGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAKpD;AAGD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG;IAClD,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAmBA;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,GACf,SAAS,MAAM,EAAE,GAAG,SAAS,CAO/B;AAGD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAGD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEtE;AAGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEpD;AAGD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAIjD;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAGD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAID;;;;;GAKG;AAEH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,MAAM,EAAO,GAC3B,MAAM,CAUR;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB;AAGD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,2BAA2B,EAClC,SAAS,EAAE,MAAM,EACjB,UAAU,SAAM,GACf,0BAA0B,CA0F5B;AAGD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,QAAQ,GAAG,MAAiB,GACrC,0BAA0B,CA+C5B"}
|
|
@@ -90,11 +90,30 @@ export function normalizePredicateToken(value) {
|
|
|
90
90
|
.replace(/[^A-Za-z0-9_-]+/g, "_")
|
|
91
91
|
.replace(/^_+|_+$/g, "");
|
|
92
92
|
}
|
|
93
|
+
const ES_PLURAL_SUFFIXES = ["ches", "shes", "sses", "xes"];
|
|
94
|
+
/**
|
|
95
|
+
* Singularize one lowercase key token: `-ches`/`-shes`/`-sses`/`-xes` drop
|
|
96
|
+
* "es", `-ies` becomes "y", and any other trailing "s" is dropped unless the
|
|
97
|
+
* token ends in "ss", "us" or "is" ("access", "status", "analysis"). Tokens of
|
|
98
|
+
* three letters or fewer and the `keep` exceptions are returned unchanged.
|
|
99
|
+
*/
|
|
93
100
|
// implements REQ-mcp-semantic-advisor-preflight
|
|
94
|
-
export function
|
|
95
|
-
if (
|
|
101
|
+
export function singularizeToken(value, keep = []) {
|
|
102
|
+
if (value.length <= 3 || keep.includes(value))
|
|
103
|
+
return value;
|
|
104
|
+
if (value.endsWith("ies") && value.length > 4) {
|
|
105
|
+
return `${value.slice(0, -3)}y`;
|
|
106
|
+
}
|
|
107
|
+
if (ES_PLURAL_SUFFIXES.some((suffix) => value.endsWith(suffix))) {
|
|
108
|
+
return value.slice(0, -2);
|
|
109
|
+
}
|
|
110
|
+
if (!value.endsWith("s") || /(?:ss|us|is)$/.test(value))
|
|
96
111
|
return value;
|
|
97
|
-
return value.
|
|
112
|
+
return value.slice(0, -1);
|
|
113
|
+
}
|
|
114
|
+
// implements REQ-mcp-semantic-advisor-preflight
|
|
115
|
+
export function singularize(value) {
|
|
116
|
+
return singularizeToken(value, ["status", "results"]);
|
|
98
117
|
}
|
|
99
118
|
// implements REQ-mcp-semantic-advisor-preflight
|
|
100
119
|
export function normalizeSubjectKey(value) {
|
package/dist/proof/inspect.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { MigrationPlan } from "../public/operations/migration-plan.js";
|
|
2
|
+
import { type ProposedIntegration } from "./integration-plan.js";
|
|
1
3
|
export type ProofInspection = Readonly<{
|
|
2
4
|
languages: readonly string[];
|
|
3
5
|
buildSystems: readonly string[];
|
|
@@ -6,7 +8,24 @@ export type ProofInspection = Readonly<{
|
|
|
6
8
|
currentIntegration: string | null;
|
|
7
9
|
recommendation: string;
|
|
8
10
|
missing: readonly string[];
|
|
11
|
+
/** The integration Kibi proposes for the detected runner, if any. */
|
|
12
|
+
proposedIntegration: ProposedIntegration["integration"] | null;
|
|
13
|
+
/** What a proof-bearing test's proof_contract names for that integration. */
|
|
14
|
+
contractDefaults: ProposedIntegration["contractDefaults"] | null;
|
|
15
|
+
/**
|
|
16
|
+
* Hash-bound kibi.migration-plan.v2 that writes the integrations file;
|
|
17
|
+
* review it, then apply it with kb_apply_plan / `kibi apply-plan`.
|
|
18
|
+
*/
|
|
19
|
+
integrationPlan: MigrationPlan | null;
|
|
20
|
+
/** Why there is no plan, or what applying it does. */
|
|
21
|
+
integrationPlanReason: string;
|
|
22
|
+
}>;
|
|
23
|
+
export type ProofInspectionOptions = Readonly<{
|
|
24
|
+
/** Plan a replacement of this existing integration id. */
|
|
25
|
+
update?: string;
|
|
26
|
+
/** Command (argv) the proposed integration runs instead of the detected one. */
|
|
27
|
+
command?: readonly string[];
|
|
9
28
|
}>;
|
|
10
29
|
export declare function directoryEntriesMatch(directory: string, regex: RegExp): boolean;
|
|
11
|
-
export declare function inspectProofEnvironment(root: string): ProofInspection;
|
|
30
|
+
export declare function inspectProofEnvironment(root: string, options?: ProofInspectionOptions): ProofInspection;
|
|
12
31
|
//# sourceMappingURL=inspect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../src/proof/inspect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../src/proof/inspect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAC;AAM/B,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,qEAAqE;IACrE,mBAAmB,EAAE,mBAAmB,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;IAC/D,6EAA6E;IAC7E,gBAAgB,EAAE,mBAAmB,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAAC;IACjE;;;OAGG;IACH,eAAe,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC,sDAAsD;IACtD,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B,CAAC,CAAC;AAwIH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAMT;AA2BD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,sBAA2B,GACnC,eAAe,CAgGjB"}
|
package/dist/proof/inspect.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { buildProofIntegrationPlan, } from "./integration-plan.js";
|
|
4
|
+
import { PROOF_INTEGRATIONS_PATH, loadProofIntegrations, } from "./integrations.js";
|
|
4
5
|
/**
|
|
5
6
|
* Declarative detection manifests. Deterministic CLI logic consumes these;
|
|
6
7
|
* agents consume the inspection output and never reinvent runner discovery.
|
|
@@ -149,7 +150,7 @@ function directoryExists(root, relative) {
|
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
// implements REQ-kibi-verification-evidence-contract
|
|
152
|
-
export function inspectProofEnvironment(root) {
|
|
153
|
+
export function inspectProofEnvironment(root, options = {}) {
|
|
153
154
|
const languages = new Set();
|
|
154
155
|
const buildSystems = new Set();
|
|
155
156
|
const runners = [];
|
|
@@ -207,17 +208,21 @@ export function inspectProofEnvironment(root) {
|
|
|
207
208
|
const currentIntegration = integrations.available
|
|
208
209
|
? integrations.integrations.integrations.map((entry) => entry.id).join(", ")
|
|
209
210
|
: null;
|
|
211
|
+
const planned = buildProofIntegrationPlan(root, options);
|
|
212
|
+
const applyHint = "review integrationPlan, then apply it with kb_apply_plan (or `kibi apply-plan --input -`) passing plan, approvedPlanHash = plan.planHash and approvedActionIds";
|
|
210
213
|
let recommendation;
|
|
211
|
-
if (
|
|
212
|
-
|
|
214
|
+
if (planned.plan !== null && options.update !== undefined) {
|
|
215
|
+
recommendation = `To replace integration '${options.update}', ${applyHint}.`;
|
|
216
|
+
}
|
|
217
|
+
else if (!integrations.available) {
|
|
218
|
+
if (planned.plan === null) {
|
|
213
219
|
recommendation =
|
|
214
220
|
"No test harness detected. Proof integration is deferred until the first proof-bearing test introduces one; command proof can prove any command.";
|
|
215
|
-
missing.push(
|
|
221
|
+
missing.push(`${PROOF_INTEGRATIONS_PATH} (written by applying the plan from kibi proof inspect --json)`);
|
|
216
222
|
}
|
|
217
223
|
else {
|
|
218
|
-
recommendation =
|
|
219
|
-
|
|
220
|
-
missing.push(".kb/proof/integrations.json (created by bootstrap)");
|
|
224
|
+
recommendation = `Kibi proposes a command integration for the detected runner. To configure it, ${applyHint}. Proof-bearing tests then name it in proof_contract.integration and kibi prove runs it.`;
|
|
225
|
+
missing.push(`${PROOF_INTEGRATIONS_PATH} (written by applying integrationPlan)`);
|
|
221
226
|
}
|
|
222
227
|
}
|
|
223
228
|
else {
|
|
@@ -232,5 +237,9 @@ export function inspectProofEnvironment(root) {
|
|
|
232
237
|
currentIntegration,
|
|
233
238
|
recommendation,
|
|
234
239
|
missing,
|
|
240
|
+
proposedIntegration: planned.proposal?.integration ?? null,
|
|
241
|
+
contractDefaults: planned.proposal?.contractDefaults ?? null,
|
|
242
|
+
integrationPlan: planned.plan,
|
|
243
|
+
integrationPlanReason: planned.reason,
|
|
235
244
|
};
|
|
236
245
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type MigrationAction, type MigrationPlan } from "../public/operations/migration-plan.js";
|
|
2
|
+
import { PROOF_CONTRACT_VERSION } from "../public/proof-protocol.js";
|
|
3
|
+
import { type ProofIntegration } from "./integrations.js";
|
|
4
|
+
/** Migration action code of a reviewed proof integration write. */
|
|
5
|
+
export declare const PROOF_INTEGRATION_CONFIGURE = "proof_integration_configure";
|
|
6
|
+
export type ProposedIntegration = Readonly<{
|
|
7
|
+
integration: ProofIntegration;
|
|
8
|
+
/** The runner the proposal was derived from (for the review summary). */
|
|
9
|
+
runner: string;
|
|
10
|
+
/** What a proof-bearing test's proof_contract names for this integration. */
|
|
11
|
+
contractDefaults: Readonly<{
|
|
12
|
+
version: typeof PROOF_CONTRACT_VERSION;
|
|
13
|
+
integration: string;
|
|
14
|
+
required_proofs: ReadonlyArray<{
|
|
15
|
+
symbol_id: string;
|
|
16
|
+
target: string;
|
|
17
|
+
}>;
|
|
18
|
+
success_policy: "all_required_first_attempt";
|
|
19
|
+
}>;
|
|
20
|
+
}>;
|
|
21
|
+
export type ProofIntegrationPlanResult = Readonly<{
|
|
22
|
+
/** The hash-bound plan to review and apply with kb_apply_plan, if any. */
|
|
23
|
+
plan: MigrationPlan | null;
|
|
24
|
+
proposal: ProposedIntegration | null;
|
|
25
|
+
/** Why no plan was produced, or what applying it does. */
|
|
26
|
+
reason: string;
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* The command-producer integration Kibi proposes for the repository's test
|
|
30
|
+
* runner: the package.json `test` script when there is one (it is what the
|
|
31
|
+
* project already runs), otherwise the first detected runner. A command
|
|
32
|
+
* integration is the universal floor: Kibi judges the run by its exit code
|
|
33
|
+
* (aggregate-run provenance), so it works for every runner without a
|
|
34
|
+
* reporter; a native producer can replace it later through an update plan.
|
|
35
|
+
*/
|
|
36
|
+
export declare function proposeProofIntegration(root: string, overrideId?: string, overrideCommand?: readonly string[]): ProposedIntegration | null;
|
|
37
|
+
/**
|
|
38
|
+
* Build the hash-bound `kibi.migration-plan.v2` that writes
|
|
39
|
+
* `.kb/proof/integrations.json` through Kibi. Without `update`, a plan is
|
|
40
|
+
* produced only while the file does not exist; with `update` it adds or
|
|
41
|
+
* replaces the named integration of an existing, valid file. The plan carries the file's
|
|
42
|
+
* current hash and the hashes of the files the proposal was derived from;
|
|
43
|
+
* `kb_apply_plan` refuses it once either changed.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildProofIntegrationPlan(root: string, options?: Readonly<{
|
|
46
|
+
update?: string;
|
|
47
|
+
command?: readonly string[];
|
|
48
|
+
}>): ProofIntegrationPlanResult;
|
|
49
|
+
/**
|
|
50
|
+
* Apply one reviewed `proof_integration_configure` action: write
|
|
51
|
+
* `.kb/proof/integrations.json` (`kibi.proof-integration.v1`). A create is
|
|
52
|
+
* refused when the file exists; an update is refused unless the file still
|
|
53
|
+
* has the hash the plan was built against, and it adds or replaces only the
|
|
54
|
+
* named integration. Either is refused when
|
|
55
|
+
* a file the proposal was derived from changed since planning.
|
|
56
|
+
*/
|
|
57
|
+
export declare function applyProofIntegrationAction(action: MigrationAction, root: string): void;
|
|
58
|
+
//# sourceMappingURL=integration-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration-plan.d.ts","sourceRoot":"","sources":["../../src/proof/integration-plan.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,aAAa,EAGnB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,sBAAsB,EAEvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,mBAAmB,CAAC;AAE3B,mEAAmE;AACnE,eAAO,MAAM,2BAA2B,gCAAgC,CAAC;AAqCzE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,gBAAgB,EAAE,QAAQ,CAAC;QACzB,OAAO,EAAE,OAAO,sBAAsB,CAAC;QACvC,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,aAAa,CAAC;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACtE,cAAc,EAAE,4BAA4B,CAAC;KAC9C,CAAC,CAAC;CACJ,CAAC,CAAC;AAEH,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,0EAA0E;IAC1E,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAoHH;;;;;;;GAOG;AAEH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,GAClC,mBAAmB,GAAG,IAAI,CAoC5B;AAsCD;;;;;;;GAOG;AAEH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,QAAQ,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAM,GACvE,0BAA0B,CAiE5B;AAMD;;;;;;;GAOG;AAEH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,MAAM,GACX,IAAI,CAyDN"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { MigrationActionRefusedError } from "../operations/planning/migration-refusal.js";
|
|
5
|
+
import { buildMigrationPlan, migrationAction, } from "../public/operations/migration-plan.js";
|
|
6
|
+
import { PROOF_CONTRACT_VERSION, PROOF_INTEGRATION_VERSION, } from "../public/proof-protocol.js";
|
|
7
|
+
import { PROOF_INTEGRATIONS_PATH, integrationsPath, proofIntegrationErrors, } from "./integrations.js";
|
|
8
|
+
/** Migration action code of a reviewed proof integration write. */
|
|
9
|
+
export const PROOF_INTEGRATION_CONFIGURE = "proof_integration_configure";
|
|
10
|
+
/**
|
|
11
|
+
* Files the proposal is derived from. Their hashes are bound into the plan,
|
|
12
|
+
* so a plan reviewed against one package.json is refused after it changed.
|
|
13
|
+
*/
|
|
14
|
+
const PROPOSAL_SOURCES = [
|
|
15
|
+
"package.json",
|
|
16
|
+
"bun.lock",
|
|
17
|
+
"bun.lockb",
|
|
18
|
+
"pnpm-lock.yaml",
|
|
19
|
+
"yarn.lock",
|
|
20
|
+
"playwright.config.ts",
|
|
21
|
+
"playwright.config.mjs",
|
|
22
|
+
"playwright.config.js",
|
|
23
|
+
"vitest.config.ts",
|
|
24
|
+
"vitest.config.mjs",
|
|
25
|
+
"vitest.config.js",
|
|
26
|
+
"jest.config.ts",
|
|
27
|
+
"jest.config.mjs",
|
|
28
|
+
"jest.config.js",
|
|
29
|
+
"pytest.ini",
|
|
30
|
+
"pyproject.toml",
|
|
31
|
+
"setup.cfg",
|
|
32
|
+
"tox.ini",
|
|
33
|
+
"go.mod",
|
|
34
|
+
"Cargo.toml",
|
|
35
|
+
"pom.xml",
|
|
36
|
+
"build.gradle",
|
|
37
|
+
"build.gradle.kts",
|
|
38
|
+
"Gemfile",
|
|
39
|
+
"Rakefile",
|
|
40
|
+
];
|
|
41
|
+
/** npm writes this placeholder into a fresh package.json; it runs no tests. */
|
|
42
|
+
const NPM_PLACEHOLDER_TEST = /no test specified/i;
|
|
43
|
+
function readPackageJson(root) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
|
|
46
|
+
return parsed !== null &&
|
|
47
|
+
typeof parsed === "object" &&
|
|
48
|
+
!Array.isArray(parsed)
|
|
49
|
+
? parsed
|
|
50
|
+
: null;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function packageTestCommand(root) {
|
|
57
|
+
const pkg = readPackageJson(root);
|
|
58
|
+
const scripts = pkg !== null &&
|
|
59
|
+
pkg.scripts !== null &&
|
|
60
|
+
typeof pkg.scripts === "object" &&
|
|
61
|
+
!Array.isArray(pkg.scripts)
|
|
62
|
+
? pkg.scripts
|
|
63
|
+
: {};
|
|
64
|
+
const test = scripts.test;
|
|
65
|
+
if (typeof test !== "string" || test.trim() === "")
|
|
66
|
+
return null;
|
|
67
|
+
if (NPM_PLACEHOLDER_TEST.test(test))
|
|
68
|
+
return null;
|
|
69
|
+
const has = (file) => existsSync(path.join(root, file));
|
|
70
|
+
if (has("bun.lock") || has("bun.lockb"))
|
|
71
|
+
return ["bun", "run", "test"];
|
|
72
|
+
if (has("pnpm-lock.yaml"))
|
|
73
|
+
return ["pnpm", "test"];
|
|
74
|
+
if (has("yarn.lock"))
|
|
75
|
+
return ["yarn", "test"];
|
|
76
|
+
return ["npm", "test"];
|
|
77
|
+
}
|
|
78
|
+
const PACKAGE_TEST_SCRIPT_RUNNER = "package.json test script";
|
|
79
|
+
/**
|
|
80
|
+
* A command integration is judged as one run. When it runs the package's
|
|
81
|
+
* whole test script, say so: any failing test fails every obligation.
|
|
82
|
+
*/
|
|
83
|
+
function wholeRunNote(proposal) {
|
|
84
|
+
if (!proposal.runner.startsWith(PACKAGE_TEST_SCRIPT_RUNNER))
|
|
85
|
+
return "";
|
|
86
|
+
return ` Kibi judges this run as a whole: a failing test anywhere in the package's test script fails every proof obligation that names '${proposal.integration.id}', so unrelated failures prove nothing. To narrow it, pass --command with a command that runs only the proof-bearing tests (kibi proof inspect --update ${proposal.integration.id} --command "<command>" --json once the file exists), or have the command write a kibi.proof-test-report.v1 to the path in KIBI_PROOF_TEST_REPORT so each test is judged by its own steps.`;
|
|
87
|
+
}
|
|
88
|
+
/** Ordered: the first runner whose marker file exists wins. */
|
|
89
|
+
const RUNNER_COMMANDS = [
|
|
90
|
+
{
|
|
91
|
+
runner: "playwright",
|
|
92
|
+
id: "e2e",
|
|
93
|
+
files: [
|
|
94
|
+
"playwright.config.ts",
|
|
95
|
+
"playwright.config.mjs",
|
|
96
|
+
"playwright.config.js",
|
|
97
|
+
],
|
|
98
|
+
command: ["npx", "playwright", "test"],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
runner: "vitest",
|
|
102
|
+
id: "unit",
|
|
103
|
+
files: ["vitest.config.ts", "vitest.config.mjs", "vitest.config.js"],
|
|
104
|
+
command: ["npx", "vitest", "run"],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
runner: "jest",
|
|
108
|
+
id: "unit",
|
|
109
|
+
files: ["jest.config.ts", "jest.config.mjs", "jest.config.js"],
|
|
110
|
+
command: ["npx", "jest", "--ci"],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
runner: "pytest",
|
|
114
|
+
id: "unit",
|
|
115
|
+
files: ["pytest.ini", "pyproject.toml", "setup.cfg", "tox.ini"],
|
|
116
|
+
command: ["pytest"],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
runner: "go test",
|
|
120
|
+
id: "unit",
|
|
121
|
+
files: ["go.mod"],
|
|
122
|
+
command: ["go", "test", "./..."],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
runner: "cargo test",
|
|
126
|
+
id: "unit",
|
|
127
|
+
files: ["Cargo.toml"],
|
|
128
|
+
command: ["cargo", "test"],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
runner: "maven",
|
|
132
|
+
id: "unit",
|
|
133
|
+
files: ["pom.xml"],
|
|
134
|
+
command: ["mvn", "test"],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
runner: "gradle",
|
|
138
|
+
id: "unit",
|
|
139
|
+
files: ["build.gradle", "build.gradle.kts"],
|
|
140
|
+
command: ["gradle", "test"],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
runner: "rake test",
|
|
144
|
+
id: "unit",
|
|
145
|
+
files: ["Rakefile"],
|
|
146
|
+
command: ["bundle", "exec", "rake", "test"],
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
/**
|
|
150
|
+
* The command-producer integration Kibi proposes for the repository's test
|
|
151
|
+
* runner: the package.json `test` script when there is one (it is what the
|
|
152
|
+
* project already runs), otherwise the first detected runner. A command
|
|
153
|
+
* integration is the universal floor: Kibi judges the run by its exit code
|
|
154
|
+
* (aggregate-run provenance), so it works for every runner without a
|
|
155
|
+
* reporter; a native producer can replace it later through an update plan.
|
|
156
|
+
*/
|
|
157
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
158
|
+
export function proposeProofIntegration(root, overrideId, overrideCommand) {
|
|
159
|
+
const npmCommand = packageTestCommand(root);
|
|
160
|
+
const detected = overrideCommand !== undefined && overrideCommand.length > 0
|
|
161
|
+
? {
|
|
162
|
+
runner: `--command (${overrideCommand.join(" ")})`,
|
|
163
|
+
id: "unit",
|
|
164
|
+
command: overrideCommand,
|
|
165
|
+
}
|
|
166
|
+
: npmCommand !== null
|
|
167
|
+
? {
|
|
168
|
+
runner: `${PACKAGE_TEST_SCRIPT_RUNNER} (${npmCommand.join(" ")})`,
|
|
169
|
+
id: "unit",
|
|
170
|
+
command: npmCommand,
|
|
171
|
+
}
|
|
172
|
+
: RUNNER_COMMANDS.find((entry) => entry.files.some((file) => existsSync(path.join(root, file))));
|
|
173
|
+
if (detected === undefined)
|
|
174
|
+
return null;
|
|
175
|
+
const id = overrideId ?? detected.id;
|
|
176
|
+
return {
|
|
177
|
+
runner: detected.runner,
|
|
178
|
+
integration: {
|
|
179
|
+
id,
|
|
180
|
+
producer: "command",
|
|
181
|
+
command: [...detected.command],
|
|
182
|
+
targets: ["default"],
|
|
183
|
+
description: `Command proof: runs ${detected.command.join(" ")} once per kibi prove and judges every selected test by its exit code.`,
|
|
184
|
+
},
|
|
185
|
+
contractDefaults: {
|
|
186
|
+
version: PROOF_CONTRACT_VERSION,
|
|
187
|
+
integration: id,
|
|
188
|
+
required_proofs: [{ symbol_id: "SYM-<symbol the test proves>", target: "default" }],
|
|
189
|
+
success_policy: "all_required_first_attempt",
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function sha256(bytes) {
|
|
194
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
195
|
+
}
|
|
196
|
+
function fileHash(filePath) {
|
|
197
|
+
try {
|
|
198
|
+
return sha256(readFileSync(filePath));
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function sourceHashes(root) {
|
|
205
|
+
const hashes = {};
|
|
206
|
+
for (const file of PROPOSAL_SOURCES) {
|
|
207
|
+
const hash = fileHash(path.join(root, file));
|
|
208
|
+
if (hash !== null)
|
|
209
|
+
hashes[file] = hash;
|
|
210
|
+
}
|
|
211
|
+
return hashes;
|
|
212
|
+
}
|
|
213
|
+
function readIntegrationsFile(root) {
|
|
214
|
+
try {
|
|
215
|
+
const parsed = JSON.parse(readFileSync(integrationsPath(root), "utf8"));
|
|
216
|
+
return proofIntegrationErrors(parsed).length === 0 ? parsed : null;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Build the hash-bound `kibi.migration-plan.v2` that writes
|
|
224
|
+
* `.kb/proof/integrations.json` through Kibi. Without `update`, a plan is
|
|
225
|
+
* produced only while the file does not exist; with `update` it adds or
|
|
226
|
+
* replaces the named integration of an existing, valid file. The plan carries the file's
|
|
227
|
+
* current hash and the hashes of the files the proposal was derived from;
|
|
228
|
+
* `kb_apply_plan` refuses it once either changed.
|
|
229
|
+
*/
|
|
230
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
231
|
+
export function buildProofIntegrationPlan(root, options = {}) {
|
|
232
|
+
const filePath = integrationsPath(root);
|
|
233
|
+
const exists = existsSync(filePath);
|
|
234
|
+
const update = options.update?.trim() || undefined;
|
|
235
|
+
if (exists && update === undefined) {
|
|
236
|
+
return {
|
|
237
|
+
plan: null,
|
|
238
|
+
proposal: null,
|
|
239
|
+
reason: `${PROOF_INTEGRATIONS_PATH} already exists; Kibi will not overwrite it. Run kibi proof inspect --update <integration id> for a plan that adds or replaces one named integration.`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
if (!exists && update !== undefined) {
|
|
243
|
+
return {
|
|
244
|
+
plan: null,
|
|
245
|
+
proposal: null,
|
|
246
|
+
reason: `${PROOF_INTEGRATIONS_PATH} does not exist yet. Run kibi proof inspect without --update for a plan that creates it.`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (update !== undefined && readIntegrationsFile(root) === null)
|
|
250
|
+
return {
|
|
251
|
+
plan: null,
|
|
252
|
+
proposal: null,
|
|
253
|
+
reason: `${PROOF_INTEGRATIONS_PATH} is not a valid ${PROOF_INTEGRATION_VERSION} file, so Kibi cannot update one integration in it. Run kibi prove for the validation error.`,
|
|
254
|
+
};
|
|
255
|
+
const proposal = proposeProofIntegration(root, update, options.command);
|
|
256
|
+
if (proposal === null) {
|
|
257
|
+
return {
|
|
258
|
+
plan: null,
|
|
259
|
+
proposal: null,
|
|
260
|
+
reason: "No test runner detected (no package.json test script and no known runner configuration), so Kibi has no command to propose. Proof integration stays deferred until the first proof-bearing test introduces a harness.",
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
const action = migrationAction({
|
|
264
|
+
id: `proof-integration-${proposal.integration.id}`,
|
|
265
|
+
code: PROOF_INTEGRATION_CONFIGURE,
|
|
266
|
+
category: "proof",
|
|
267
|
+
safety: "automatic",
|
|
268
|
+
autoApplicable: true,
|
|
269
|
+
invocation: {
|
|
270
|
+
kind: "review",
|
|
271
|
+
instruction: `Review the proposed ${proposal.integration.producer} integration '${proposal.integration.id}' (command: ${proposal.integration.command.join(" ")}), then apply this plan with kb_apply_plan (plan, approvedPlanHash, approvedActionIds) to ${update === undefined ? "create" : "update"} ${PROOF_INTEGRATIONS_PATH}.`,
|
|
272
|
+
},
|
|
273
|
+
affectedFiles: [PROOF_INTEGRATIONS_PATH],
|
|
274
|
+
evidence: {
|
|
275
|
+
path: PROOF_INTEGRATIONS_PATH,
|
|
276
|
+
mode: update === undefined ? "create" : "update",
|
|
277
|
+
integration: proposal.integration,
|
|
278
|
+
runner: proposal.runner,
|
|
279
|
+
contractDefaults: proposal.contractDefaults,
|
|
280
|
+
previousFileHash: update === undefined ? null : fileHash(filePath),
|
|
281
|
+
sourceHashes: sourceHashes(root),
|
|
282
|
+
},
|
|
283
|
+
postconditions: [
|
|
284
|
+
{ file: PROOF_INTEGRATIONS_PATH, version: PROOF_INTEGRATION_VERSION },
|
|
285
|
+
],
|
|
286
|
+
});
|
|
287
|
+
return {
|
|
288
|
+
plan: buildMigrationPlan({
|
|
289
|
+
evaluatedDomains: ["proof"],
|
|
290
|
+
actions: [action],
|
|
291
|
+
}),
|
|
292
|
+
proposal,
|
|
293
|
+
reason: `Applying the plan ${update === undefined ? "creates" : "updates"} ${PROOF_INTEGRATIONS_PATH} with the ${proposal.integration.producer} integration '${proposal.integration.id}' (${proposal.integration.command.join(" ")}). Proof-bearing tests then name it in proof_contract.integration and kibi prove runs it.${wholeRunNote(proposal)}`,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function isRecord(value) {
|
|
297
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Apply one reviewed `proof_integration_configure` action: write
|
|
301
|
+
* `.kb/proof/integrations.json` (`kibi.proof-integration.v1`). A create is
|
|
302
|
+
* refused when the file exists; an update is refused unless the file still
|
|
303
|
+
* has the hash the plan was built against, and it adds or replaces only the
|
|
304
|
+
* named integration. Either is refused when
|
|
305
|
+
* a file the proposal was derived from changed since planning.
|
|
306
|
+
*/
|
|
307
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
308
|
+
export function applyProofIntegrationAction(action, root) {
|
|
309
|
+
const evidence = action.evidence;
|
|
310
|
+
const integration = evidence.integration;
|
|
311
|
+
if (evidence.path !== PROOF_INTEGRATIONS_PATH ||
|
|
312
|
+
(evidence.mode !== "create" && evidence.mode !== "update") ||
|
|
313
|
+
!isRecord(integration) ||
|
|
314
|
+
typeof integration.id !== "string")
|
|
315
|
+
throw new MigrationActionRefusedError(`Proof integration action '${action.id}' is malformed; rerun kibi proof inspect for a new plan.`);
|
|
316
|
+
const planned = isRecord(evidence.sourceHashes) ? evidence.sourceHashes : {};
|
|
317
|
+
const live = sourceHashes(root);
|
|
318
|
+
const changed = [
|
|
319
|
+
...new Set([...Object.keys(planned), ...Object.keys(live)]),
|
|
320
|
+
].filter((file) => planned[file] !== live[file]);
|
|
321
|
+
if (changed.length > 0)
|
|
322
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: ${changed.join(", ")} changed since planning; rerun kibi proof inspect and review the new plan.`);
|
|
323
|
+
const filePath = integrationsPath(root);
|
|
324
|
+
let integrations;
|
|
325
|
+
if (evidence.mode === "create") {
|
|
326
|
+
if (existsSync(filePath))
|
|
327
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: ${PROOF_INTEGRATIONS_PATH} already exists and this plan only creates it. Run kibi proof inspect --update <integration id> for a plan that adds or replaces one named integration.`);
|
|
328
|
+
integrations = [integration];
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
if (fileHash(filePath) !== evidence.previousFileHash)
|
|
332
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: ${PROOF_INTEGRATIONS_PATH} changed since planning; rerun kibi proof inspect --update ${integration.id}.`);
|
|
333
|
+
const current = readIntegrationsFile(root);
|
|
334
|
+
if (current === null)
|
|
335
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: ${PROOF_INTEGRATIONS_PATH} is not a valid ${PROOF_INTEGRATION_VERSION} file.`);
|
|
336
|
+
integrations = current.integrations.some((entry) => entry.id === integration.id)
|
|
337
|
+
? current.integrations.map((entry) => entry.id === integration.id ? integration : entry)
|
|
338
|
+
: [...current.integrations, integration];
|
|
339
|
+
}
|
|
340
|
+
const file = { version: PROOF_INTEGRATION_VERSION, integrations };
|
|
341
|
+
const errors = proofIntegrationErrors(file);
|
|
342
|
+
if (errors.length > 0)
|
|
343
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: the result would be invalid (${errors.join("; ")}).`);
|
|
344
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
345
|
+
const temporary = `${filePath}.${process.pid}.tmp`;
|
|
346
|
+
writeFileSync(temporary, `${JSON.stringify(file, null, 2)}\n`, "utf8");
|
|
347
|
+
renameSync(temporary, filePath);
|
|
348
|
+
}
|
|
@@ -70,7 +70,7 @@ export function loadProofIntegrations(workspaceRoot) {
|
|
|
70
70
|
if (!existsSync(filePath)) {
|
|
71
71
|
return {
|
|
72
72
|
available: false,
|
|
73
|
-
error: `No proof integration configuration at ${PROOF_INTEGRATIONS_PATH}. Run
|
|
73
|
+
error: `No proof integration configuration at ${PROOF_INTEGRATIONS_PATH}. Run kibi proof inspect --json and apply its integrationPlan with kb_apply_plan to configure one.`,
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
let parsed;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-depth-quality.d.ts","sourceRoot":"","sources":["../../../src/public/impact/coverage-depth-quality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAsIpD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAI1E;
|
|
1
|
+
{"version":3,"file":"coverage-depth-quality.d.ts","sourceRoot":"","sources":["../../../src/public/impact/coverage-depth-quality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAsIpD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAI1E;AAgLD,wBAAgB,qCAAqC,CACnD,eAAe,EAAE,SAAS,gBAAgB,EAAE,EAC5C,kBAAkB,GAAE,WAAW,CAC7B,MAAM,EACN,QAAQ,CAAC;IACP,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C,CAAC,CACS,GACZ,SAAS,iBAAiB,EAAE,CAmB9B"}
|