kibi-cli 2.16.0 → 2.18.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 +46 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +9 -3
- package/dist/commands/sync/untracked-targets.d.ts.map +1 -1
- package/dist/commands/sync/untracked-targets.js +39 -9
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +26 -5
- package/dist/engine.d.ts +4 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +40 -10
- package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applicability.js +12 -3
- package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-catalog-4.js +4 -0
- package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-loader.js +3 -0
- package/dist/operations/modeling/predicate-score-3.js +1 -1
- package/dist/operations/modeling/predicate-types.d.ts +5 -0
- package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-utils.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-utils.js +2 -3
- package/dist/operations/mutation/source-authoring.d.ts +9 -0
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +38 -2
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +33 -8
- 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/clauses.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/clauses.js +7 -1
- 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/prolog/store-lock.d.ts +22 -0
- package/dist/prolog/store-lock.d.ts.map +1 -1
- package/dist/prolog/store-lock.js +45 -1
- package/dist/prolog.d.ts +7 -1
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +21 -3
- package/dist/proof/inspect.d.ts +9 -0
- package/dist/proof/inspect.d.ts.map +1 -1
- package/dist/proof/inspect.js +10 -0
- 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/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +3 -0
- package/dist/public/operations/discovery-executors.d.ts +14 -1
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +62 -12
- 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 +31 -0
- package/dist/public/operations/result-envelope.d.ts.map +1 -1
- package/dist/public/operations/result-envelope.js +23 -3
- package/dist/public/operations/source-relationship-parity.d.ts.map +1 -1
- package/dist/public/operations/source-relationship-parity.js +55 -1
- package/dist/public/skills/kibi-usage/SKILL.md +19 -4
- package/dist/public/skills/kibi-usage/resources/fact-lanes.md +3 -3
- package/dist/public/skills/kibi-usage/resources/proof.md +44 -3
- package/dist/utils/branch-store.d.ts +8 -0
- package/dist/utils/branch-store.d.ts.map +1 -1
- package/dist/utils/branch-store.js +27 -1
- package/dist/utils/content-freshness.d.ts +13 -0
- package/dist/utils/content-freshness.d.ts.map +1 -0
- package/dist/utils/content-freshness.js +161 -0
- package/package.json +2 -2
- package/src/public/impact/coverage-depth-quality.ts +46 -1
- package/src/public/operations/contracts.ts +3 -0
- package/src/public/operations/discovery-executors.ts +89 -11
- package/src/public/operations/migration-plan.ts +44 -0
- package/src/public/operations/result-envelope.ts +31 -3
- package/src/public/operations/source-relationship-parity.ts +60 -3
- package/src/public/skills/kibi-usage/SKILL.md +19 -4
- package/src/public/skills/kibi-usage/resources/fact-lanes.md +3 -3
- package/src/public/skills/kibi-usage/resources/proof.md +44 -3
|
@@ -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];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../../src/public/operations/contracts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAC1B,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAsZpE;;uEAEuE;AACvE,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,GAC1B,mBAAmB,CAMrB;
|
|
1
|
+
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../../src/public/operations/contracts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAC1B,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAsZpE;;uEAEuE;AACvE,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,GAC1B,mBAAmB,CAMrB;AAoXD,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAa3C,CAAC;AAmDF,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,EAClD,YAAY,EAAE,MAAM,GACnB,IAAI,CAIN;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,SAAS,0BAA0B,EAAE,CAoBvC"}
|
|
@@ -355,6 +355,8 @@ const BASE_DATA_SCHEMAS = {
|
|
|
355
355
|
truncated: booleanValue,
|
|
356
356
|
queryAnalysis: recordValue,
|
|
357
357
|
answer: recordValue,
|
|
358
|
+
branch: { type: ["string", "null"] },
|
|
359
|
+
branchStore: recordValue,
|
|
358
360
|
}, ["results", "count"]),
|
|
359
361
|
kb_status: objectData({
|
|
360
362
|
branch: stringValue,
|
|
@@ -377,6 +379,7 @@ const BASE_DATA_SCHEMAS = {
|
|
|
377
379
|
staleReasons: recordArray,
|
|
378
380
|
staleReasonCount: integerValue,
|
|
379
381
|
staleReasonsTruncated: booleanValue,
|
|
382
|
+
notices: recordArray,
|
|
380
383
|
branchAttachment: recordValue,
|
|
381
384
|
proofSnapshotChanges: recordArray,
|
|
382
385
|
proofSnapshotChangeCount: integerValue,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type IntentSearchAnalysis, type IntentSearchFacets, type IntentSearchMatch, type SourceLocation } from "../../intent-search.js";
|
|
2
2
|
import { type SearchAnswer } from "../../search-answer.js";
|
|
3
3
|
import type { SearchMatch } from "../../search-ranking.js";
|
|
4
|
-
import { type BranchStoreInspection } from "../../utils/branch-store.js";
|
|
4
|
+
import { BRANCH_STORE_NOT_COMPILED, type BranchStoreInspection } from "../../utils/branch-store.js";
|
|
5
5
|
import { type MigrationConfigStatus, type MigrationPlan } from "./migration-plan.js";
|
|
6
6
|
import type { OperationContext } from "./runtime-types.js";
|
|
7
7
|
import type { OperationResult } from "./types.js";
|
|
@@ -35,6 +35,17 @@ export type SearchPayload = {
|
|
|
35
35
|
readonly truncated?: boolean;
|
|
36
36
|
readonly queryAnalysis?: IntentSearchAnalysis;
|
|
37
37
|
readonly answer?: SearchAnswer;
|
|
38
|
+
/** The Kibi branch whose store was searched, when the runtime attached one. */
|
|
39
|
+
readonly branch?: string | null;
|
|
40
|
+
/** Present when the searched store was never compiled for this branch. */
|
|
41
|
+
readonly branchStore?: SearchBranchStoreNotice;
|
|
42
|
+
};
|
|
43
|
+
export type SearchBranchStoreNotice = {
|
|
44
|
+
readonly state: "not_compiled";
|
|
45
|
+
readonly code: typeof BRANCH_STORE_NOT_COMPILED;
|
|
46
|
+
readonly path: string;
|
|
47
|
+
readonly detail: string;
|
|
48
|
+
readonly remediation: Readonly<Record<string, unknown>>;
|
|
38
49
|
};
|
|
39
50
|
export type StatusInput = Readonly<Record<string, never>>;
|
|
40
51
|
export type StatusPayload = {
|
|
@@ -54,6 +65,8 @@ export type StatusPayload = {
|
|
|
54
65
|
readonly staleReasons?: readonly Record<string, unknown>[];
|
|
55
66
|
readonly staleReasonCount?: number;
|
|
56
67
|
readonly staleReasonsTruncated?: boolean;
|
|
68
|
+
/** Informational, non-blocking observations (never make the KB stale). */
|
|
69
|
+
readonly notices?: readonly Record<string, unknown>[];
|
|
57
70
|
readonly branchAttachment?: {
|
|
58
71
|
readonly gitBranch: string;
|
|
59
72
|
readonly kbBranch: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery-executors.d.ts","sourceRoot":"","sources":["../../../src/public/operations/discovery-executors.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,KAAK,YAAY,EAAqB,MAAM,wBAAwB,CAAC;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,
|
|
1
|
+
{"version":3,"file":"discovery-executors.d.ts","sourceRoot":"","sources":["../../../src/public/operations/discovery-executors.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,11 +7,34 @@ import { SwiplResolutionError } from "../../prolog/swipl-resolver.js";
|
|
|
7
7
|
import { buildSearchAnswer } from "../../search-answer.js";
|
|
8
8
|
import { rankEntities } from "../../search-ranking.js";
|
|
9
9
|
import { resolveBranchAttachment } from "../../utils/branch-resolver.js";
|
|
10
|
-
import { BRANCH_STORE_NOT_COMPILED, branchStoreReason, inspectBranchStore, storeLockJournalReason, uncompiledBranchStoreReason, } from "../../utils/branch-store.js";
|
|
10
|
+
import { BRANCH_STORE_NOT_COMPILED, branchStoreReason, inspectBranchStore, storeLockJournalReason, storeLockTakeoverReason, uncompiledBranchStoreReason, } from "../../utils/branch-store.js";
|
|
11
|
+
import { confirmSourceNewerReasons } from "../../utils/content-freshness.js";
|
|
11
12
|
import { loadSearchCandidates, paginateResults, queryEntitiesViaQuery, reloadFullEntities, validateEntityType, } from "./discovery-entities.js";
|
|
12
13
|
import { buildActionsFromStatus, readMigrationConfigStatus, } from "./migration-plan.js";
|
|
13
14
|
import { OperationJsonDecodeError, runOperationJsonQuery, } from "./prolog-json.js";
|
|
14
15
|
import { readWorkspaceSnapshot } from "./workspace-snapshot.js";
|
|
16
|
+
/**
|
|
17
|
+
* An empty result from a store that was never compiled says nothing about
|
|
18
|
+
* the authored requirements, so search reports the store state itself (the
|
|
19
|
+
* same reason kb_status and kb_check use) instead of a bare "no results".
|
|
20
|
+
*/
|
|
21
|
+
// implements REQ-cli-status-pre-first-sync, REQ-mcp-search-discovery
|
|
22
|
+
function uncompiledSearchNotice(context) {
|
|
23
|
+
const attachment = context.branchAttachment;
|
|
24
|
+
if (attachment === undefined)
|
|
25
|
+
return null;
|
|
26
|
+
const store = inspectBranchStore(context.workspaceRoot, attachment.kbBranch);
|
|
27
|
+
const reason = uncompiledBranchStoreReason(context.workspaceRoot, store, attachment.kbBranch);
|
|
28
|
+
if (reason === null)
|
|
29
|
+
return null;
|
|
30
|
+
return {
|
|
31
|
+
state: "not_compiled",
|
|
32
|
+
code: BRANCH_STORE_NOT_COMPILED,
|
|
33
|
+
path: store.path,
|
|
34
|
+
detail: String(reason.detail),
|
|
35
|
+
remediation: reason.remediation,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
15
38
|
function requireProlog(context) {
|
|
16
39
|
if (!context.prolog) {
|
|
17
40
|
throw new Error("Discovery operation requires a Prolog context");
|
|
@@ -118,6 +141,15 @@ export async function executeSearch(input, context) {
|
|
|
118
141
|
validateEntityType(type);
|
|
119
142
|
try {
|
|
120
143
|
const prolog = requireProlog(context);
|
|
144
|
+
const branch = context.branchAttachment?.kbBranch ?? null;
|
|
145
|
+
const storeNotice = uncompiledSearchNotice(context);
|
|
146
|
+
const branchFields = {
|
|
147
|
+
branch,
|
|
148
|
+
...(storeNotice ? { branchStore: storeNotice } : {}),
|
|
149
|
+
};
|
|
150
|
+
const storeNote = storeNotice
|
|
151
|
+
? ` Note: ${storeNotice.detail} Run kibi sync before relying on search results.`
|
|
152
|
+
: "";
|
|
121
153
|
const intentMode = rankingMode === "intent-v1" ||
|
|
122
154
|
semanticFacets !== undefined ||
|
|
123
155
|
sourceLocations !== undefined;
|
|
@@ -142,12 +174,12 @@ export async function executeSearch(input, context) {
|
|
|
142
174
|
: undefined;
|
|
143
175
|
// The structured payload carries every row; the text is a short
|
|
144
176
|
// human-readable digest instead of a second copy of the results.
|
|
145
|
-
const text = intentResult.matches.length === 0
|
|
177
|
+
const text = `${intentResult.matches.length === 0
|
|
146
178
|
? `No intent search results for '${trimmedQuery}' (abstained).`
|
|
147
179
|
: `Found ${intentResult.matches.length} intent search results for '${trimmedQuery}'${intentResult.analysis.ambiguous ? " (top matches are close; confirm before relying on one)" : ""}. Top: ${paginated
|
|
148
180
|
.slice(0, 5)
|
|
149
181
|
.map((match) => String(match.entity.id ?? ""))
|
|
150
|
-
.join(", ")}${answerLayer ? `. Governing: ${answerLayer.governing.map((req) => req.id).join(", ") || "none found"}` : ""}
|
|
182
|
+
.join(", ")}${answerLayer ? `. Governing: ${answerLayer.governing.map((req) => req.id).join(", ") || "none found"}` : ""}.`}${storeNote}`;
|
|
151
183
|
return {
|
|
152
184
|
content: [{ type: "text", text }],
|
|
153
185
|
structuredContent: {
|
|
@@ -156,6 +188,7 @@ export async function executeSearch(input, context) {
|
|
|
156
188
|
truncated: offset + paginated.length < intentResult.matches.length,
|
|
157
189
|
queryAnalysis: intentResult.analysis,
|
|
158
190
|
...(answerLayer ? { answer: answerLayer } : {}),
|
|
191
|
+
...branchFields,
|
|
159
192
|
},
|
|
160
193
|
};
|
|
161
194
|
}
|
|
@@ -168,17 +201,18 @@ export async function executeSearch(input, context) {
|
|
|
168
201
|
}, context.signal);
|
|
169
202
|
const matches = await rankEntities(entities, trimmedQuery, context.workspaceRoot);
|
|
170
203
|
const paginated = paginateResults(matches, limit, offset);
|
|
171
|
-
const text = matches.length === 0
|
|
204
|
+
const text = `${matches.length === 0
|
|
172
205
|
? `No search results for '${trimmedQuery}'.`
|
|
173
206
|
: `Found ${matches.length} search results for '${trimmedQuery}'. Showing ${paginated.length} (offset ${offset}, limit ${limit}): ${paginated
|
|
174
207
|
.map((match) => `${String(match.entity.id ?? "")} [${match.reasons.join(", ")}]`)
|
|
175
|
-
.join(", ")}`;
|
|
208
|
+
.join(", ")}`}${storeNote}`;
|
|
176
209
|
return {
|
|
177
210
|
content: [{ type: "text", text }],
|
|
178
211
|
structuredContent: {
|
|
179
212
|
results: await projectMatches(prolog, paginated, fields),
|
|
180
213
|
count: matches.length,
|
|
181
214
|
truncated: offset + paginated.length < matches.length,
|
|
215
|
+
...branchFields,
|
|
182
216
|
},
|
|
183
217
|
};
|
|
184
218
|
}
|
|
@@ -272,7 +306,19 @@ export async function executeStatus(_input, context) {
|
|
|
272
306
|
await ownedEngine?.terminate();
|
|
273
307
|
}
|
|
274
308
|
const snapshotEvidence = await readWorkspaceSnapshot(context);
|
|
275
|
-
|
|
309
|
+
// implements REQ-cli-status-pre-first-sync
|
|
310
|
+
// Prolog compares file times only. A fresh clone or a checkout rewrites
|
|
311
|
+
// every mtime without changing a byte, so confirm each "newer" source
|
|
312
|
+
// against the content hashes the last compilation recorded.
|
|
313
|
+
const prologReasons = payload.staleReasons ?? [];
|
|
314
|
+
const confirmed = confirmSourceNewerReasons(context.workspaceRoot, store.path, prologReasons);
|
|
315
|
+
const existingReasons = confirmed.kept;
|
|
316
|
+
if (confirmed.dropped.length > 0 &&
|
|
317
|
+
confirmed.kept.length === 0 &&
|
|
318
|
+
payload.staleReasonsTruncated !== true &&
|
|
319
|
+
payload.syncState === "stale") {
|
|
320
|
+
payload = { ...payload, syncState: "fresh", dirty: false };
|
|
321
|
+
}
|
|
276
322
|
// implements REQ-cli-status-pre-first-sync
|
|
277
323
|
// A missing or empty store beside authored sources gets the one reason
|
|
278
324
|
// whose remediation works (kibi sync); `kibi branch ensure` alone would
|
|
@@ -280,6 +326,9 @@ export async function executeStatus(_input, context) {
|
|
|
280
326
|
const storeReason = uncompiledBranchStoreReason(context.workspaceRoot, store, attachment.kbBranch) ?? branchStoreReason(store);
|
|
281
327
|
const storeUncompiled = storeReason?.code === BRANCH_STORE_NOT_COMPILED;
|
|
282
328
|
const lockReason = storeLockJournalReason(store.path);
|
|
329
|
+
// implements REQ-core-journaled-engine-persistence
|
|
330
|
+
// A stale-lock takeover heals itself; report it without making the KB stale.
|
|
331
|
+
const takeoverNotice = storeLockTakeoverReason(store.path);
|
|
283
332
|
const compilerReason = compilerFingerprintReason(store.path);
|
|
284
333
|
const engineReason = engineStatus
|
|
285
334
|
? {
|
|
@@ -311,13 +360,14 @@ export async function executeStatus(_input, context) {
|
|
|
311
360
|
// Source hashes can all match while the compilation itself is lossy, so
|
|
312
361
|
// a compiler contract change alone makes the store stale.
|
|
313
362
|
...(compilerReason ? { syncState: "stale", dirty: true } : {}),
|
|
314
|
-
//
|
|
315
|
-
// current: the authored sources were never compiled.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
363
|
+
// A missing or empty store has no stale file to report, yet nothing in
|
|
364
|
+
// it is current: the authored sources were never compiled. (A missing
|
|
365
|
+
// store without authored sources, and an unreadable or incomplete one,
|
|
366
|
+
// stay "unknown".)
|
|
367
|
+
...(storeUncompiled ? { syncState: "stale", dirty: true } : {}),
|
|
319
368
|
branchAttachment: attachment,
|
|
320
369
|
branchStore: store,
|
|
370
|
+
...(takeoverNotice ? { notices: [takeoverNotice] } : {}),
|
|
321
371
|
...(engineStatus ? { engineStatus } : {}),
|
|
322
372
|
staleReasons,
|
|
323
373
|
staleReasonCount: staleReasons.length,
|
|
@@ -447,7 +497,7 @@ export async function executeStatus(_input, context) {
|
|
|
447
497
|
content: [
|
|
448
498
|
{
|
|
449
499
|
type: "text",
|
|
450
|
-
text: `Branch ${payload.branch} is ${enrichedPayload.syncState} (snapshot ${payload.snapshotId}, dirty=${enrichedPayload.dirty}, proofSnapshot=${enrichedPayload.proofSnapshot})${storeUncompiled ? `. Blocking: ${String(storeReason?.detail)} Run kibi sync.` : ""}`,
|
|
500
|
+
text: `Branch ${payload.branch} is ${enrichedPayload.syncState} (snapshot ${payload.snapshotId}, dirty=${enrichedPayload.dirty}, proofSnapshot=${enrichedPayload.proofSnapshot})${storeUncompiled ? `. Blocking: ${String(storeReason?.detail)} Run kibi sync.` : ""}${takeoverNotice ? `. Note: ${String(takeoverNotice.detail)}` : ""}`,
|
|
451
501
|
},
|
|
452
502
|
],
|
|
453
503
|
structuredContent: statusWithPlan,
|
|
@@ -94,6 +94,8 @@ export type MigrationConfigStatus = Readonly<{
|
|
|
94
94
|
configHash: string | null;
|
|
95
95
|
}>;
|
|
96
96
|
export declare function readMigrationConfigStatus(workspaceRoot: string): MigrationConfigStatus;
|
|
97
|
+
/** Migration action code of the automatic recompile of changed sources. */
|
|
98
|
+
export declare const FRESHNESS_RESYNC = "freshness_resync";
|
|
97
99
|
export declare function buildActionsFromStatus(input: {
|
|
98
100
|
workspaceRoot: string;
|
|
99
101
|
branchAttachment?: Readonly<Record<string, unknown>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-plan.d.ts","sourceRoot":"","sources":["../../../src/public/operations/migration-plan.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migration-plan.d.ts","sourceRoot":"","sources":["../../../src/public/operations/migration-plan.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,sBAAsB,EAAG,wBAAiC,CAAC;AAExE,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,SAAS,CAAC;AACvD,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAChF,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC;AAErE,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,CAAC;IACP,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1C,CAAC,GACF,QAAQ,CAAC;IACP,IAAI,EAAE,KAAK,CAAC;IACZ,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC,CAAC,GACF,QAAQ,CAAC;IACP,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAAC;AAEP,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EACJ,SAAS,GACT,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,WAAW,GACX,cAAc,GACd,QAAQ,GACR,UAAU,GACV,OAAO,GACP,SAAS,CAAC;IACd,KAAK,EAAE,oBAAoB,CAAC;IAC5B,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,mBAAmB,CAAC;IAChC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,aAAa,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAC5D,cAAc,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAC7D,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,mBAAmB,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACtD,CAAC,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,OAAO,EAAE,OAAO,sBAAsB,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IACvD,QAAQ,EAAE,QAAQ,CAAC;QACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,CAAC,CAAC;IACH,KAAK,EAAE,QAAQ,CAAC;QACd,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;QACpC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;QACrC,QAAQ,EAAE,OAAO,CAAC;KACnB,CAAC,CAAC;IACH,OAAO,EAAE,QAAQ,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,mBAAmB,EAAE,MAAM,CAAC;QAC5B,oBAAoB,EAAE,MAAM,CAAC;KAC9B,CAAC,CAAC;IACH,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC;AAEH,KAAK,SAAS,GAAG,QAAQ,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9C,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC,CAAC,CAAC;AAuBH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIxD;AAeD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,GACzC,MAAM,CAOR;AAuED,wBAAgB,kBAAkB,CAAC,KAAK,GAAE,SAAc,GAAG,aAAa,CAsCvE;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,aAAa,EAAE,GAC9B,aAAa,CA0Bf;AAED;;;GAGG;AAEH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GACzB,aAAa,CASf;AA6BD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,CAAC,GACrE,eAAe,CAEjB;AAkDD,uEAAuE;AACvE,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;AAG5E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,UAAU,CAAC,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAC1D,kBAAkB,CAAC,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;CACnE,GAAG,eAAe,EAAE,CAkEpB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/C,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACtD,GAAG,eAAe,EAAE,CAgFpB;AAED,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IAC9D,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC,CAAC;AAEH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,GACpB,qBAAqB,CA8BvB;AAwBD,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAC5D,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,qBAAqB,CAAC;CACtC,GAAG,aAAa,CAuRhB"}
|
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { BRANCH_STORE_NOT_COMPILED } from "../../utils/branch-store.js";
|
|
5
5
|
import { legacyConfigExists } from "../../utils/config.js";
|
|
6
|
+
import { MTIME_SOURCE_REASON_CODES } from "../../utils/content-freshness.js";
|
|
6
7
|
import { readKbManifestStatus } from "../../utils/kb-manifest.js";
|
|
7
8
|
import { getSchemaVersionStatus, normalizeSchemaVersion, } from "../../utils/schema-version.js";
|
|
8
9
|
import { lifecycleActionsFromViolations, schema6ReviewActionsFromCheck, } from "./schema6-check-actions.js";
|
|
@@ -438,6 +439,8 @@ function readLegacySchemaVersion(workspaceRoot) {
|
|
|
438
439
|
}
|
|
439
440
|
}
|
|
440
441
|
// implements REQ-agent-guided-migration-orchestration
|
|
442
|
+
/** Migration action code of the automatic recompile of changed sources. */
|
|
443
|
+
export const FRESHNESS_RESYNC = "freshness_resync";
|
|
441
444
|
export function buildActionsFromStatus(input) {
|
|
442
445
|
const actions = [];
|
|
443
446
|
const attachment = input.branchAttachment;
|
|
@@ -562,11 +565,39 @@ export function buildActionsFromStatus(input) {
|
|
|
562
565
|
autoApplicable: true,
|
|
563
566
|
}));
|
|
564
567
|
}
|
|
568
|
+
// implements REQ-cli-status-pre-first-sync
|
|
569
|
+
// Sources whose content changed since the last compilation (kb_status has
|
|
570
|
+
// already dropped mtime-only reasons) are fixed by recompiling them: one
|
|
571
|
+
// automatic `kibi sync`, not a review per file. An uncompiled store is
|
|
572
|
+
// compiled by the step above, which also covers these files.
|
|
573
|
+
const resyncReasons = (input.staleReasons ?? []).filter((reason) => typeof reason.code === "string" &&
|
|
574
|
+
MTIME_SOURCE_REASON_CODES.has(reason.code));
|
|
575
|
+
if (resyncReasons.length > 0 && uncompiled === undefined) {
|
|
576
|
+
actions.push(migrationAction({
|
|
577
|
+
id: "freshness-resync",
|
|
578
|
+
code: FRESHNESS_RESYNC,
|
|
579
|
+
category: "freshness",
|
|
580
|
+
safety: "automatic",
|
|
581
|
+
invocation: { kind: "cli", command_argv: ["kibi", "sync"] },
|
|
582
|
+
affectedFiles: [
|
|
583
|
+
...new Set(resyncReasons.flatMap((reason) => typeof reason.path === "string" ? [reason.path] : [])),
|
|
584
|
+
].sort(),
|
|
585
|
+
evidence: { reasons: resyncReasons },
|
|
586
|
+
dependsOn: actions
|
|
587
|
+
.map((action) => action.id)
|
|
588
|
+
.filter((id) => id === "branch-store-ensure" || id === "schema-config-upgrade"),
|
|
589
|
+
postconditions: [{ syncState: "fresh" }],
|
|
590
|
+
autoApplicable: true,
|
|
591
|
+
}));
|
|
592
|
+
}
|
|
565
593
|
for (const reason of input.staleReasons ?? []) {
|
|
566
594
|
const code = typeof reason.code === "string" ? reason.code : "stale_source";
|
|
567
595
|
// Planned above as an automatic compile step, not a review.
|
|
568
596
|
if (code === BRANCH_STORE_NOT_COMPILED)
|
|
569
597
|
continue;
|
|
598
|
+
// Planned above as one automatic resync (or covered by the compile).
|
|
599
|
+
if (MTIME_SOURCE_REASON_CODES.has(code))
|
|
600
|
+
continue;
|
|
570
601
|
const file = typeof reason.path === "string" ? reason.path : "";
|
|
571
602
|
actions.push(migrationAction({
|
|
572
603
|
id: `freshness-${code}-${file}`,
|
|
@@ -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,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-relationship-parity.d.ts","sourceRoot":"","sources":["../../../src/public/operations/source-relationship-parity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"source-relationship-parity.d.ts","sourceRoot":"","sources":["../../../src/public/operations/source-relationship-parity.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACpC,CAAC,CAAC;AAaH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,SAAS,wBAAwB,EAAE,EAC7C,QAAQ,EAAE,SAAS,wBAAwB,EAAE,GAC5C,SAAS,EAAE,CAwDb;AAuCD,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,wBAAwB,EAAE,CAmB5B;AA6BD,wBAAsB,yCAAyC,CAC7D,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,EAAE,CAAC,CA0DtB"}
|
|
@@ -2,6 +2,7 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { discoverSourceFiles } from "../../commands/sync/discovery.js";
|
|
4
4
|
import { processExtractions } from "../../commands/sync/extraction.js";
|
|
5
|
+
import { findUntrackedDocumentMatches } from "../../commands/sync/untracked-targets.js";
|
|
5
6
|
import { RELATIONSHIP_TYPES } from "../../operations/mutation/relationships.js";
|
|
6
7
|
import { parseListOfLists, parsePrologValue } from "../../prolog/codec.js";
|
|
7
8
|
import { readAllShards } from "../../relationships/shards.js";
|
|
@@ -173,5 +174,58 @@ export async function collectSourceRelationshipParityViolations(workspaceRoot, p
|
|
|
173
174
|
source: ".kb/relationships",
|
|
174
175
|
});
|
|
175
176
|
}
|
|
176
|
-
return compareRelationshipParity(authored, await compiledRelationships(prolog));
|
|
177
|
+
return explainUntrackedEndpoints(workspaceRoot, compareRelationshipParity(authored, await compiledRelationships(prolog)));
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* An authored relationship is missing from the compiled KB most often
|
|
181
|
+
* because an endpoint is declared in a file sync cannot see: one Git does not
|
|
182
|
+
* track and no pending-source receipt covers (for example a `.kb/symbols.yaml`
|
|
183
|
+
* that `kibi init` created and kb_upsert appended to before receipts covered
|
|
184
|
+
* existing untracked files). Name that file and the fix.
|
|
185
|
+
*/
|
|
186
|
+
// implements REQ-kibi-source-relationship-parity
|
|
187
|
+
function explainUntrackedEndpoints(workspaceRoot, violations) {
|
|
188
|
+
const endpoints = new Set();
|
|
189
|
+
for (const violation of violations) {
|
|
190
|
+
const relationship = authoredToCompiledRelationship(violation);
|
|
191
|
+
if (relationship === null)
|
|
192
|
+
continue;
|
|
193
|
+
endpoints.add(relationship.from);
|
|
194
|
+
endpoints.add(relationship.to);
|
|
195
|
+
}
|
|
196
|
+
if (endpoints.size === 0)
|
|
197
|
+
return violations;
|
|
198
|
+
const untracked = findUntrackedDocumentMatches(workspaceRoot, [...endpoints]);
|
|
199
|
+
if (untracked.size === 0)
|
|
200
|
+
return violations;
|
|
201
|
+
return violations.map((violation) => {
|
|
202
|
+
const relationship = authoredToCompiledRelationship(violation);
|
|
203
|
+
if (relationship === null)
|
|
204
|
+
return violation;
|
|
205
|
+
const sources = [relationship.from, relationship.to].flatMap((id) => {
|
|
206
|
+
const file = untracked.get(id);
|
|
207
|
+
return file === undefined ? [] : [{ entityId: id, file }];
|
|
208
|
+
});
|
|
209
|
+
if (sources.length === 0)
|
|
210
|
+
return violation;
|
|
211
|
+
const files = [...new Set(sources.map((source) => source.file))];
|
|
212
|
+
return {
|
|
213
|
+
...violation,
|
|
214
|
+
suggestion: `${sources.map((source) => source.entityId).join(" and ")} ${sources.length === 1 ? "is" : "are"} declared in ${files.join(", ")}, which Git does not track and no pending-source receipt covers, so sync never compiles it. Run 'git add ${files.join(" ")}' (staging is enough) or rewrite the entity with kb_upsert, then run kibi sync and rerun kb_check.`,
|
|
215
|
+
evidence: {
|
|
216
|
+
...(violation.evidence ?? {}),
|
|
217
|
+
untrackedSources: sources,
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function authoredToCompiledRelationship(violation) {
|
|
223
|
+
const evidence = violation.evidence;
|
|
224
|
+
if (evidence?.direction !== "authored_to_compiled")
|
|
225
|
+
return null;
|
|
226
|
+
const from = evidence.relationship?.from;
|
|
227
|
+
const to = evidence.relationship?.to;
|
|
228
|
+
return typeof from === "string" && typeof to === "string"
|
|
229
|
+
? { from, to }
|
|
230
|
+
: null;
|
|
177
231
|
}
|