kibi-cli 2.21.2 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-register-foundation.d.ts.map +1 -1
- package/dist/cli-register-foundation.js +1 -0
- package/dist/commands/check.d.ts +0 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +47 -111
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +96 -6
- package/dist/commands/init-helpers.d.ts +14 -1
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +49 -10
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -1
- package/dist/commands/legacy-storage-migration.d.ts +46 -1
- package/dist/commands/legacy-storage-migration.d.ts.map +1 -1
- package/dist/commands/legacy-storage-migration.js +219 -9
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +48 -182
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +9 -0
- package/dist/extractors/symbols-coordinator.js +1 -0
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +28 -6
- package/dist/operations/migration/inventory-rederive.d.ts +40 -1
- package/dist/operations/migration/inventory-rederive.d.ts.map +1 -1
- package/dist/operations/migration/inventory-rederive.js +99 -8
- package/dist/operations/migration/requirement-status.d.ts +25 -0
- package/dist/operations/migration/requirement-status.d.ts.map +1 -0
- package/dist/operations/migration/requirement-status.js +105 -0
- package/dist/operations/migration/schema6.d.ts +25 -0
- package/dist/operations/migration/schema6.d.ts.map +1 -1
- package/dist/operations/migration/schema6.js +265 -7
- package/dist/operations/migration/symbol-legacy-links.d.ts +51 -0
- package/dist/operations/migration/symbol-legacy-links.d.ts.map +1 -0
- package/dist/operations/migration/symbol-legacy-links.js +319 -0
- package/dist/operations/mutation/context-warning.d.ts +12 -2
- package/dist/operations/mutation/context-warning.d.ts.map +1 -1
- package/dist/operations/mutation/context-warning.js +6 -6
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +130 -35
- package/dist/operations/mutation/types.d.ts +2 -1
- package/dist/operations/mutation/types.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.d.ts +17 -2
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +28 -11
- package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
- package/dist/operations/mutation/validate-upsert.js +40 -8
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +18 -3
- package/dist/public/impact/diagnostics.d.ts.map +1 -1
- package/dist/public/impact/diagnostics.js +24 -14
- package/dist/public/operations/catalog.d.ts +3 -3
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +3 -0
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +6 -1
- package/dist/public/operations/discovery-executors.d.ts +2 -0
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +9 -2
- package/dist/public/operations/migration-plan.d.ts +2 -0
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +101 -16
- package/dist/public/operations/result-envelope.d.ts.map +1 -1
- package/dist/public/operations/result-envelope.js +27 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
- package/dist/public/operations/specs/check-rules.generated.js +2 -1
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/planning.d.ts +2 -2
- package/dist/public/operations/specs/planning.js +2 -2
- package/dist/public/operations/symbol-granularity-check.d.ts +18 -0
- package/dist/public/operations/symbol-granularity-check.d.ts.map +1 -0
- package/dist/public/operations/symbol-granularity-check.js +175 -0
- package/dist/public/symbol-granularity.d.ts +17 -0
- package/dist/public/symbol-granularity.d.ts.map +1 -1
- package/dist/public/symbol-granularity.js +21 -0
- package/dist/traceability/evidence-model.d.ts +7 -3
- package/dist/traceability/evidence-model.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +6 -5
- package/dist/traceability/validate.d.ts +14 -1
- package/dist/traceability/validate.d.ts.map +1 -1
- package/dist/traceability/validate.js +41 -0
- package/dist/utils/rule-registry.generated.d.ts +5 -0
- package/dist/utils/rule-registry.generated.d.ts.map +1 -1
- package/dist/utils/rule-registry.generated.js +6 -0
- package/package.json +3 -3
- package/src/public/impact/diagnostics.ts +29 -17
- package/src/public/operations/check-executor.ts +7 -0
- package/src/public/operations/contracts.ts +6 -1
- package/src/public/operations/discovery-executors.ts +12 -2
- package/src/public/operations/migration-plan.ts +100 -19
- package/src/public/operations/result-envelope.ts +34 -1
- package/src/public/operations/specs/check-rules.generated.ts +2 -1
- package/src/public/operations/specs/planning.ts +2 -2
- package/src/public/operations/symbol-granularity-check.ts +233 -0
- package/src/public/symbol-granularity.ts +37 -0
|
@@ -264,8 +264,7 @@ function getEffectiveManifestRecords(options) {
|
|
|
264
264
|
stagedCoordinateArtifact,
|
|
265
265
|
};
|
|
266
266
|
}
|
|
267
|
-
|
|
268
|
-
const MAX_DETAIL_ITEMS = 6;
|
|
267
|
+
// implements REQ-cli-staged-impact-enforcement
|
|
269
268
|
function diffSymbolEvidence(sourcePath, expectedSymbols, stagedSymbols) {
|
|
270
269
|
const stagedTitles = new Set(stagedSymbols.map((symbol) => symbol.title));
|
|
271
270
|
const expectedTitles = new Set(expectedSymbols.map((symbol) => symbol.title));
|
|
@@ -282,12 +281,14 @@ function diffSymbolEvidence(sourcePath, expectedSymbols, stagedSymbols) {
|
|
|
282
281
|
if (missing.length === 0 && extra.length === 0) {
|
|
283
282
|
return null;
|
|
284
283
|
}
|
|
284
|
+
// Keep the complete lists: human-facing renderers truncate (with an
|
|
285
|
+
// accurate "… and N more"), while JSON evidence carries every name.
|
|
285
286
|
return {
|
|
286
287
|
path: sourcePath,
|
|
287
288
|
expectedCount: expectedSymbols.length,
|
|
288
|
-
coveredCount:
|
|
289
|
-
missing
|
|
290
|
-
extra
|
|
289
|
+
coveredCount: expectedSymbols.length - missing.length,
|
|
290
|
+
missing,
|
|
291
|
+
extra,
|
|
291
292
|
};
|
|
292
293
|
}
|
|
293
294
|
export function assessStagedSymbolsManifest(options) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PrologProcess } from "../prolog.js";
|
|
2
|
+
import { type ParsedViolation } from "../prolog/codec.js";
|
|
2
3
|
export interface ValidationOptions {
|
|
3
4
|
minLinks: number;
|
|
4
5
|
prolog: PrologProcess;
|
|
@@ -17,6 +18,18 @@ declare function splitTopLevelComma(s: string): string[];
|
|
|
17
18
|
declare function splitTopLevelLists(value: string): string[];
|
|
18
19
|
declare function parsePrologListOfLists(value: string): string[][];
|
|
19
20
|
export declare function validateStagedSymbols(options: ValidationOptions): Promise<Violation[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Full-check `symbol-coverage` for the staged change set: the changed source
|
|
23
|
+
* symbols in the consulted overlay plus `extraSymbolIds` (symbols whose staged
|
|
24
|
+
* manifest entry changed). The Prolog rule is the one `kibi check` runs, so a
|
|
25
|
+
* commit cannot pass the staged gate with coverage the full check rejects.
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateStagedSymbolCoverage(options: {
|
|
28
|
+
prolog: PrologProcess;
|
|
29
|
+
extraSymbolIds?: readonly string[];
|
|
30
|
+
}): Promise<ParsedViolation[]>;
|
|
31
|
+
/** Text lines for staged symbol-coverage violations, in `kibi check` form. */
|
|
32
|
+
export declare function formatCoverageViolations(violations: readonly ParsedViolation[]): string;
|
|
20
33
|
export declare function formatViolations(violations: Violation[]): string;
|
|
21
34
|
export declare const __test__: {
|
|
22
35
|
unquoteAtom: typeof unquoteAtom;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/traceability/validate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/traceability/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,cAAc,CAAC;AACnE,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO1C;AAED,iBAAS,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CA0B/C;AAED,iBAAS,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA8CnD;AAED,iBAAS,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAuBzD;AAGD,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,SAAS,EAAE,CAAC,CAgDtB;AAED;;;;;GAKG;AAEH,wBAAsB,4BAA4B,CAAC,OAAO,EAAE;IAC1D,MAAM,EAAE,aAAa,CAAC;IACtB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAgB7B;AAED,8EAA8E;AAE9E,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,SAAS,eAAe,EAAE,GACrC,MAAM,CAaR;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAkBhE;AAGD,eAAO,MAAM,QAAQ;;;;;CAKpB,CAAC"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { resolveKbPlPath } from "../prolog.js";
|
|
3
|
+
import { escapeAtom, parseViolationRows, toPrologAtom, } from "../prolog/codec.js";
|
|
1
4
|
function unquoteAtom(value) {
|
|
2
5
|
// remove surrounding single quotes and unescape doubled quotes
|
|
3
6
|
const trimmed = value.trim();
|
|
@@ -141,6 +144,44 @@ export async function validateStagedSymbols(options) {
|
|
|
141
144
|
}
|
|
142
145
|
return violations;
|
|
143
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Full-check `symbol-coverage` for the staged change set: the changed source
|
|
149
|
+
* symbols in the consulted overlay plus `extraSymbolIds` (symbols whose staged
|
|
150
|
+
* manifest entry changed). The Prolog rule is the one `kibi check` runs, so a
|
|
151
|
+
* commit cannot pass the staged gate with coverage the full check rejects.
|
|
152
|
+
*/
|
|
153
|
+
// implements REQ-014
|
|
154
|
+
export async function validateStagedSymbolCoverage(options) {
|
|
155
|
+
const extra = `[${[...new Set(options.extraSymbolIds ?? [])]
|
|
156
|
+
.sort()
|
|
157
|
+
.map(toPrologAtom)
|
|
158
|
+
.join(",")}]`;
|
|
159
|
+
const checksPl = escapeAtom(path.join(path.dirname(resolveKbPlPath()), "checks.pl"));
|
|
160
|
+
const goal = `(use_module('${checksPl}'), call(checks:check_changed_symbol_coverage(${extra}, Violations)))`;
|
|
161
|
+
const res = await options.prolog.query(goal);
|
|
162
|
+
if (!res.success) {
|
|
163
|
+
throw new Error(`Prolog symbol-coverage query failed: ${res.error || "unknown error"}`);
|
|
164
|
+
}
|
|
165
|
+
return parseViolationRows(res.bindings.Violations ?? "[]");
|
|
166
|
+
}
|
|
167
|
+
/** Text lines for staged symbol-coverage violations, in `kibi check` form. */
|
|
168
|
+
// implements REQ-014
|
|
169
|
+
export function formatCoverageViolations(violations) {
|
|
170
|
+
if (violations.length === 0)
|
|
171
|
+
return "";
|
|
172
|
+
const lines = [
|
|
173
|
+
`Found ${violations.length} symbol-coverage violation(s) in staged symbols (the same rule kibi check runs):`,
|
|
174
|
+
];
|
|
175
|
+
for (const violation of violations) {
|
|
176
|
+
lines.push("", `[${violation.rule}] ${violation.entityId}`);
|
|
177
|
+
if (violation.source)
|
|
178
|
+
lines.push(` Source: ${violation.source}`);
|
|
179
|
+
lines.push(` ${violation.description}`);
|
|
180
|
+
if (violation.suggestion)
|
|
181
|
+
lines.push(` Suggestion: ${violation.suggestion}`);
|
|
182
|
+
}
|
|
183
|
+
return lines.join("\n");
|
|
184
|
+
}
|
|
144
185
|
export function formatViolations(violations) {
|
|
145
186
|
// implements REQ-014
|
|
146
187
|
if (!violations || violations.length === 0)
|
|
@@ -13,6 +13,11 @@ export declare const GENERATED_RULES: readonly [{
|
|
|
13
13
|
readonly description: "Production symbols must directly implement requirements for ownership; covered_by is coverage only and executable_for is test identity only";
|
|
14
14
|
readonly enforcementClass: "canonical";
|
|
15
15
|
readonly category: "traceability";
|
|
16
|
+
}, {
|
|
17
|
+
readonly name: "symbol-granularity";
|
|
18
|
+
readonly description: "A symbol with traceability relationships must not link its source file coarsely while narrower behavioral symbols are available, unless it carries an allowed granularity_reason; the staged gate's symbol_granularity_violation evaluated KB-wide";
|
|
19
|
+
readonly enforcementClass: "canonical";
|
|
20
|
+
readonly category: "traceability";
|
|
16
21
|
}, {
|
|
17
22
|
readonly name: "no-dangling-refs";
|
|
18
23
|
readonly description: "All relationship targets must exist in the KB";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-registry.generated.d.ts","sourceRoot":"","sources":["../../src/utils/rule-registry.generated.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"rule-registry.generated.d.ts","sourceRoot":"","sources":["../../src/utils/rule-registry.generated.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgXlB,CAAC"}
|
|
@@ -26,6 +26,12 @@ export const GENERATED_RULES = [
|
|
|
26
26
|
enforcementClass: "canonical",
|
|
27
27
|
category: "traceability",
|
|
28
28
|
},
|
|
29
|
+
{
|
|
30
|
+
name: "symbol-granularity",
|
|
31
|
+
description: "A symbol with traceability relationships must not link its source file coarsely while narrower behavioral symbols are available, unless it carries an allowed granularity_reason; the staged gate's symbol_granularity_violation evaluated KB-wide",
|
|
32
|
+
enforcementClass: "canonical",
|
|
33
|
+
category: "traceability",
|
|
34
|
+
},
|
|
29
35
|
{
|
|
30
36
|
name: "no-dangling-refs",
|
|
31
37
|
description: "All relationship targets must exist in the KB",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.22.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -235,8 +235,8 @@
|
|
|
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.
|
|
239
|
-
"kibi-plugin-builtin": "^0.5.
|
|
238
|
+
"kibi-core": "^0.22.0",
|
|
239
|
+
"kibi-plugin-builtin": "^0.5.2",
|
|
240
240
|
"kibi-plugin-sdk": "^0.5.0",
|
|
241
241
|
"kibi-swipl": "^1.0.1",
|
|
242
242
|
"saxes": "^6.0.0",
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getNonBehavioralSymbolNames,
|
|
13
13
|
isAllowedGranularityReason,
|
|
14
14
|
isTraceabilityRelationshipType,
|
|
15
|
+
narrowerSymbols,
|
|
15
16
|
} from "../symbol-granularity.js";
|
|
16
17
|
import type {
|
|
17
18
|
QualityDiagnostic,
|
|
@@ -19,16 +20,6 @@ import type {
|
|
|
19
20
|
SymbolGranularityDiagnosticsOptions,
|
|
20
21
|
} from "./types.js";
|
|
21
22
|
|
|
22
|
-
function isNarrowerSymbol(
|
|
23
|
-
manifestSymbolName: string,
|
|
24
|
-
candidate: ExtractedSymbol,
|
|
25
|
-
sourceSymbols: readonly ExtractedSymbol[],
|
|
26
|
-
): boolean {
|
|
27
|
-
if (candidate.name === manifestSymbolName) return false;
|
|
28
|
-
if (candidate.name.startsWith(`${manifestSymbolName}.`)) return true;
|
|
29
|
-
return !sourceSymbols.some((symbol) => symbol.name === manifestSymbolName);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
23
|
function hasTraceabilityRelationship(result: ExtractionResult): boolean {
|
|
33
24
|
return result.relationships.some((relationship) =>
|
|
34
25
|
isTraceabilityRelationshipType(relationship.type),
|
|
@@ -110,6 +101,7 @@ function isChangedBehavioralReviewCandidate(symbol: ExtractedSymbol): boolean {
|
|
|
110
101
|
return symbol.hunkRanges.length > 0;
|
|
111
102
|
}
|
|
112
103
|
|
|
104
|
+
// implements REQ-cli-staged-impact-enforcement, REQ-cli-check
|
|
113
105
|
export function createSymbolGranularityDiagnostics(
|
|
114
106
|
options: SymbolGranularityDiagnosticsOptions,
|
|
115
107
|
): KibiImpactDiagnostic[] {
|
|
@@ -130,15 +122,17 @@ export function createSymbolGranularityDiagnostics(
|
|
|
130
122
|
result.sourceFile,
|
|
131
123
|
options,
|
|
132
124
|
);
|
|
133
|
-
const
|
|
134
|
-
|
|
125
|
+
const narrower = narrowerSymbols(
|
|
126
|
+
result.entity.title,
|
|
127
|
+
candidateSymbols,
|
|
128
|
+
sourceSymbols,
|
|
135
129
|
);
|
|
136
|
-
if (
|
|
130
|
+
if (narrower.length === 0) continue;
|
|
137
131
|
|
|
138
|
-
const behavioralNames = getBehavioralSymbolNames(
|
|
132
|
+
const behavioralNames = getBehavioralSymbolNames(narrower);
|
|
139
133
|
if (behavioralNames.length === 0) continue;
|
|
140
134
|
|
|
141
|
-
const nonBehavioralNames = getNonBehavioralSymbolNames(
|
|
135
|
+
const nonBehavioralNames = getNonBehavioralSymbolNames(narrower);
|
|
142
136
|
const ignoredSymbolsSuggestion =
|
|
143
137
|
nonBehavioralNames.length > 0
|
|
144
138
|
? ` Non-behavioral symbols ignored for this decision: ${nonBehavioralNames.join(
|
|
@@ -146,15 +140,33 @@ export function createSymbolGranularityDiagnostics(
|
|
|
146
140
|
)}.`
|
|
147
141
|
: "";
|
|
148
142
|
|
|
143
|
+
// A title the extractor does not produce (a private class member, a
|
|
144
|
+
// renamed or removed declaration) can never become granular by refresh:
|
|
145
|
+
// say so instead of only calling the link coarse.
|
|
146
|
+
const titleExtracted = sourceSymbols.some(
|
|
147
|
+
(symbol) => symbol.name === result.entity.title,
|
|
148
|
+
);
|
|
149
149
|
diagnostics.push({
|
|
150
150
|
id: "symbol_granularity_violation",
|
|
151
151
|
severity: "error",
|
|
152
152
|
blocking: true,
|
|
153
153
|
category: "symbol",
|
|
154
|
+
entityId: result.entity.id,
|
|
155
|
+
...(result.entity.source ? { source: result.entity.source } : {}),
|
|
154
156
|
files: [result.entity.source, result.sourceFile],
|
|
155
157
|
docs: ["docs/symbol-traceability-taxonomy.md"],
|
|
156
|
-
message:
|
|
157
|
-
|
|
158
|
+
message: titleExtracted
|
|
159
|
+
? `Symbol ${result.entity.id} links ${result.sourceFile} coarsely while granular symbols are available (behavioral only): ${behavioralNames.join(", ")}`
|
|
160
|
+
: `Symbol ${result.entity.id} names '${result.entity.title}', which the source extractor does not produce for ${result.sourceFile} (private class members and removed or renamed declarations are not tracked), so it links ${result.sourceFile} coarsely while granular symbols are available (behavioral only): ${behavioralNames.join(", ")}`,
|
|
161
|
+
suggestion: titleExtracted
|
|
162
|
+
? `Move ownership/coverage/test relationships to the narrow behavioral symbol, add a manifest behavioral anchor, or add granularity_reason with ${ALLOWED_GRANULARITY_REASONS_PROSE} when the coarse symbol is intentional.${ignoredSymbolsSuggestion}`
|
|
163
|
+
: `Re-point this symbol's relationships to the public declaration that owns the behavior (one of: ${behavioralNames.join(", ")}) or remove the symbol; add granularity_reason with ${ALLOWED_GRANULARITY_REASONS_PROSE} only when coarse ownership is intentional.${ignoredSymbolsSuggestion}`,
|
|
164
|
+
evidence: {
|
|
165
|
+
sourceFile: result.sourceFile,
|
|
166
|
+
title: result.entity.title,
|
|
167
|
+
titleExtracted,
|
|
168
|
+
granularSymbols: behavioralNames,
|
|
169
|
+
},
|
|
158
170
|
});
|
|
159
171
|
}
|
|
160
172
|
|
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
collectSourcePathDanglingViolations,
|
|
58
58
|
} from "./source-path-dangling.js";
|
|
59
59
|
import { collectSourceRelationshipParityViolations } from "./source-relationship-parity.js";
|
|
60
|
+
import { collectSymbolGranularityViolations } from "./symbol-granularity-check.js";
|
|
60
61
|
import type { OperationResult } from "./types.js";
|
|
61
62
|
import { readWorkspaceSnapshot } from "./workspace-snapshot.js";
|
|
62
63
|
|
|
@@ -367,6 +368,11 @@ export async function executeCheck(
|
|
|
367
368
|
const sourcePathFindings = rulesAllowlist.has(SOURCE_PATH_DANGLING_RULE)
|
|
368
369
|
? collectSourcePathDanglingViolations(workspaceRoot)
|
|
369
370
|
: [];
|
|
371
|
+
const symbolGranularityFindings = await collectSymbolGranularityViolations(
|
|
372
|
+
prolog,
|
|
373
|
+
workspaceRoot,
|
|
374
|
+
rulesAllowlist,
|
|
375
|
+
);
|
|
370
376
|
const originReviewFindings = await collectOriginReviewViolations(
|
|
371
377
|
prolog,
|
|
372
378
|
rulesAllowlist,
|
|
@@ -390,6 +396,7 @@ export async function executeCheck(
|
|
|
390
396
|
...queryPlanViolations,
|
|
391
397
|
...sourceRelationshipParityViolations,
|
|
392
398
|
...sourcePathFindings,
|
|
399
|
+
...symbolGranularityFindings,
|
|
393
400
|
...predicateConformanceFindings,
|
|
394
401
|
...originReviewFindings,
|
|
395
402
|
...entityContextFindings,
|
|
@@ -442,7 +442,12 @@ const BASE_DATA_SCHEMAS: Readonly<
|
|
|
442
442
|
}),
|
|
443
443
|
kb_skills_read: objectData({ content: stringValue }, ["content"]),
|
|
444
444
|
kb_query: objectData(
|
|
445
|
-
{
|
|
445
|
+
{
|
|
446
|
+
entities: recordArray,
|
|
447
|
+
count: integerValue,
|
|
448
|
+
factQuery: recordValue,
|
|
449
|
+
branchStore: recordValue,
|
|
450
|
+
},
|
|
446
451
|
["entities", "count"],
|
|
447
452
|
),
|
|
448
453
|
kb_search: objectData(
|
|
@@ -95,6 +95,8 @@ export type QueryPayload = {
|
|
|
95
95
|
readonly entities: readonly Record<string, unknown>[];
|
|
96
96
|
readonly count: number;
|
|
97
97
|
readonly factQuery?: FactQueryPayload;
|
|
98
|
+
/** Present when an empty answer came from a store never compiled for this branch. */
|
|
99
|
+
readonly branchStore?: SearchBranchStoreNotice;
|
|
98
100
|
};
|
|
99
101
|
|
|
100
102
|
export const FACT_KINDS = [
|
|
@@ -711,9 +713,13 @@ export async function executeQuery(
|
|
|
711
713
|
? entity
|
|
712
714
|
: { ...entity, firmness };
|
|
713
715
|
});
|
|
716
|
+
// An empty page from a never-compiled store says nothing about the
|
|
717
|
+
// authored entities, so the answer names the store state.
|
|
718
|
+
const storeNotice =
|
|
719
|
+
indexedPage.count === 0 ? uncompiledSearchNotice(context) : null;
|
|
714
720
|
const text =
|
|
715
721
|
indexedPage.count === 0
|
|
716
|
-
? `No entities found${type ? ` of type '${type}'` : ""}.`
|
|
722
|
+
? `No entities found${type ? ` of type '${type}'` : ""}.${storeNotice ? ` Note: ${storeNotice.detail} Run kibi sync before relying on query results.` : ""}`
|
|
717
723
|
: `Found ${indexedPage.count} entities${type ? ` of type '${type}'` : ""}. Showing ${paginated.length} (offset ${offset}, limit ${limit}): ${paginated
|
|
718
724
|
.map((entity) => {
|
|
719
725
|
const entityId = String(entity.id ?? "").replace(
|
|
@@ -725,7 +731,11 @@ export async function executeQuery(
|
|
|
725
731
|
.join(", ")}`;
|
|
726
732
|
return {
|
|
727
733
|
content: [{ type: "text", text }],
|
|
728
|
-
structuredContent: {
|
|
734
|
+
structuredContent: {
|
|
735
|
+
entities: paginated,
|
|
736
|
+
count: indexedPage.count,
|
|
737
|
+
...(storeNotice ? { branchStore: storeNotice } : {}),
|
|
738
|
+
},
|
|
729
739
|
};
|
|
730
740
|
} catch (error) {
|
|
731
741
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -396,6 +396,66 @@ function predicateSchemaAlignmentAction(
|
|
|
396
396
|
/** Migration code for mechanical predicate namespace/alias repairs. */
|
|
397
397
|
export const PREDICATE_SCHEMA_ALIGNMENT_CODE = "predicate_schema_alignment";
|
|
398
398
|
|
|
399
|
+
/** Migration code of the grouped untracked-source parity findings. */
|
|
400
|
+
export const UNTRACKED_KNOWLEDGE_SOURCES_CODE = "untracked_knowledge_sources";
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Every source-relationship-parity finding explained by untracked files
|
|
404
|
+
* (evidence `untrackedSources`) has one cause and one fix: the files Git
|
|
405
|
+
* does not track and no pending-source receipt covers. After a storage move
|
|
406
|
+
* that can be thousands of relationships, so they become one review action
|
|
407
|
+
* naming the files instead of one per relationship.
|
|
408
|
+
*/
|
|
409
|
+
// implements REQ-agent-guided-migration-orchestration, REQ-kibi-source-relationship-parity
|
|
410
|
+
function untrackedSourceActionFromViolations(
|
|
411
|
+
violations: readonly Readonly<Record<string, unknown>>[],
|
|
412
|
+
): { action: MigrationAction; consumed: ReadonlySet<number> } | null {
|
|
413
|
+
const consumed = new Set<number>();
|
|
414
|
+
const files = new Set<string>();
|
|
415
|
+
const entityIds = new Set<string>();
|
|
416
|
+
const relationships: unknown[] = [];
|
|
417
|
+
for (const [index, violation] of violations.entries()) {
|
|
418
|
+
if (violation.rule !== "source-relationship-parity") continue;
|
|
419
|
+
const evidence = isRecord(violation.evidence) ? violation.evidence : {};
|
|
420
|
+
const sources = Array.isArray(evidence.untrackedSources)
|
|
421
|
+
? evidence.untrackedSources.filter(isRecord)
|
|
422
|
+
: [];
|
|
423
|
+
if (sources.length === 0) continue;
|
|
424
|
+
consumed.add(index);
|
|
425
|
+
for (const source of sources) {
|
|
426
|
+
if (typeof source.file === "string") files.add(source.file);
|
|
427
|
+
if (typeof source.entityId === "string") entityIds.add(source.entityId);
|
|
428
|
+
}
|
|
429
|
+
if (evidence.relationship !== undefined)
|
|
430
|
+
relationships.push(evidence.relationship);
|
|
431
|
+
}
|
|
432
|
+
if (consumed.size === 0) return null;
|
|
433
|
+
const listed = [...files].sort();
|
|
434
|
+
return {
|
|
435
|
+
consumed,
|
|
436
|
+
action: migrationAction({
|
|
437
|
+
id: "untracked-knowledge-sources",
|
|
438
|
+
code: UNTRACKED_KNOWLEDGE_SOURCES_CODE,
|
|
439
|
+
category: "relationship",
|
|
440
|
+
safety: "review",
|
|
441
|
+
invocation: {
|
|
442
|
+
kind: "review",
|
|
443
|
+
instruction: `${consumed.size} authored relationship(s) are missing from the compiled KB because ${listed.length} file(s) declaring their endpoints are not tracked by Git and no pending-source receipt covers them: ${listed.join(", ")}. Stage them (git add; staging is enough, Kibi never stages files itself) or rewrite the entities with kb_upsert, then run kibi sync and rerun kibi migrate.`,
|
|
444
|
+
},
|
|
445
|
+
affectedEntityIds: [...entityIds],
|
|
446
|
+
affectedFiles: listed,
|
|
447
|
+
postconditions: [{ rule: "source-relationship-parity", findings: 0 }],
|
|
448
|
+
evidence: {
|
|
449
|
+
relationshipCount: consumed.size,
|
|
450
|
+
files: listed,
|
|
451
|
+
// A sample; the count above is the full number.
|
|
452
|
+
relationships: relationships.slice(0, 20),
|
|
453
|
+
},
|
|
454
|
+
dispositionRequired: true,
|
|
455
|
+
}),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
399
459
|
// implements REQ-agent-guided-migration-orchestration, REQ-kibi-predicate-vocabulary-migration, REQ-cli-schema-migration
|
|
400
460
|
export function buildActionsFromCheck(input: {
|
|
401
461
|
violations?: readonly Readonly<Record<string, unknown>>[];
|
|
@@ -404,8 +464,11 @@ export function buildActionsFromCheck(input: {
|
|
|
404
464
|
const actions: MigrationAction[] = [];
|
|
405
465
|
const lifecycle = lifecycleActionsFromViolations(input.violations ?? []);
|
|
406
466
|
actions.push(...lifecycle.actions.map(migrationAction));
|
|
467
|
+
const untracked = untrackedSourceActionFromViolations(input.violations ?? []);
|
|
468
|
+
if (untracked !== null) actions.push(untracked.action);
|
|
407
469
|
for (const [index, violation] of (input.violations ?? []).entries()) {
|
|
408
470
|
if (lifecycle.consumed.has(index)) continue;
|
|
471
|
+
if (untracked?.consumed.has(index)) continue;
|
|
409
472
|
const rule = typeof violation.rule === "string" ? violation.rule : "check";
|
|
410
473
|
const entityId =
|
|
411
474
|
typeof violation.entityId === "string" ? violation.entityId : "";
|
|
@@ -621,6 +684,18 @@ function readLegacySchemaVersion(
|
|
|
621
684
|
/** Migration action code of the automatic recompile of changed sources. */
|
|
622
685
|
export const FRESHNESS_RESYNC = "freshness_resync";
|
|
623
686
|
|
|
687
|
+
// implements REQ-cli-status-pre-first-sync
|
|
688
|
+
/**
|
|
689
|
+
* Stale reasons one `kibi sync` clears: sources whose content changed since
|
|
690
|
+
* the last compilation, and a store compiled under another compiler contract
|
|
691
|
+
* (the sync discards that cache, re-imports every source and stamps the
|
|
692
|
+
* current contract).
|
|
693
|
+
*/
|
|
694
|
+
const RESYNC_REASON_CODES: ReadonlySet<string> = new Set([
|
|
695
|
+
...MTIME_SOURCE_REASON_CODES,
|
|
696
|
+
"compiler_changed",
|
|
697
|
+
]);
|
|
698
|
+
|
|
624
699
|
export function buildActionsFromStatus(input: {
|
|
625
700
|
workspaceRoot: string;
|
|
626
701
|
branchAttachment?: Readonly<Record<string, unknown>>;
|
|
@@ -686,17 +761,20 @@ export function buildActionsFromStatus(input: {
|
|
|
686
761
|
}
|
|
687
762
|
|
|
688
763
|
const storeState = typeof store?.state === "string" ? store.state : null;
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
764
|
+
// Legacy storage is evaluated whether or not the legacy config survives:
|
|
765
|
+
// legacy copies left beside a migrated .kb/ (moved, identical or stale)
|
|
766
|
+
// are the same finding 'kibi doctor' and 'kibi migrate' report.
|
|
767
|
+
// implements REQ-cli-schema-migration
|
|
768
|
+
const legacyStorage = planLegacyStorageMigration(input.workspaceRoot);
|
|
769
|
+
const legacyStorageMigrationRequired =
|
|
770
|
+
legacyConfigExists(input.workspaceRoot) ||
|
|
771
|
+
legacyStorage.moves.length > 0 ||
|
|
772
|
+
legacyStorage.removals.length > 0 ||
|
|
773
|
+
legacyStorage.blockers.length > 0;
|
|
692
774
|
// A legacy cutover that cannot move its files (a destination already
|
|
693
775
|
// exists, a malformed config) is an operator decision, not an automatic
|
|
694
776
|
// step that fails when applied and takes the compile with it.
|
|
695
|
-
const
|
|
696
|
-
? planLegacyStorageMigration(input.workspaceRoot)
|
|
697
|
-
: null;
|
|
698
|
-
const legacyStorageBlocked =
|
|
699
|
-
legacyStorage !== null && legacyStorage.blockers.length > 0;
|
|
777
|
+
const legacyStorageBlocked = legacyStorage.blockers.length > 0;
|
|
700
778
|
if (storeState === "missing") {
|
|
701
779
|
actions.push(
|
|
702
780
|
migrationAction({
|
|
@@ -750,26 +828,29 @@ export function buildActionsFromStatus(input: {
|
|
|
750
828
|
invocation: legacyStorageBlocked
|
|
751
829
|
? {
|
|
752
830
|
kind: "review",
|
|
753
|
-
instruction: `
|
|
831
|
+
instruction: `Legacy knowledge files outside .kb/ cannot be migrated automatically: ${legacyStorage.blockers.join(" ")} Then run 'kibi migrate --yes'.`,
|
|
754
832
|
}
|
|
755
833
|
: { kind: "cli", command_argv: ["kibi", "migrate", "--yes"] },
|
|
756
834
|
affectedFiles: [
|
|
757
835
|
".kb/manifest.json",
|
|
758
836
|
".kb/migrations",
|
|
759
837
|
".kb/config.json",
|
|
760
|
-
...
|
|
838
|
+
...legacyStorage.moves.map((move) => move.from),
|
|
839
|
+
...legacyStorage.removals.map((removal) => removal.path),
|
|
761
840
|
],
|
|
762
841
|
evidence: {
|
|
763
842
|
configStatus,
|
|
764
|
-
...(
|
|
765
|
-
? {
|
|
766
|
-
: {
|
|
843
|
+
...(legacyStorageMigrationRequired
|
|
844
|
+
? {
|
|
767
845
|
legacyStorage: {
|
|
768
846
|
moves: legacyStorage.moves,
|
|
847
|
+
removals: legacyStorage.removals,
|
|
848
|
+
conflicts: legacyStorage.conflicts,
|
|
769
849
|
blockers: legacyStorage.blockers,
|
|
770
850
|
legacyConfig: legacyStorage.legacyConfig,
|
|
771
851
|
},
|
|
772
|
-
}
|
|
852
|
+
}
|
|
853
|
+
: {}),
|
|
773
854
|
},
|
|
774
855
|
dependsOn: storeState === "missing" ? ["branch-store-ensure"] : [],
|
|
775
856
|
autoApplicable: !operatorOnly,
|
|
@@ -816,12 +897,12 @@ export function buildActionsFromStatus(input: {
|
|
|
816
897
|
// implements REQ-cli-status-pre-first-sync
|
|
817
898
|
// Sources whose content changed since the last compilation (kb_status has
|
|
818
899
|
// already dropped mtime-only reasons) are fixed by recompiling them: one
|
|
819
|
-
// automatic `kibi sync`, not a review per file.
|
|
820
|
-
//
|
|
900
|
+
// automatic `kibi sync`, not a review per file. A store compiled under
|
|
901
|
+
// another compiler contract is fixed by the same sync. An uncompiled store
|
|
902
|
+
// is compiled by the step above, which also covers these reasons.
|
|
821
903
|
const resyncReasons = (input.staleReasons ?? []).filter(
|
|
822
904
|
(reason) =>
|
|
823
|
-
typeof reason.code === "string" &&
|
|
824
|
-
MTIME_SOURCE_REASON_CODES.has(reason.code),
|
|
905
|
+
typeof reason.code === "string" && RESYNC_REASON_CODES.has(reason.code),
|
|
825
906
|
);
|
|
826
907
|
if (resyncReasons.length > 0 && uncompiled === undefined) {
|
|
827
908
|
actions.push(
|
|
@@ -856,7 +937,7 @@ export function buildActionsFromStatus(input: {
|
|
|
856
937
|
// Planned above as an automatic compile step, not a review.
|
|
857
938
|
if (code === BRANCH_STORE_NOT_COMPILED) continue;
|
|
858
939
|
// Planned above as one automatic resync (or covered by the compile).
|
|
859
|
-
if (
|
|
940
|
+
if (RESYNC_REASON_CODES.has(code)) continue;
|
|
860
941
|
const file = typeof reason.path === "string" ? reason.path : "";
|
|
861
942
|
actions.push(
|
|
862
943
|
migrationAction({
|
|
@@ -17,6 +17,36 @@ function diagnostics(value: unknown): KibiResult["diagnostics"] {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// Discovery operations whose empty answer may come from an uncompiled store.
|
|
21
|
+
const STORE_READ_OPERATIONS = new Set(["kb_search", "kb_query"]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A search or query answered from a branch store that was never compiled
|
|
25
|
+
* reads an empty KB. The payload already names the store state; the envelope
|
|
26
|
+
* also raises it as a warning, so a caller that reads only `status` and
|
|
27
|
+
* `diagnostics` does not take the empty answer as "nothing governs this".
|
|
28
|
+
*/
|
|
29
|
+
// implements REQ-cli-status-pre-first-sync, REQ-mcp-search-discovery
|
|
30
|
+
function uncompiledStoreDiagnostics(
|
|
31
|
+
operation: string,
|
|
32
|
+
row: Record<string, unknown> | undefined,
|
|
33
|
+
): KibiResult["diagnostics"] {
|
|
34
|
+
const store = row?.branchStore;
|
|
35
|
+
if (
|
|
36
|
+
!STORE_READ_OPERATIONS.has(operation) ||
|
|
37
|
+
!record(store) ||
|
|
38
|
+
store.state !== "not_compiled"
|
|
39
|
+
)
|
|
40
|
+
return [];
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
...(typeof store.code === "string" ? { code: store.code } : {}),
|
|
44
|
+
severity: "warning",
|
|
45
|
+
message: `${typeof store.detail === "string" ? `${store.detail} ` : ""}An empty answer here does not mean no requirement governs this; run \`kibi sync\` and ask again.`,
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
|
|
20
50
|
function nextActions(value: unknown): KibiResult["nextActions"] {
|
|
21
51
|
if (!Array.isArray(value)) return [];
|
|
22
52
|
return value.flatMap((entry) => {
|
|
@@ -214,7 +244,10 @@ export function toKibiResult<T>(
|
|
|
214
244
|
...spec.effects.map((effect) => effectStatus(effect, row, outcome)),
|
|
215
245
|
...extraFailedEffects(spec.effects, row),
|
|
216
246
|
],
|
|
217
|
-
diagnostics: options.diagnostics ??
|
|
247
|
+
diagnostics: options.diagnostics ?? [
|
|
248
|
+
...diagnostics(row?.diagnostics),
|
|
249
|
+
...uncompiledStoreDiagnostics(String(spec.name), row),
|
|
250
|
+
],
|
|
218
251
|
nextActions: options.nextActions ?? nextActions(row?.nextActions),
|
|
219
252
|
...(resultError ? { error: resultError } : {}),
|
|
220
253
|
};
|
|
@@ -13,6 +13,7 @@ export const SELECTABLE_RULE_NAMES: readonly string[] = [
|
|
|
13
13
|
"must-priority-coverage",
|
|
14
14
|
"symbol-coverage",
|
|
15
15
|
"symbol-traceability",
|
|
16
|
+
"symbol-granularity",
|
|
16
17
|
"no-dangling-refs",
|
|
17
18
|
"source-relationship-parity",
|
|
18
19
|
"branch-store-not-compiled",
|
|
@@ -66,4 +67,4 @@ export const SELECTABLE_RULE_NAMES: readonly string[] = [
|
|
|
66
67
|
// implements REQ-006
|
|
67
68
|
/** Comma-separated "Allowed:" fragment for the kb_check rules description. */
|
|
68
69
|
export const SELECTABLE_RULES_ALLOWED =
|
|
69
|
-
"must-priority-coverage, symbol-coverage, symbol-traceability, no-dangling-refs, source-relationship-parity, branch-store-not-compiled, source-path-dangling, no-cycles, required-fields, deprecated-adr-no-successor, superseded-requirement-open, scenario-feasibility, scenario-feasibility-unknown, exception-claim-keys, numeric-string-value, rule-key-arguments-missing, domain-contradictions, logic-coverage, rule-safety, rule-verifiability, query-plan-safety, req-status-vocabulary, strict-fact-shape, strict-req-fact-pairing, predicate-verifiability, strict-readiness, semantic-completeness, related-requirement-unmodeled, proof-contract-symbols, entity-id-style, domain-redundancy, domain-implication, subject-key-identity, subject-key-shape, ontology-quality, exception-unapproved, exception-approval-self-attested, agent-requirement-unapproved, requirement-rationale-missing, symbol-owner-superseded, adr-unlinked, adr-proposed, predicate-schema-conformance, entity-context-missing, entity-context-acknowledged, decision-firm-unreferenced, decision-firm-superseded, supersedes-reason-missing, supersedes-reason-acknowledged, policy-ownership, policy-markers";
|
|
70
|
+
"must-priority-coverage, symbol-coverage, symbol-traceability, symbol-granularity, no-dangling-refs, source-relationship-parity, branch-store-not-compiled, source-path-dangling, no-cycles, required-fields, deprecated-adr-no-successor, superseded-requirement-open, scenario-feasibility, scenario-feasibility-unknown, exception-claim-keys, numeric-string-value, rule-key-arguments-missing, domain-contradictions, logic-coverage, rule-safety, rule-verifiability, query-plan-safety, req-status-vocabulary, strict-fact-shape, strict-req-fact-pairing, predicate-verifiability, strict-readiness, semantic-completeness, related-requirement-unmodeled, proof-contract-symbols, entity-id-style, domain-redundancy, domain-implication, subject-key-identity, subject-key-shape, ontology-quality, exception-unapproved, exception-approval-self-attested, agent-requirement-unapproved, requirement-rationale-missing, symbol-owner-superseded, adr-unlinked, adr-proposed, predicate-schema-conformance, entity-context-missing, entity-context-acknowledged, decision-firm-unreferenced, decision-firm-superseded, supersedes-reason-missing, supersedes-reason-acknowledged, policy-ownership, policy-markers";
|
|
@@ -236,13 +236,13 @@ export const applyPlanSpec = {
|
|
|
236
236
|
type: "object",
|
|
237
237
|
required: ["version", "planHash"],
|
|
238
238
|
description:
|
|
239
|
-
"The complete kibi.bootstrap-plan.v1, kibi.compile-plan.v1, kibi.migration-plan.v2, or hash-bound kibi.entity-deletion-plan.v1 object. Migration plans require approvedActionIds and reject blocked
|
|
239
|
+
"The complete kibi.bootstrap-plan.v1, kibi.compile-plan.v1, kibi.migration-plan.v2, or hash-bound kibi.entity-deletion-plan.v1 object. Migration plans require approvedActionIds and reject blocked actions and review actions Kibi cannot execute.",
|
|
240
240
|
},
|
|
241
241
|
approvedActionIds: {
|
|
242
242
|
type: "array",
|
|
243
243
|
items: { type: "string", minLength: 1 },
|
|
244
244
|
description:
|
|
245
|
-
"Required for migration-plan.v2. Exact
|
|
245
|
+
"Required for migration-plan.v2. Exact action IDs explicitly approved for this application: automatic actions, or review actions with a cli invocation that Kibi executes only when approved by id (for example semantic_inventory_rederive_drop_grounding).",
|
|
246
246
|
},
|
|
247
247
|
recoveryJournalId: {
|
|
248
248
|
type: "string",
|