kibi-cli 2.16.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 +37 -1
- 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-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 +23 -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 +2 -0
- package/dist/proof/inspect.d.ts.map +1 -1
- package/dist/proof/integration-plan.d.ts +2 -1
- package/dist/proof/integration-plan.d.ts.map +1 -1
- package/dist/proof/integration-plan.js +30 -13
- 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/discovery-executors.js +5 -5
- package/dist/public/operations/result-envelope.d.ts.map +1 -1
- package/dist/public/operations/result-envelope.js +23 -3
- package/dist/public/skills/kibi-usage/SKILL.md +8 -2
- package/dist/public/skills/kibi-usage/resources/fact-lanes.md +3 -3
- package/dist/public/skills/kibi-usage/resources/proof.md +21 -3
- package/package.json +2 -2
- package/src/public/impact/coverage-depth-quality.ts +46 -1
- package/src/public/operations/discovery-executors.ts +5 -5
- package/src/public/operations/result-envelope.ts +31 -3
- package/src/public/skills/kibi-usage/SKILL.md +8 -2
- package/src/public/skills/kibi-usage/resources/fact-lanes.md +3 -3
- package/src/public/skills/kibi-usage/resources/proof.md +21 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-protocol.d.ts","sourceRoot":"","sources":["../src/cli-protocol.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AAS3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAMpE,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG;IAC1C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CACpD,CAAC;AAGF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,iBAAS,WAAW,CAClB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAAG,cAAc,EAClC,IAAI,CAAC,EAAE;IACL,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,EACD,SAAS,UAAO,EAChB,OAAO,CAAC,EAAE,OAAO,GAChB,iBAAiB,CAwBnB;AA4BD;;;;GAIG;AAEH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EACvC,KAAK,EAAE,OAAO,GACb,iBAAiB,CAWnB;AAGD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"cli-protocol.d.ts","sourceRoot":"","sources":["../src/cli-protocol.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AAS3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAMpE,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG;IAC1C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CACpD,CAAC;AAGF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,iBAAS,WAAW,CAClB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAAG,cAAc,EAClC,IAAI,CAAC,EAAE;IACL,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,EACD,SAAS,UAAO,EAChB,OAAO,CAAC,EAAE,OAAO,GAChB,iBAAiB,CAwBnB;AA4BD;;;;GAIG;AAEH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EACvC,KAAK,EAAE,OAAO,GACb,iBAAiB,CAWnB;AAGD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAqF5B"}
|
package/dist/cli-protocol.js
CHANGED
|
@@ -78,6 +78,19 @@ export async function executeOperation(catalogName, input, context) {
|
|
|
78
78
|
if (!protocolValid(spec, envelope)) {
|
|
79
79
|
return errorResult(catalogName, new OperationError("PROTOCOL_VALIDATION_FAILED", "Operation produced a result that does not satisfy its generated output contract."), spec);
|
|
80
80
|
}
|
|
81
|
+
// A result the operation itself reports as an error (a rejected
|
|
82
|
+
// bootstrap, a refused migration plan) exits non-zero like any other.
|
|
83
|
+
if (envelope.status === "error") {
|
|
84
|
+
return {
|
|
85
|
+
exitCode: 1,
|
|
86
|
+
stdout: `${JSON.stringify(envelope)}\n`,
|
|
87
|
+
...(envelope.error
|
|
88
|
+
? {
|
|
89
|
+
stderr: `Error [${envelope.error.code}]: ${envelope.error.message}\n`,
|
|
90
|
+
}
|
|
91
|
+
: {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
81
94
|
return { exitCode: 0, stdout: `${JSON.stringify(envelope)}\n` };
|
|
82
95
|
}
|
|
83
96
|
catch (error) {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
export declare function registerProofCommand(program: Command): void;
|
|
3
|
+
/**
|
|
4
|
+
* `--command` for `kibi proof inspect`: a JSON array of argv strings, or
|
|
5
|
+
* space-separated words (no shell quoting; Kibi runs integrations with
|
|
6
|
+
* `shell: false`).
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseCommandOption(value: string): readonly string[];
|
|
3
9
|
//# sourceMappingURL=cli-register-proof.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-register-proof.d.ts","sourceRoot":"","sources":["../src/cli-register-proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"cli-register-proof.d.ts","sourceRoot":"","sources":["../src/cli-register-proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2M3D;AAED;;;;GAIG;AAEH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAwBnE"}
|
|
@@ -41,9 +41,16 @@ export function registerProofCommand(program) {
|
|
|
41
41
|
.command("inspect")
|
|
42
42
|
.description("Detect test infrastructure, propose a proof integration and return the reviewed plan that writes .kb/proof/integrations.json")
|
|
43
43
|
.option("--update <id>", "Plan adding or replacing this integration in an existing .kb/proof/integrations.json")
|
|
44
|
+
.option("--command <command>", 'Command the proposed integration runs instead of the detected one: space-separated words, or a JSON array of argv strings (e.g. \'["npx","vitest","run","tests/e2e"]\')')
|
|
44
45
|
.option("--json", "Emit structured JSON", false)
|
|
45
46
|
.action(withExitCode(async (options) => {
|
|
46
|
-
const
|
|
47
|
+
const command = options.command === undefined
|
|
48
|
+
? undefined
|
|
49
|
+
: parseCommandOption(options.command);
|
|
50
|
+
const result = await (await import("./proof/inspect.js")).inspectProofEnvironment(process.cwd(), {
|
|
51
|
+
...(options.update === undefined ? {} : { update: options.update }),
|
|
52
|
+
...(command === undefined ? {} : { command }),
|
|
53
|
+
});
|
|
47
54
|
if (options.json) {
|
|
48
55
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
49
56
|
return undefined;
|
|
@@ -109,6 +116,33 @@ export function registerProofCommand(program) {
|
|
|
109
116
|
});
|
|
110
117
|
}));
|
|
111
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* `--command` for `kibi proof inspect`: a JSON array of argv strings, or
|
|
121
|
+
* space-separated words (no shell quoting; Kibi runs integrations with
|
|
122
|
+
* `shell: false`).
|
|
123
|
+
*/
|
|
124
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
125
|
+
export function parseCommandOption(value) {
|
|
126
|
+
const trimmed = value.trim();
|
|
127
|
+
if (trimmed.startsWith("[")) {
|
|
128
|
+
let parsed;
|
|
129
|
+
try {
|
|
130
|
+
parsed = JSON.parse(trimmed);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
throw new Error("--command must be a JSON array of argv strings or space-separated words.");
|
|
134
|
+
}
|
|
135
|
+
if (!Array.isArray(parsed) ||
|
|
136
|
+
parsed.length === 0 ||
|
|
137
|
+
!parsed.every((entry) => typeof entry === "string" && entry !== ""))
|
|
138
|
+
throw new Error("--command must be a JSON array of non-empty argv strings.");
|
|
139
|
+
return parsed;
|
|
140
|
+
}
|
|
141
|
+
const words = trimmed.split(/\s+/).filter((word) => word !== "");
|
|
142
|
+
if (words.length === 0)
|
|
143
|
+
throw new Error("--command must not be empty.");
|
|
144
|
+
return words;
|
|
145
|
+
}
|
|
112
146
|
function renderInspection(result) {
|
|
113
147
|
const inspection = result;
|
|
114
148
|
const lines = ["Proof environment", ""];
|
|
@@ -145,6 +179,8 @@ function renderInspection(result) {
|
|
|
145
179
|
if (inspection.integrationPlan) {
|
|
146
180
|
const plan = inspection.integrationPlan;
|
|
147
181
|
lines.push(`Integration plan: ${plan.planHash.slice(0, 12)} (actions: ${plan.actions.map((action) => action.id).join(", ")}); rerun with --json for the plan to pass to kb_apply_plan.`);
|
|
182
|
+
if (inspection.integrationPlanReason)
|
|
183
|
+
lines.push(inspection.integrationPlanReason);
|
|
148
184
|
}
|
|
149
185
|
else if (inspection.integrationPlanReason) {
|
|
150
186
|
lines.push("");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"predicate-applicability.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-applicability.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,qBAAqB,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"predicate-applicability.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-applicability.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AA2W1C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,MAAM,GACX,qBAAqB,CA2EvB"}
|
|
@@ -96,7 +96,13 @@ const INTENT_RULES = {
|
|
|
96
96
|
permission_rule: {
|
|
97
97
|
allOf: [
|
|
98
98
|
[/only|may|can|allowed|denied|forbidden|must\s+not|cannot/i],
|
|
99
|
-
[
|
|
99
|
+
[
|
|
100
|
+
/delete|export|access|read|write|update|remove|manage|permission/i,
|
|
101
|
+
// A common action verb counts only right after the permission modal
|
|
102
|
+
// ("must not publish", "may approve"), so a quota such as "uploads
|
|
103
|
+
// must not exceed 10 MB" does not read as a permission.
|
|
104
|
+
/\b(?:may|can|cannot|can't|must\s+(?:not|never)|never|(?:is|are)\s+(?:not\s+)?(?:allowed|permitted|forbidden)\s+to)\s+(?:only\s+)?(?:publish|unpublish|approve|assign|view|see|edit|modify|create|submit|share|invite|upload|download|comment|post|complete|archive|restore|grant|revoke|purchase|cancel|execute|invoke|configure|moderate|ban|join|book|reserve|transfer|close|reopen|merge)\b/i,
|
|
105
|
+
],
|
|
100
106
|
],
|
|
101
107
|
},
|
|
102
108
|
resource_constraint: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"predicate-utils.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"predicate-utils.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-utils.ts"],"names":[],"mappings":"AAMA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQlD;AAGD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,GAAG,SAAS,CAGpB;AAGD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAIR;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAM5D;AAGD,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM1C;AAGD,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAO9D;AAED;;;;;;GAMG;AAEH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,WAAW,CAAC,EAAE,QAAQ,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAAC,GACD,MAAM,CAeR;AAGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASjD;AAGD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C;AAGD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAGD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAOT;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAGD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMtD;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAclD;AAGD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAGD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM9C;AAGD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9C;AAGD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { singularizeToken } from "../semantic-advisor/shared.js";
|
|
2
3
|
const DEFAULT_MIN_SCORE = 0.35;
|
|
3
4
|
// implements REQ-mcp-suggest-predicates
|
|
4
5
|
export function normalizeText(text) {
|
|
@@ -114,9 +115,7 @@ export function normalizePredicateToken(value) {
|
|
|
114
115
|
}
|
|
115
116
|
// implements REQ-mcp-suggest-predicates
|
|
116
117
|
export function singularize(value) {
|
|
117
|
-
|
|
118
|
-
return value;
|
|
119
|
-
return value.endsWith("s") && value.length > 3 ? value.slice(0, -1) : value;
|
|
118
|
+
return singularizeToken(value, ["changes", "status", "results"]);
|
|
120
119
|
}
|
|
121
120
|
// implements REQ-mcp-suggest-predicates
|
|
122
121
|
export function normalizeSubjectKey(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/apply-plan.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,0CAA0C,CAAC;AASlD,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/apply-plan.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,0CAA0C,CAAC;AASlD,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;AAgE/B,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAC;AA6G9E,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EACnE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,IAAI,CAaN;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,SAAS,cAAoB,GAC5B,eAAe,EAAE,CA2CnB;AA8zBD;;;;;;GAMG;AAEH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAyqDD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,aAAa,EACnB,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,eAAe,CAAC;CACpC,CAAC,CAkDD"}
|
|
@@ -24,6 +24,7 @@ import { canonicalSourcePath, renderSourceDocument, } from "../mutation/source-a
|
|
|
24
24
|
import { assertRelationshipShardContained, executeUpsert, validateUpsertForCommit, } from "../mutation/upsert.js";
|
|
25
25
|
import { acquireWorkspaceMutationLock, releaseWorkspaceMutationLock, } from "../mutation/workspace-mutation-lock.js";
|
|
26
26
|
import { compilePlanHash, isBlockingWitness, parseWhatIfAnalysis, planWhatIfGoal, } from "./compile-intent.js";
|
|
27
|
+
import { MigrationActionRefusedError } from "./migration-refusal.js";
|
|
27
28
|
// implements REQ-kibi-change-to-proof-plan-compiler-v2
|
|
28
29
|
export const PLAN_APPLY_RESULT_VERSION = "kibi.plan-apply-result.v1";
|
|
29
30
|
const ENTITY_TYPES = new Set([
|
|
@@ -2121,6 +2122,9 @@ async function applyMigrationPlan(args, context, onCommitted = () => undefined)
|
|
|
2121
2122
|
}
|
|
2122
2123
|
const results = [];
|
|
2123
2124
|
let failed = false;
|
|
2125
|
+
// Failures that changed nothing (a precondition refused the action).
|
|
2126
|
+
let refusals = 0;
|
|
2127
|
+
let failures = 0;
|
|
2124
2128
|
for (const action of actions) {
|
|
2125
2129
|
if (failed) {
|
|
2126
2130
|
results.push({
|
|
@@ -2141,6 +2145,9 @@ async function applyMigrationPlan(args, context, onCommitted = () => undefined)
|
|
|
2141
2145
|
}
|
|
2142
2146
|
catch (error) {
|
|
2143
2147
|
failed = true;
|
|
2148
|
+
failures += 1;
|
|
2149
|
+
if (error instanceof MigrationActionRefusedError)
|
|
2150
|
+
refusals += 1;
|
|
2144
2151
|
results.push({
|
|
2145
2152
|
actionId: action.id,
|
|
2146
2153
|
outcome: "failed",
|
|
@@ -2190,10 +2197,15 @@ async function applyMigrationPlan(args, context, onCommitted = () => undefined)
|
|
|
2190
2197
|
});
|
|
2191
2198
|
}
|
|
2192
2199
|
}
|
|
2200
|
+
// refused: nothing was applied and every failure was a refusal, so the
|
|
2201
|
+
// workspace is as it was; reconciliation_required: an action failed
|
|
2202
|
+
// without being refused, so its effect must be inspected.
|
|
2193
2203
|
const outcome = failed
|
|
2194
2204
|
? results.some((result) => result.outcome === "applied")
|
|
2195
2205
|
? "partially_applied"
|
|
2196
|
-
:
|
|
2206
|
+
: failures > 0 && refusals === failures
|
|
2207
|
+
? "refused"
|
|
2208
|
+
: "reconciliation_required"
|
|
2197
2209
|
: "applied";
|
|
2198
2210
|
const kbState = finalStatus.branchAttachment?.migrationRequired
|
|
2199
2211
|
? "legacy_compat"
|
|
@@ -2237,7 +2249,7 @@ async function applyMigrationPlan(args, context, onCommitted = () => undefined)
|
|
|
2237
2249
|
closeout: {
|
|
2238
2250
|
taskOutcome: outcome === "applied"
|
|
2239
2251
|
? "complete"
|
|
2240
|
-
: outcome === "reconciliation_required"
|
|
2252
|
+
: outcome === "reconciliation_required" || outcome === "refused"
|
|
2241
2253
|
? "blocked"
|
|
2242
2254
|
: "interim",
|
|
2243
2255
|
kbState,
|
|
@@ -2250,7 +2262,11 @@ async function applyMigrationPlan(args, context, onCommitted = () => undefined)
|
|
|
2250
2262
|
content: [
|
|
2251
2263
|
{
|
|
2252
2264
|
type: "text",
|
|
2253
|
-
text:
|
|
2265
|
+
text: outcome === "applied"
|
|
2266
|
+
? `Applied migration plan ${args.plan.planHash.slice(0, 12)}.`
|
|
2267
|
+
: outcome === "refused"
|
|
2268
|
+
? `Refused migration plan ${args.plan.planHash.slice(0, 12)}; nothing was changed.`
|
|
2269
|
+
: `Stopped after migration plan ${args.plan.planHash.slice(0, 12)}.`,
|
|
2254
2270
|
},
|
|
2255
2271
|
],
|
|
2256
2272
|
structuredContent: payload,
|
|
@@ -2327,7 +2343,7 @@ async function applyMigrationAction(action, context) {
|
|
|
2327
2343
|
await applySchema6MigrationAction(action, context);
|
|
2328
2344
|
return;
|
|
2329
2345
|
}
|
|
2330
|
-
throw new
|
|
2346
|
+
throw new MigrationActionRefusedError(`Migration action '${action.code}' has no automatic executor.`);
|
|
2331
2347
|
}
|
|
2332
2348
|
}
|
|
2333
2349
|
/**
|
|
@@ -2338,10 +2354,10 @@ async function applyMigrationAction(action, context) {
|
|
|
2338
2354
|
async function applyPredicateSchemaAlignment(action, context) {
|
|
2339
2355
|
const invocation = action.invocation;
|
|
2340
2356
|
if (invocation.kind !== "operation" || invocation.name !== "kb_upsert")
|
|
2341
|
-
throw new
|
|
2357
|
+
throw new MigrationActionRefusedError("Predicate schema alignment requires its planned kb_upsert invocation.");
|
|
2342
2358
|
const input = asUpsert(invocation.input);
|
|
2343
2359
|
if (input.type !== "fact")
|
|
2344
|
-
throw new
|
|
2360
|
+
throw new MigrationActionRefusedError("Predicate schema alignment only rewrites fact entities.");
|
|
2345
2361
|
const prolog = context.prolog ?? (await context.ensureProlog?.());
|
|
2346
2362
|
if (!prolog)
|
|
2347
2363
|
throw new Error("Predicate schema alignment requires a Prolog runtime.");
|
|
@@ -2363,6 +2379,6 @@ async function applyPredicateSchemaAlignment(action, context) {
|
|
|
2363
2379
|
typeof rewrite.index === "number" &&
|
|
2364
2380
|
currentArgs[rewrite.index] === rewrite.from);
|
|
2365
2381
|
if (!unchanged)
|
|
2366
|
-
throw new
|
|
2382
|
+
throw new MigrationActionRefusedError(`Predicate fact ${input.id} changed since planning; rerun kibi check and approve the new plan.`);
|
|
2367
2383
|
await executeUpsert(input, operationContext);
|
|
2368
2384
|
}
|
|
@@ -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
|
@@ -23,6 +23,8 @@ export type ProofInspection = Readonly<{
|
|
|
23
23
|
export type ProofInspectionOptions = Readonly<{
|
|
24
24
|
/** Plan a replacement of this existing integration id. */
|
|
25
25
|
update?: string;
|
|
26
|
+
/** Command (argv) the proposed integration runs instead of the detected one. */
|
|
27
|
+
command?: readonly string[];
|
|
26
28
|
}>;
|
|
27
29
|
export declare function directoryEntriesMatch(directory: string, regex: RegExp): boolean;
|
|
28
30
|
export declare function inspectProofEnvironment(root: string, options?: ProofInspectionOptions): ProofInspection;
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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"}
|
|
@@ -33,7 +33,7 @@ export type ProofIntegrationPlanResult = Readonly<{
|
|
|
33
33
|
* (aggregate-run provenance), so it works for every runner without a
|
|
34
34
|
* reporter; a native producer can replace it later through an update plan.
|
|
35
35
|
*/
|
|
36
|
-
export declare function proposeProofIntegration(root: string, overrideId?: string): ProposedIntegration | null;
|
|
36
|
+
export declare function proposeProofIntegration(root: string, overrideId?: string, overrideCommand?: readonly string[]): ProposedIntegration | null;
|
|
37
37
|
/**
|
|
38
38
|
* Build the hash-bound `kibi.migration-plan.v2` that writes
|
|
39
39
|
* `.kb/proof/integrations.json` through Kibi. Without `update`, a plan is
|
|
@@ -44,6 +44,7 @@ export declare function proposeProofIntegration(root: string, overrideId?: strin
|
|
|
44
44
|
*/
|
|
45
45
|
export declare function buildProofIntegrationPlan(root: string, options?: Readonly<{
|
|
46
46
|
update?: string;
|
|
47
|
+
command?: readonly string[];
|
|
47
48
|
}>): ProofIntegrationPlanResult;
|
|
48
49
|
/**
|
|
49
50
|
* Apply one reviewed `proof_integration_configure` action: write
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration-plan.d.ts","sourceRoot":"","sources":["../../src/proof/integration-plan.ts"],"names":[],"mappings":"
|
|
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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { MigrationActionRefusedError } from "../operations/planning/migration-refusal.js";
|
|
4
5
|
import { buildMigrationPlan, migrationAction, } from "../public/operations/migration-plan.js";
|
|
5
6
|
import { PROOF_CONTRACT_VERSION, PROOF_INTEGRATION_VERSION, } from "../public/proof-protocol.js";
|
|
6
7
|
import { PROOF_INTEGRATIONS_PATH, integrationsPath, proofIntegrationErrors, } from "./integrations.js";
|
|
@@ -74,6 +75,16 @@ function packageTestCommand(root) {
|
|
|
74
75
|
return ["yarn", "test"];
|
|
75
76
|
return ["npm", "test"];
|
|
76
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
|
+
}
|
|
77
88
|
/** Ordered: the first runner whose marker file exists wins. */
|
|
78
89
|
const RUNNER_COMMANDS = [
|
|
79
90
|
{
|
|
@@ -144,15 +155,21 @@ const RUNNER_COMMANDS = [
|
|
|
144
155
|
* reporter; a native producer can replace it later through an update plan.
|
|
145
156
|
*/
|
|
146
157
|
// implements REQ-kibi-verification-evidence-contract
|
|
147
|
-
export function proposeProofIntegration(root, overrideId) {
|
|
158
|
+
export function proposeProofIntegration(root, overrideId, overrideCommand) {
|
|
148
159
|
const npmCommand = packageTestCommand(root);
|
|
149
|
-
const detected =
|
|
160
|
+
const detected = overrideCommand !== undefined && overrideCommand.length > 0
|
|
150
161
|
? {
|
|
151
|
-
runner:
|
|
162
|
+
runner: `--command (${overrideCommand.join(" ")})`,
|
|
152
163
|
id: "unit",
|
|
153
|
-
command:
|
|
164
|
+
command: overrideCommand,
|
|
154
165
|
}
|
|
155
|
-
:
|
|
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))));
|
|
156
173
|
if (detected === undefined)
|
|
157
174
|
return null;
|
|
158
175
|
const id = overrideId ?? detected.id;
|
|
@@ -235,7 +252,7 @@ export function buildProofIntegrationPlan(root, options = {}) {
|
|
|
235
252
|
proposal: null,
|
|
236
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.`,
|
|
237
254
|
};
|
|
238
|
-
const proposal = proposeProofIntegration(root, update);
|
|
255
|
+
const proposal = proposeProofIntegration(root, update, options.command);
|
|
239
256
|
if (proposal === null) {
|
|
240
257
|
return {
|
|
241
258
|
plan: null,
|
|
@@ -273,7 +290,7 @@ export function buildProofIntegrationPlan(root, options = {}) {
|
|
|
273
290
|
actions: [action],
|
|
274
291
|
}),
|
|
275
292
|
proposal,
|
|
276
|
-
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
|
|
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)}`,
|
|
277
294
|
};
|
|
278
295
|
}
|
|
279
296
|
function isRecord(value) {
|
|
@@ -295,27 +312,27 @@ export function applyProofIntegrationAction(action, root) {
|
|
|
295
312
|
(evidence.mode !== "create" && evidence.mode !== "update") ||
|
|
296
313
|
!isRecord(integration) ||
|
|
297
314
|
typeof integration.id !== "string")
|
|
298
|
-
throw new
|
|
315
|
+
throw new MigrationActionRefusedError(`Proof integration action '${action.id}' is malformed; rerun kibi proof inspect for a new plan.`);
|
|
299
316
|
const planned = isRecord(evidence.sourceHashes) ? evidence.sourceHashes : {};
|
|
300
317
|
const live = sourceHashes(root);
|
|
301
318
|
const changed = [
|
|
302
319
|
...new Set([...Object.keys(planned), ...Object.keys(live)]),
|
|
303
320
|
].filter((file) => planned[file] !== live[file]);
|
|
304
321
|
if (changed.length > 0)
|
|
305
|
-
throw new
|
|
322
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: ${changed.join(", ")} changed since planning; rerun kibi proof inspect and review the new plan.`);
|
|
306
323
|
const filePath = integrationsPath(root);
|
|
307
324
|
let integrations;
|
|
308
325
|
if (evidence.mode === "create") {
|
|
309
326
|
if (existsSync(filePath))
|
|
310
|
-
throw new
|
|
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.`);
|
|
311
328
|
integrations = [integration];
|
|
312
329
|
}
|
|
313
330
|
else {
|
|
314
331
|
if (fileHash(filePath) !== evidence.previousFileHash)
|
|
315
|
-
throw new
|
|
332
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: ${PROOF_INTEGRATIONS_PATH} changed since planning; rerun kibi proof inspect --update ${integration.id}.`);
|
|
316
333
|
const current = readIntegrationsFile(root);
|
|
317
334
|
if (current === null)
|
|
318
|
-
throw new
|
|
335
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: ${PROOF_INTEGRATIONS_PATH} is not a valid ${PROOF_INTEGRATION_VERSION} file.`);
|
|
319
336
|
integrations = current.integrations.some((entry) => entry.id === integration.id)
|
|
320
337
|
? current.integrations.map((entry) => entry.id === integration.id ? integration : entry)
|
|
321
338
|
: [...current.integrations, integration];
|
|
@@ -323,7 +340,7 @@ export function applyProofIntegrationAction(action, root) {
|
|
|
323
340
|
const file = { version: PROOF_INTEGRATION_VERSION, integrations };
|
|
324
341
|
const errors = proofIntegrationErrors(file);
|
|
325
342
|
if (errors.length > 0)
|
|
326
|
-
throw new
|
|
343
|
+
throw new MigrationActionRefusedError(`Proof integration plan refused: the result would be invalid (${errors.join("; ")}).`);
|
|
327
344
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
328
345
|
const temporary = `${filePath}.${process.pid}.tmp`;
|
|
329
346
|
writeFileSync(temporary, `${JSON.stringify(file, null, 2)}\n`, "utf8");
|
|
@@ -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"}
|
|
@@ -185,10 +185,38 @@ function coverageForRequirement(result, manifestResults, entities) {
|
|
|
185
185
|
scenarioTests,
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Requirement statuses that keep a requirement in the coverage-depth review.
|
|
190
|
+
* `closed` is reviewed only when tagged `implemented` (the documented
|
|
191
|
+
* "done, with evidence" shape); a plain closed, deprecated or superseded
|
|
192
|
+
* requirement is retired history, not a coverage gap.
|
|
193
|
+
*/
|
|
194
|
+
const REVIEWED_REQUIREMENT_STATUSES = new Set([
|
|
195
|
+
"open",
|
|
196
|
+
"in_progress",
|
|
197
|
+
"active",
|
|
198
|
+
"approved",
|
|
199
|
+
]);
|
|
200
|
+
function supersededRequirementIds(manifestResults) {
|
|
201
|
+
return new Set(manifestResults.flatMap((result) => result.entity.type === "req"
|
|
202
|
+
? result.relationships.flatMap((relationship) => relationship.type === "supersedes" ? [relationship.to] : [])
|
|
203
|
+
: []));
|
|
204
|
+
}
|
|
205
|
+
// implements REQ-audit-quality-diagnostics-v1
|
|
206
|
+
function isReviewableRequirement(requirement, superseded) {
|
|
207
|
+
if (superseded.has(requirement.id))
|
|
208
|
+
return false;
|
|
209
|
+
const status = requirement.status.trim().toLowerCase();
|
|
210
|
+
if (REVIEWED_REQUIREMENT_STATUSES.has(status))
|
|
211
|
+
return true;
|
|
212
|
+
return (status === "closed" && (requirement.tags ?? []).includes("implemented"));
|
|
213
|
+
}
|
|
188
214
|
export function createCoverageDepthQualityDiagnostics(manifestResults, proofByRequirement = new Map()) {
|
|
189
215
|
const entities = entitiesById(manifestResults);
|
|
216
|
+
const superseded = supersededRequirementIds(manifestResults);
|
|
190
217
|
return manifestResults
|
|
191
|
-
.filter((result) => result.entity.type === "req"
|
|
218
|
+
.filter((result) => result.entity.type === "req" &&
|
|
219
|
+
isReviewableRequirement(result.entity, superseded))
|
|
192
220
|
.flatMap((result) => {
|
|
193
221
|
const diagnostic = createDiagnostic(coverageForRequirement(result, manifestResults, entities), proofByRequirement.get(result.entity.id));
|
|
194
222
|
return diagnostic === undefined ? [] : [diagnostic];
|
|
@@ -311,11 +311,11 @@ export async function executeStatus(_input, context) {
|
|
|
311
311
|
// Source hashes can all match while the compilation itself is lossy, so
|
|
312
312
|
// a compiler contract change alone makes the store stale.
|
|
313
313
|
...(compilerReason ? { syncState: "stale", dirty: true } : {}),
|
|
314
|
-
//
|
|
315
|
-
// current: the authored sources were never compiled.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
314
|
+
// A missing or empty store has no stale file to report, yet nothing in
|
|
315
|
+
// it is current: the authored sources were never compiled. (A missing
|
|
316
|
+
// store without authored sources, and an unreadable or incomplete one,
|
|
317
|
+
// stay "unknown".)
|
|
318
|
+
...(storeUncompiled ? { syncState: "stale", dirty: true } : {}),
|
|
319
319
|
branchAttachment: attachment,
|
|
320
320
|
branchStore: store,
|
|
321
321
|
...(engineStatus ? { engineStatus } : {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result-envelope.d.ts","sourceRoot":"","sources":["../../../src/public/operations/result-envelope.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"result-envelope.d.ts","sourceRoot":"","sources":["../../../src/public/operations/result-envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE7E,eAAO,MAAM,qBAAqB,EAAG,CAAU,CAAC;AAoChD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAS/B;AAoFD,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC,GAAG,MAAM,CAET;AAYD,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE;IACJ,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC,EACD,IAAI,EAAE,CAAC,EACP,OAAO,GAAE,OAAO,CACd,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,GAAG,aAAa,GAAG,aAAa,GAAG,OAAO,CAAC,CACxE,GAAG;IACF;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CACzB,GACL,UAAU,CAAC,CAAC,CAAC,CAmDf;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKrD;AAGD,MAAM,WAAW,uBAAuB;IACtC,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,GACd,uBAAuB,CAYzB;AAaD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC3C,IAAI,CAKN"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MIGRATION_PLAN_REFUSED } from "../../operations/planning/migration-refusal.js";
|
|
1
2
|
export const KIBI_PROTOCOL_VERSION = 1;
|
|
2
3
|
function record(value) {
|
|
3
4
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -103,14 +104,25 @@ function extraFailedEffects(declared, data) {
|
|
|
103
104
|
export function resultVersion(spec) {
|
|
104
105
|
return spec.resultVersion ?? `kibi.${spec.name}.v1`;
|
|
105
106
|
}
|
|
107
|
+
function refusalDetail(row) {
|
|
108
|
+
const results = Array.isArray(row.actionResults) ? row.actionResults : [];
|
|
109
|
+
const failure = results.find((entry) => record(entry) && entry.outcome === "failed");
|
|
110
|
+
return record(failure) && typeof failure.detail === "string"
|
|
111
|
+
? failure.detail
|
|
112
|
+
: "";
|
|
113
|
+
}
|
|
106
114
|
export function toKibiResult(spec, data, options = {}) {
|
|
107
115
|
const row = record(data) ? data : undefined;
|
|
116
|
+
// A migration plan whose actions were all refused changed nothing: it is
|
|
117
|
+
// an error, not a success that needs reconciliation.
|
|
118
|
+
const migrationRefused = row?.version === "kibi.migration-apply-result.v1" &&
|
|
119
|
+
row.outcome === "refused";
|
|
108
120
|
let status = options.status;
|
|
109
121
|
if (!status) {
|
|
110
122
|
status = "success";
|
|
111
123
|
if (row?.status === "committed_with_repairs")
|
|
112
124
|
status = "committed_with_repairs";
|
|
113
|
-
if (row?.status === "rejected")
|
|
125
|
+
if (row?.status === "rejected" || migrationRefused)
|
|
114
126
|
status = "error";
|
|
115
127
|
}
|
|
116
128
|
const terminalError = row?.status === "rejected"
|
|
@@ -119,9 +131,17 @@ export function toKibiResult(spec, data, options = {}) {
|
|
|
119
131
|
message: "Bootstrap stopped at a deterministic failure. Inspect actionResults for committed actions and re-plan from the current state.",
|
|
120
132
|
retryable: false,
|
|
121
133
|
}
|
|
122
|
-
:
|
|
134
|
+
: migrationRefused
|
|
135
|
+
? {
|
|
136
|
+
code: MIGRATION_PLAN_REFUSED,
|
|
137
|
+
message: `Migration plan refused; nothing was changed. ${refusalDetail(row)}`.trim(),
|
|
138
|
+
retryable: false,
|
|
139
|
+
}
|
|
140
|
+
: undefined;
|
|
123
141
|
const resultError = options.error ?? terminalError;
|
|
124
|
-
const outcome = effectOutcome(status, options
|
|
142
|
+
const outcome = effectOutcome(status, migrationRefused && options.attempted === undefined
|
|
143
|
+
? { ...options, attempted: false }
|
|
144
|
+
: options);
|
|
125
145
|
return {
|
|
126
146
|
kibiProtocol: KIBI_PROTOCOL_VERSION,
|
|
127
147
|
operation: spec.name,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
id: kibi-usage
|
|
3
3
|
name: Kibi Usage
|
|
4
4
|
description: Use Kibi's source-first, exact-Git, migration-aware, proof-aware operations safely across MCP or the trusted local CLI, including partial completion repair.
|
|
5
|
-
version: 2.
|
|
5
|
+
version: 2.13.0
|
|
6
6
|
kibiCompatibility: ">=1.0.0"
|
|
7
7
|
tags:
|
|
8
8
|
- kibi
|
|
@@ -148,7 +148,13 @@ error `BOOTSTRAP_PLAN_REJECTED` can follow earlier committed actions: inspect
|
|
|
148
148
|
`data.actionResults` and `data.applied`, then re-plan from the current state.
|
|
149
149
|
Its journal is terminal; never recover or replay it. `BOOTSTRAP_PLAN_INVALID`
|
|
150
150
|
fails preflight before bootstrap writes or a new journal; obtain a corrected
|
|
151
|
-
plan.
|
|
151
|
+
plan. A migration plan whose actions were refused before changing anything
|
|
152
|
+
returns `status: error`, `MIGRATION_PLAN_REFUSED` and `data.outcome:
|
|
153
|
+
refused`: nothing changed, so read the action `detail` and get a new plan.
|
|
154
|
+
`data.outcome: reconciliation_required` (status success) means an action
|
|
155
|
+
failed mid-way: inspect its effect before retrying. `kb_delete` of an
|
|
156
|
+
authored entity returns status success with `deleted: 0`; its `errors` say
|
|
157
|
+
why nothing was deleted. If the status is
|
|
152
158
|
`committed_with_repairs`, the mutation already committed: inspect failed effects,
|
|
153
159
|
execute each required repair action in order, and never retry the original
|
|
154
160
|
operation. Record effect failures, followed actions, and unsafe retries in
|
|
@@ -61,16 +61,16 @@ A schema can close an argument with `argument_constants` (allowed values keyed b
|
|
|
61
61
|
|
|
62
62
|
### Deny predicate
|
|
63
63
|
|
|
64
|
-
Claim: “Suspended users must not publish articles.” When `permission_rule` fits, preserve its positive schema name and encode prohibition as polarity:
|
|
64
|
+
Claim: “Suspended users must not publish articles.” When `permission_rule` fits, preserve its positive schema name and encode prohibition as polarity. `kb_model` (`mode: "predicates"`) plans this fact for the claim; the built-in schema's fourth argument, `decision`, repeats the polarity:
|
|
65
65
|
|
|
66
66
|
```yaml
|
|
67
67
|
id: FACT-SUSPENDED-PUBLISH-DENIED
|
|
68
68
|
status: active
|
|
69
69
|
fact_kind: predicate
|
|
70
70
|
predicate_name: permission_rule
|
|
71
|
-
predicate_args: [suspended_user, publish,
|
|
71
|
+
predicate_args: [suspended_user, publish, articles, deny]
|
|
72
72
|
polarity: deny
|
|
73
|
-
canonical_key: permission_rule(suspended_user,publish,
|
|
73
|
+
canonical_key: permission_rule(suspended_user,publish,articles,deny)
|
|
74
74
|
claim_key: <advisor-issued key for this exact clause>
|
|
75
75
|
claim_text: Suspended users must not publish articles.
|
|
76
76
|
relationship: { type: requires_predicate, from: REQ-SUSPENDED-PUBLISH-DENIED, to: FACT-SUSPENDED-PUBLISH-DENIED }
|
|
@@ -51,6 +51,15 @@ proof_bindings: # optional provenance metadata
|
|
|
51
51
|
|
|
52
52
|
Provenance lives in `proof_bindings`; the contract stays semantic.
|
|
53
53
|
|
|
54
|
+
- Every `required_proofs[].symbol_id` must name an existing `symbol` entity,
|
|
55
|
+
or `kb_check` reports `proof-contract-symbols`. Create the symbols first
|
|
56
|
+
(`kb_upsert`, narrow method-level granularity, never a `type-shape`
|
|
57
|
+
symbol): a production symbol `implements` the requirement and is
|
|
58
|
+
`covered_by` the test; an executable test symbol is `executable_for` it.
|
|
59
|
+
- `proof_bindings[].source_file`, when set, must equal the named symbol's
|
|
60
|
+
`sourceFile`: for a production symbol that is the production file, not the
|
|
61
|
+
spec file that exercises it. Omit it when unsure.
|
|
62
|
+
|
|
54
63
|
## Integration configuration
|
|
55
64
|
|
|
56
65
|
`.kb/proof/integrations.json` (`kibi.proof-integration.v1`) is tracked,
|
|
@@ -62,12 +71,21 @@ returns `integrationPlan`, a hash-bound `kibi.migration-plan.v2`, plus the
|
|
|
62
71
|
proposal to the human, then apply the plan with `kb_apply_plan` (or
|
|
63
72
|
`kibi apply-plan --input -`) passing `plan`, `approvedPlanHash` (the plan's
|
|
64
73
|
`planHash`) and `approvedActionIds`. Once the file exists, a create plan is
|
|
65
|
-
refused
|
|
66
|
-
|
|
74
|
+
refused (`status: error`, `MIGRATION_PLAN_REFUSED`, `data.outcome: refused`;
|
|
75
|
+
nothing is written); `kibi proof inspect --update <id> --json` plans adding or
|
|
76
|
+
replacing one named integration.
|
|
67
77
|
|
|
68
78
|
- `producer: command` — Kibi synthesizes the envelope from the process
|
|
69
79
|
outcome; obligations are bound with `aggregate_run` provenance and the
|
|
70
|
-
single process invocation counts as the known first attempt.
|
|
80
|
+
single process invocation counts as the known first attempt. The run is
|
|
81
|
+
judged as a whole: any failing test in it fails every obligation of every
|
|
82
|
+
test that names the integration, so a suite with unrelated failures proves
|
|
83
|
+
nothing. Narrow the command to the proof-bearing tests with
|
|
84
|
+
`kibi proof inspect --update <id> --command "<command>" --json` (without
|
|
85
|
+
`--command` the proposal is the `package.json` `test` script or the
|
|
86
|
+
detected runner again), or make the run attributable per test: have the
|
|
87
|
+
command write a `kibi.proof-test-report.v1` to the path in
|
|
88
|
+
`KIBI_PROOF_TEST_REPORT` so each test is judged by its own steps.
|
|
71
89
|
- `producer: playwright` (or custom) — the child emits `kibi.proof-run.v1`
|
|
72
90
|
itself (bundled reporter: `kibi-cli/playwright-reporter`).
|
|
73
91
|
- `producer: junit` / `tap` — Kibi converts the native report at `artifact`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
"fast-glob": "^3.2.12",
|
|
236
236
|
"gray-matter": "^4.0.3",
|
|
237
237
|
"js-yaml": "^4.1.0",
|
|
238
|
-
"kibi-core": "^0.18.
|
|
238
|
+
"kibi-core": "^0.18.2",
|
|
239
239
|
"kibi-plugin-builtin": "^0.5.0",
|
|
240
240
|
"kibi-plugin-sdk": "^0.5.0",
|
|
241
241
|
"kibi-swipl": "^1.0.1",
|
|
@@ -276,6 +276,46 @@ function coverageForRequirement(
|
|
|
276
276
|
};
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Requirement statuses that keep a requirement in the coverage-depth review.
|
|
281
|
+
* `closed` is reviewed only when tagged `implemented` (the documented
|
|
282
|
+
* "done, with evidence" shape); a plain closed, deprecated or superseded
|
|
283
|
+
* requirement is retired history, not a coverage gap.
|
|
284
|
+
*/
|
|
285
|
+
const REVIEWED_REQUIREMENT_STATUSES = new Set([
|
|
286
|
+
"open",
|
|
287
|
+
"in_progress",
|
|
288
|
+
"active",
|
|
289
|
+
"approved",
|
|
290
|
+
]);
|
|
291
|
+
|
|
292
|
+
function supersededRequirementIds(
|
|
293
|
+
manifestResults: readonly ExtractionResult[],
|
|
294
|
+
): ReadonlySet<string> {
|
|
295
|
+
return new Set(
|
|
296
|
+
manifestResults.flatMap((result) =>
|
|
297
|
+
result.entity.type === "req"
|
|
298
|
+
? result.relationships.flatMap((relationship) =>
|
|
299
|
+
relationship.type === "supersedes" ? [relationship.to] : [],
|
|
300
|
+
)
|
|
301
|
+
: [],
|
|
302
|
+
),
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// implements REQ-audit-quality-diagnostics-v1
|
|
307
|
+
function isReviewableRequirement(
|
|
308
|
+
requirement: ExtractedEntity,
|
|
309
|
+
superseded: ReadonlySet<string>,
|
|
310
|
+
): boolean {
|
|
311
|
+
if (superseded.has(requirement.id)) return false;
|
|
312
|
+
const status = requirement.status.trim().toLowerCase();
|
|
313
|
+
if (REVIEWED_REQUIREMENT_STATUSES.has(status)) return true;
|
|
314
|
+
return (
|
|
315
|
+
status === "closed" && (requirement.tags ?? []).includes("implemented")
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
279
319
|
export function createCoverageDepthQualityDiagnostics(
|
|
280
320
|
manifestResults: readonly ExtractionResult[],
|
|
281
321
|
proofByRequirement: ReadonlyMap<
|
|
@@ -289,8 +329,13 @@ export function createCoverageDepthQualityDiagnostics(
|
|
|
289
329
|
> = new Map(),
|
|
290
330
|
): readonly QualityDiagnostic[] {
|
|
291
331
|
const entities = entitiesById(manifestResults);
|
|
332
|
+
const superseded = supersededRequirementIds(manifestResults);
|
|
292
333
|
return manifestResults
|
|
293
|
-
.filter(
|
|
334
|
+
.filter(
|
|
335
|
+
(result) =>
|
|
336
|
+
result.entity.type === "req" &&
|
|
337
|
+
isReviewableRequirement(result.entity, superseded),
|
|
338
|
+
)
|
|
294
339
|
.flatMap((result) => {
|
|
295
340
|
const diagnostic = createDiagnostic(
|
|
296
341
|
coverageForRequirement(result, manifestResults, entities),
|
|
@@ -507,11 +507,11 @@ export async function executeStatus(
|
|
|
507
507
|
// Source hashes can all match while the compilation itself is lossy, so
|
|
508
508
|
// a compiler contract change alone makes the store stale.
|
|
509
509
|
...(compilerReason ? { syncState: "stale", dirty: true } : {}),
|
|
510
|
-
//
|
|
511
|
-
// current: the authored sources were never compiled.
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
510
|
+
// A missing or empty store has no stale file to report, yet nothing in
|
|
511
|
+
// it is current: the authored sources were never compiled. (A missing
|
|
512
|
+
// store without authored sources, and an unreadable or incomplete one,
|
|
513
|
+
// stay "unknown".)
|
|
514
|
+
...(storeUncompiled ? { syncState: "stale", dirty: true } : {}),
|
|
515
515
|
branchAttachment: attachment,
|
|
516
516
|
branchStore: store,
|
|
517
517
|
...(engineStatus ? { engineStatus } : {}),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MIGRATION_PLAN_REFUSED } from "../../operations/planning/migration-refusal.js";
|
|
1
2
|
import type { KibiResult, OperationEffect, OperationName } from "./types.js";
|
|
2
3
|
|
|
3
4
|
export const KIBI_PROTOCOL_VERSION = 1 as const;
|
|
@@ -139,6 +140,16 @@ export function resultVersion(spec: {
|
|
|
139
140
|
return spec.resultVersion ?? `kibi.${spec.name}.v1`;
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
function refusalDetail(row: Record<string, unknown>): string {
|
|
144
|
+
const results = Array.isArray(row.actionResults) ? row.actionResults : [];
|
|
145
|
+
const failure = results.find(
|
|
146
|
+
(entry) => record(entry) && entry.outcome === "failed",
|
|
147
|
+
);
|
|
148
|
+
return record(failure) && typeof failure.detail === "string"
|
|
149
|
+
? failure.detail
|
|
150
|
+
: "";
|
|
151
|
+
}
|
|
152
|
+
|
|
142
153
|
export function toKibiResult<T>(
|
|
143
154
|
spec: {
|
|
144
155
|
readonly name: OperationName | string;
|
|
@@ -158,12 +169,17 @@ export function toKibiResult<T>(
|
|
|
158
169
|
} = {},
|
|
159
170
|
): KibiResult<T> {
|
|
160
171
|
const row = record(data) ? data : undefined;
|
|
172
|
+
// A migration plan whose actions were all refused changed nothing: it is
|
|
173
|
+
// an error, not a success that needs reconciliation.
|
|
174
|
+
const migrationRefused =
|
|
175
|
+
row?.version === "kibi.migration-apply-result.v1" &&
|
|
176
|
+
row.outcome === "refused";
|
|
161
177
|
let status = options.status;
|
|
162
178
|
if (!status) {
|
|
163
179
|
status = "success";
|
|
164
180
|
if (row?.status === "committed_with_repairs")
|
|
165
181
|
status = "committed_with_repairs";
|
|
166
|
-
if (row?.status === "rejected") status = "error";
|
|
182
|
+
if (row?.status === "rejected" || migrationRefused) status = "error";
|
|
167
183
|
}
|
|
168
184
|
const terminalError =
|
|
169
185
|
row?.status === "rejected"
|
|
@@ -173,9 +189,21 @@ export function toKibiResult<T>(
|
|
|
173
189
|
"Bootstrap stopped at a deterministic failure. Inspect actionResults for committed actions and re-plan from the current state.",
|
|
174
190
|
retryable: false,
|
|
175
191
|
}
|
|
176
|
-
:
|
|
192
|
+
: migrationRefused
|
|
193
|
+
? {
|
|
194
|
+
code: MIGRATION_PLAN_REFUSED,
|
|
195
|
+
message:
|
|
196
|
+
`Migration plan refused; nothing was changed. ${refusalDetail(row)}`.trim(),
|
|
197
|
+
retryable: false,
|
|
198
|
+
}
|
|
199
|
+
: undefined;
|
|
177
200
|
const resultError = options.error ?? terminalError;
|
|
178
|
-
const outcome = effectOutcome(
|
|
201
|
+
const outcome = effectOutcome(
|
|
202
|
+
status,
|
|
203
|
+
migrationRefused && options.attempted === undefined
|
|
204
|
+
? { ...options, attempted: false }
|
|
205
|
+
: options,
|
|
206
|
+
);
|
|
179
207
|
return {
|
|
180
208
|
kibiProtocol: KIBI_PROTOCOL_VERSION,
|
|
181
209
|
operation: spec.name as OperationName,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
id: kibi-usage
|
|
3
3
|
name: Kibi Usage
|
|
4
4
|
description: Use Kibi's source-first, exact-Git, migration-aware, proof-aware operations safely across MCP or the trusted local CLI, including partial completion repair.
|
|
5
|
-
version: 2.
|
|
5
|
+
version: 2.13.0
|
|
6
6
|
kibiCompatibility: ">=1.0.0"
|
|
7
7
|
tags:
|
|
8
8
|
- kibi
|
|
@@ -148,7 +148,13 @@ error `BOOTSTRAP_PLAN_REJECTED` can follow earlier committed actions: inspect
|
|
|
148
148
|
`data.actionResults` and `data.applied`, then re-plan from the current state.
|
|
149
149
|
Its journal is terminal; never recover or replay it. `BOOTSTRAP_PLAN_INVALID`
|
|
150
150
|
fails preflight before bootstrap writes or a new journal; obtain a corrected
|
|
151
|
-
plan.
|
|
151
|
+
plan. A migration plan whose actions were refused before changing anything
|
|
152
|
+
returns `status: error`, `MIGRATION_PLAN_REFUSED` and `data.outcome:
|
|
153
|
+
refused`: nothing changed, so read the action `detail` and get a new plan.
|
|
154
|
+
`data.outcome: reconciliation_required` (status success) means an action
|
|
155
|
+
failed mid-way: inspect its effect before retrying. `kb_delete` of an
|
|
156
|
+
authored entity returns status success with `deleted: 0`; its `errors` say
|
|
157
|
+
why nothing was deleted. If the status is
|
|
152
158
|
`committed_with_repairs`, the mutation already committed: inspect failed effects,
|
|
153
159
|
execute each required repair action in order, and never retry the original
|
|
154
160
|
operation. Record effect failures, followed actions, and unsafe retries in
|
|
@@ -61,16 +61,16 @@ A schema can close an argument with `argument_constants` (allowed values keyed b
|
|
|
61
61
|
|
|
62
62
|
### Deny predicate
|
|
63
63
|
|
|
64
|
-
Claim: “Suspended users must not publish articles.” When `permission_rule` fits, preserve its positive schema name and encode prohibition as polarity:
|
|
64
|
+
Claim: “Suspended users must not publish articles.” When `permission_rule` fits, preserve its positive schema name and encode prohibition as polarity. `kb_model` (`mode: "predicates"`) plans this fact for the claim; the built-in schema's fourth argument, `decision`, repeats the polarity:
|
|
65
65
|
|
|
66
66
|
```yaml
|
|
67
67
|
id: FACT-SUSPENDED-PUBLISH-DENIED
|
|
68
68
|
status: active
|
|
69
69
|
fact_kind: predicate
|
|
70
70
|
predicate_name: permission_rule
|
|
71
|
-
predicate_args: [suspended_user, publish,
|
|
71
|
+
predicate_args: [suspended_user, publish, articles, deny]
|
|
72
72
|
polarity: deny
|
|
73
|
-
canonical_key: permission_rule(suspended_user,publish,
|
|
73
|
+
canonical_key: permission_rule(suspended_user,publish,articles,deny)
|
|
74
74
|
claim_key: <advisor-issued key for this exact clause>
|
|
75
75
|
claim_text: Suspended users must not publish articles.
|
|
76
76
|
relationship: { type: requires_predicate, from: REQ-SUSPENDED-PUBLISH-DENIED, to: FACT-SUSPENDED-PUBLISH-DENIED }
|
|
@@ -51,6 +51,15 @@ proof_bindings: # optional provenance metadata
|
|
|
51
51
|
|
|
52
52
|
Provenance lives in `proof_bindings`; the contract stays semantic.
|
|
53
53
|
|
|
54
|
+
- Every `required_proofs[].symbol_id` must name an existing `symbol` entity,
|
|
55
|
+
or `kb_check` reports `proof-contract-symbols`. Create the symbols first
|
|
56
|
+
(`kb_upsert`, narrow method-level granularity, never a `type-shape`
|
|
57
|
+
symbol): a production symbol `implements` the requirement and is
|
|
58
|
+
`covered_by` the test; an executable test symbol is `executable_for` it.
|
|
59
|
+
- `proof_bindings[].source_file`, when set, must equal the named symbol's
|
|
60
|
+
`sourceFile`: for a production symbol that is the production file, not the
|
|
61
|
+
spec file that exercises it. Omit it when unsure.
|
|
62
|
+
|
|
54
63
|
## Integration configuration
|
|
55
64
|
|
|
56
65
|
`.kb/proof/integrations.json` (`kibi.proof-integration.v1`) is tracked,
|
|
@@ -62,12 +71,21 @@ returns `integrationPlan`, a hash-bound `kibi.migration-plan.v2`, plus the
|
|
|
62
71
|
proposal to the human, then apply the plan with `kb_apply_plan` (or
|
|
63
72
|
`kibi apply-plan --input -`) passing `plan`, `approvedPlanHash` (the plan's
|
|
64
73
|
`planHash`) and `approvedActionIds`. Once the file exists, a create plan is
|
|
65
|
-
refused
|
|
66
|
-
|
|
74
|
+
refused (`status: error`, `MIGRATION_PLAN_REFUSED`, `data.outcome: refused`;
|
|
75
|
+
nothing is written); `kibi proof inspect --update <id> --json` plans adding or
|
|
76
|
+
replacing one named integration.
|
|
67
77
|
|
|
68
78
|
- `producer: command` — Kibi synthesizes the envelope from the process
|
|
69
79
|
outcome; obligations are bound with `aggregate_run` provenance and the
|
|
70
|
-
single process invocation counts as the known first attempt.
|
|
80
|
+
single process invocation counts as the known first attempt. The run is
|
|
81
|
+
judged as a whole: any failing test in it fails every obligation of every
|
|
82
|
+
test that names the integration, so a suite with unrelated failures proves
|
|
83
|
+
nothing. Narrow the command to the proof-bearing tests with
|
|
84
|
+
`kibi proof inspect --update <id> --command "<command>" --json` (without
|
|
85
|
+
`--command` the proposal is the `package.json` `test` script or the
|
|
86
|
+
detected runner again), or make the run attributable per test: have the
|
|
87
|
+
command write a `kibi.proof-test-report.v1` to the path in
|
|
88
|
+
`KIBI_PROOF_TEST_REPORT` so each test is judged by its own steps.
|
|
71
89
|
- `producer: playwright` (or custom) — the child emits `kibi.proof-run.v1`
|
|
72
90
|
itself (bundled reporter: `kibi-cli/playwright-reporter`).
|
|
73
91
|
- `producer: junit` / `tap` — Kibi converts the native report at `artifact`
|