kibi-cli 2.39.0 → 2.40.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.js +2 -2
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +33 -9
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +15 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +4 -0
- package/dist/commands/migrate-plan-record.d.ts +18 -0
- package/dist/commands/migrate-plan-record.d.ts.map +1 -0
- package/dist/commands/migrate-plan-record.js +112 -0
- package/dist/commands/migrate.d.ts +11 -2
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +53 -7
- package/dist/commands/prove.d.ts.map +1 -1
- package/dist/commands/prove.js +18 -3
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +17 -4
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +8 -0
- package/dist/commands/telemetry.d.ts.map +1 -1
- package/dist/commands/telemetry.js +12 -2
- package/dist/extractors/symbol-title-match.d.ts +31 -0
- package/dist/extractors/symbol-title-match.d.ts.map +1 -0
- package/dist/extractors/symbol-title-match.js +133 -0
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +81 -12
- package/dist/operations/bootstrap/discovery-evidence.js +2 -2
- package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance-actions.js +12 -1
- package/dist/operations/bootstrap/plugin-offers.d.ts +7 -1
- package/dist/operations/bootstrap/plugin-offers.d.ts.map +1 -1
- package/dist/operations/bootstrap/plugin-offers.js +73 -3
- package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
- package/dist/operations/modeling/logic-modeling.js +2 -1
- package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
- package/dist/operations/modeling/model-requirement.js +2 -1
- package/dist/operations/mutation/symbol-refresh.d.ts +2 -0
- package/dist/operations/mutation/symbol-refresh.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-refresh.js +6 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +7 -0
- package/dist/operations/planning/compile-intent.d.ts.map +1 -1
- package/dist/operations/planning/compile-intent.js +2 -1
- package/dist/operations/proof/claim-bindings.d.ts +11 -0
- package/dist/operations/proof/claim-bindings.d.ts.map +1 -1
- package/dist/operations/proof/claim-bindings.js +144 -1
- package/dist/operations/proof/code-certificates.d.ts +10 -3
- package/dist/operations/proof/code-certificates.d.ts.map +1 -1
- package/dist/operations/proof/code-certificates.js +65 -13
- package/dist/operations/proof/code-languages.d.ts +104 -0
- package/dist/operations/proof/code-languages.d.ts.map +1 -0
- package/dist/operations/proof/code-languages.js +119 -0
- package/dist/operations/proof/code-models.d.ts +2 -0
- package/dist/operations/proof/code-models.d.ts.map +1 -1
- package/dist/operations/proof/code-models.js +11 -5
- package/dist/operations/proof/code-runner.d.ts +12 -9
- package/dist/operations/proof/code-runner.d.ts.map +1 -1
- package/dist/operations/proof/code-runner.js +81 -38
- package/dist/operations/proof/code-scope.d.ts +8 -0
- package/dist/operations/proof/code-scope.d.ts.map +1 -1
- package/dist/operations/proof/code-scope.js +30 -1
- package/dist/operations/proof/explain.d.ts +13 -0
- package/dist/operations/proof/explain.d.ts.map +1 -1
- package/dist/operations/proof/explain.js +66 -0
- package/dist/operations/proof/ingest-proof.d.ts +2 -0
- package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
- package/dist/operations/proof/ingest-proof.js +1 -0
- package/dist/operations/proof/java-package-shadow.d.ts +14 -0
- package/dist/operations/proof/java-package-shadow.d.ts.map +1 -0
- package/dist/operations/proof/java-package-shadow.js +389 -0
- package/dist/operations/proof/prolog-evaluator.d.ts +14 -0
- package/dist/operations/proof/prolog-evaluator.d.ts.map +1 -0
- package/dist/operations/proof/prolog-evaluator.js +247 -0
- package/dist/operations/proof/prolog-source-reader.d.ts +20 -0
- package/dist/operations/proof/prolog-source-reader.d.ts.map +1 -0
- package/dist/operations/proof/prolog-source-reader.js +1123 -0
- package/dist/operations/proof/source-code-readers.d.ts +6 -0
- package/dist/operations/proof/source-code-readers.d.ts.map +1 -1
- package/dist/operations/proof/source-code-readers.js +879 -75
- package/dist/plugins/approved-source-analyzers.generated.d.ts +13 -35
- package/dist/plugins/approved-source-analyzers.generated.d.ts.map +1 -1
- package/dist/plugins/approved-source-analyzers.generated.js +13 -35
- package/dist/plugins/plugin-install-check.d.ts +21 -0
- package/dist/plugins/plugin-install-check.d.ts.map +1 -0
- package/dist/plugins/plugin-install-check.js +62 -0
- package/dist/plugins/proof-reference-extractors.d.ts +20 -0
- package/dist/plugins/proof-reference-extractors.d.ts.map +1 -0
- package/dist/plugins/proof-reference-extractors.js +104 -0
- package/dist/plugins/reference-language-suggestion.d.ts +51 -0
- package/dist/plugins/reference-language-suggestion.d.ts.map +1 -0
- package/dist/plugins/reference-language-suggestion.js +277 -0
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +3 -1
- package/dist/plugins/source-analysis-service.d.ts.map +1 -1
- package/dist/plugins/source-analysis-service.js +4 -0
- package/dist/plugins/source-change-analysis.d.ts +26 -1
- package/dist/plugins/source-change-analysis.d.ts.map +1 -1
- package/dist/plugins/source-change-analysis.js +77 -5
- package/dist/plugins/source-classification.d.ts.map +1 -1
- package/dist/plugins/source-classification.js +23 -3
- package/dist/public/operations/catalog.d.ts +32 -7
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +3 -0
- package/dist/public/operations/evidence-floor.d.ts +8 -0
- package/dist/public/operations/evidence-floor.d.ts.map +1 -1
- package/dist/public/operations/evidence-floor.js +19 -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/mutation-schemas.d.ts +31 -6
- package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.d.ts +8 -0
- package/dist/public/operations/specs/reporting.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.js +26 -3
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +2 -1
- package/dist/public/operations/specs/upsert.d.ts +31 -6
- package/dist/public/operations/specs/upsert.d.ts.map +1 -1
- package/dist/public/operations/symbol-coordinates-check.d.ts +11 -0
- package/dist/public/operations/symbol-coordinates-check.d.ts.map +1 -0
- package/dist/public/operations/symbol-coordinates-check.js +66 -0
- package/dist/public/proof-protocol.d.ts +31 -6
- package/dist/public/proof-protocol.d.ts.map +1 -1
- package/dist/public/proof-protocol.js +45 -15
- package/dist/public/proof-sources.d.ts +4 -2
- package/dist/public/proof-sources.d.ts.map +1 -1
- package/dist/public/proof-sources.js +43 -4
- package/dist/public/skills/kibi-usage/SKILL.md +6 -3
- package/dist/public/skills/kibi-usage/resources/prolog-proofs.md +207 -0
- package/dist/public/skills/kibi-usage/resources/proof.md +44 -3
- package/dist/schemas/entity.schema.json +34 -6
- package/dist/traceability/analysis-gate.d.ts +3 -1
- package/dist/traceability/analysis-gate.d.ts.map +1 -1
- package/dist/traceability/analysis-gate.js +9 -1
- package/dist/traceability/staged-file-coverage.d.ts.map +1 -1
- package/dist/traceability/staged-file-coverage.js +7 -2
- package/dist/traceability/symbol-extract.d.ts +17 -0
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +69 -15
- package/dist/traceability/validate.d.ts.map +1 -1
- package/dist/traceability/validate.js +5 -7
- package/dist/utils/first-sentence.d.ts +9 -0
- package/dist/utils/first-sentence.d.ts.map +1 -0
- package/dist/utils/first-sentence.js +52 -0
- package/dist/utils/rule-registry.generated.d.ts +6 -0
- package/dist/utils/rule-registry.generated.d.ts.map +1 -1
- package/dist/utils/rule-registry.generated.js +7 -0
- package/package.json +5 -5
- package/src/public/operations/check-executor.ts +8 -0
- package/src/public/operations/evidence-floor.ts +29 -1
- package/src/public/operations/specs/check-rules.generated.ts +2 -1
- package/src/public/operations/specs/reporting.ts +38 -3
- package/src/public/operations/specs/semantic.ts +2 -1
- package/src/public/operations/symbol-coordinates-check.ts +79 -0
- package/src/public/proof-protocol-types.d.ts +22 -5
- package/src/public/proof-protocol.ts +78 -19
- package/src/public/proof-sources.ts +51 -3
- package/src/public/skills/kibi-usage/SKILL.md +6 -3
- package/src/public/skills/kibi-usage/resources/prolog-proofs.md +207 -0
- package/src/public/skills/kibi-usage/resources/proof.md +44 -3
- package/src/schemas/entity.schema.json +34 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,IAAI,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,IAAI,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAMvF,OAAO,EAEL,oBAAoB,EACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,KAAK,UAAU,EACf,KAAK,UAAU,EAEhB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,KAAK,oBAAoB,GAAG,uBAAuB,GAAG;IACpD,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;CACtD,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAU7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAE9D,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG5E;AAuND,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C;;;OAGG;IACH,WAAW,CAAC,EAAE,CACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,KACZ,oBAAoB,GAAG,IAAI,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;CACzE,CAAC,CAAC;AAWH,wBAAgB,4BAA4B,CAE1C,UAAU,EAAE,UAAU,EACtB,cAAc,CAAC,EAAE,cAAc,EAC/B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,eAAe,EAAE,CAkCnB;AAED;;;GAGG;AAEH,wBAAsB,iCAAiC,CACrD,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,OAAO,EAAE,qBAAqB,GAC5B,QAAQ,CAAC;IACP,QAAQ,CAAC,EAAE,OAAO,wBAAwB,EAAE,kBAAkB,CAAC;CAChE,CAAC,GACH,OAAO,CAAC,eAAe,EAAE,CAAC,CA4B5B;AAED,KAAK,aAAa,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;;GAGG;AAEH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,eAAe,CAAC,EAAE,SAAS,CAAC,aAAa,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;CACnE,GAAG,OAAO,CAeV;AA4DD,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readManifestWithCoordinateOverlay } from "../extractors/manifest.js";
|
|
3
|
+
import { declarationTitleAliases, matchDeclarationTitle, } from "../extractors/symbol-title-match.js";
|
|
3
4
|
import { createTsMorphSourceAnalysisProvider, isPrivateClassMember, } from "../extractors/symbols-ts.js";
|
|
4
5
|
import { inferSymbolRole, } from "../public/symbol-granularity.js";
|
|
6
|
+
import { analysisObligation } from "./analysis-gate.js";
|
|
5
7
|
const TRACEABILITY_RELATIONSHIP_TYPES = new Set([
|
|
6
8
|
"implements",
|
|
7
9
|
"covered_by",
|
|
@@ -103,7 +105,7 @@ function extractRelationshipsFromManifestRecord(record) {
|
|
|
103
105
|
}
|
|
104
106
|
return relationships;
|
|
105
107
|
}
|
|
106
|
-
function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifestLookup) {
|
|
108
|
+
function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifestLookup, nestedSpans = []) {
|
|
107
109
|
const { id, relationships } = resolveSymbolTraceability(stagedFile.path, name, manifestLookup);
|
|
108
110
|
const manifestReqLinks = getRequirementLinks(relationships);
|
|
109
111
|
const mergedReqLinks = inlineReqLinks.length > 0 ? inlineReqLinks : manifestReqLinks;
|
|
@@ -117,7 +119,7 @@ function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifes
|
|
|
117
119
|
startLine: span.startLine,
|
|
118
120
|
endLine: span.endLine,
|
|
119
121
|
},
|
|
120
|
-
hunkRanges:
|
|
122
|
+
hunkRanges: ownedHunks(span, nestedSpans, stagedFile.hunkRanges),
|
|
121
123
|
reqLinks: mergedReqLinks,
|
|
122
124
|
...(relationships !== undefined ? { relationships } : {}),
|
|
123
125
|
};
|
|
@@ -215,11 +217,33 @@ export async function extractSymbolsFromStagedFileAsync(stagedFile, manifestLook
|
|
|
215
217
|
else {
|
|
216
218
|
analysis = analyzeWithBuiltinFallback(stagedFile.path, content);
|
|
217
219
|
}
|
|
220
|
+
// A local gap (a macro, an include, a dynamic definition) does not hide the
|
|
221
|
+
// declarations the parser placed, so those are validated; a changed line
|
|
222
|
+
// that no placed declaration contains is reviewed at file level instead.
|
|
223
|
+
if (analysis && partialAnalysisSymbolsUsable(analysis))
|
|
224
|
+
return symbolsFromAnalysis(stagedFile, analysis, manifestLookup);
|
|
218
225
|
if (analysis?.status === "failed" || analysis?.status === "partial") {
|
|
219
226
|
throw new Error(`Source analysis ${analysis.status} for ${stagedFile.path}; complete symbol validation is unavailable`);
|
|
220
227
|
}
|
|
221
228
|
return symbolsFromAnalysis(stagedFile, analysis, manifestLookup);
|
|
222
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Whether a partial result still places its declarations: every gap is local
|
|
232
|
+
* (no parser, timeout or integrity finding) and no output cap hid symbols.
|
|
233
|
+
*/
|
|
234
|
+
// implements REQ-cli-staged-impact-enforcement
|
|
235
|
+
export function partialAnalysisSymbolsUsable(analysis) {
|
|
236
|
+
if (analysis.status !== "partial")
|
|
237
|
+
return false;
|
|
238
|
+
const codes = (analysis.diagnostics ?? []).map((item) => item.code);
|
|
239
|
+
if (codes.some((code) => /LIMIT/i.test(code)))
|
|
240
|
+
return false;
|
|
241
|
+
return (analysisObligation("after", {
|
|
242
|
+
status: "partial",
|
|
243
|
+
diagnosticCodes: codes,
|
|
244
|
+
uncoveredRanges: analysis.uncoveredRanges ?? [],
|
|
245
|
+
}, []).kind !== "blocked");
|
|
246
|
+
}
|
|
223
247
|
function symbolsFromAnalysis(stagedFile, analysis, manifestLookup) {
|
|
224
248
|
if (!analysis)
|
|
225
249
|
return [];
|
|
@@ -234,8 +258,9 @@ function symbolsFromAnalysis(stagedFile, analysis, manifestLookup) {
|
|
|
234
258
|
}
|
|
235
259
|
const results = [];
|
|
236
260
|
for (const symbol of analysis.symbols) {
|
|
261
|
+
const nested = nestedSpans(symbol, analysis.symbols);
|
|
237
262
|
try {
|
|
238
|
-
results.push(buildSymbolResult(stagedFile, sourceSymbolLocator(symbol, analysis.symbols, stagedFile.path, manifestLookup), symbol.kind, { startLine: symbol.startLine, endLine: symbol.endLine }, parseReqDirectives(symbol.directiveText ?? ""), manifestLookup));
|
|
263
|
+
results.push(buildSymbolResult(stagedFile, sourceSymbolLocator(symbol, analysis.symbols, stagedFile.path, manifestLookup), symbol.kind, { startLine: symbol.startLine, endLine: symbol.endLine }, parseReqDirectives(symbol.directiveText ?? ""), manifestLookup, nested));
|
|
239
264
|
}
|
|
240
265
|
catch (error) {
|
|
241
266
|
if (analysis.status !== undefined)
|
|
@@ -252,13 +277,37 @@ function symbolsFromAnalysis(stagedFile, analysis, manifestLookup) {
|
|
|
252
277
|
return results;
|
|
253
278
|
}
|
|
254
279
|
export { isPrivateClassMember };
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
280
|
+
/** Spans of the other declarations strictly inside a declaration's span. */
|
|
281
|
+
function nestedSpans(symbol, symbols) {
|
|
282
|
+
return symbols
|
|
283
|
+
.filter((other) => other !== symbol &&
|
|
284
|
+
symbol.startLine <= other.startLine &&
|
|
285
|
+
other.endLine <= symbol.endLine &&
|
|
286
|
+
(other.startLine !== symbol.startLine ||
|
|
287
|
+
other.endLine !== symbol.endLine))
|
|
288
|
+
.sort((left, right) => left.startLine - right.startLine);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Hunks with a changed line inside the span that no nested declaration
|
|
292
|
+
* contains: a line inside a member changes the member, not its class.
|
|
293
|
+
*/
|
|
294
|
+
function ownedHunks(span, nested, hunks) {
|
|
295
|
+
return hunks.filter((hunk) => {
|
|
296
|
+
if (!rangesIntersect(span.startLine, span.endLine, hunk.start, hunk.end))
|
|
297
|
+
return false;
|
|
298
|
+
const end = Math.min(span.endLine, hunk.end);
|
|
299
|
+
let line = Math.max(span.startLine, hunk.start);
|
|
300
|
+
for (const child of nested) {
|
|
301
|
+
if (child.endLine < line)
|
|
302
|
+
continue;
|
|
303
|
+
if (child.startLine > line)
|
|
304
|
+
return true;
|
|
305
|
+
line = child.endLine + 1;
|
|
306
|
+
if (line > end)
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
return line <= end;
|
|
310
|
+
});
|
|
262
311
|
}
|
|
263
312
|
// implements REQ-cli-staged-impact-enforcement
|
|
264
313
|
function sourceSymbolLocator(symbol, symbols, filePath, lookup) {
|
|
@@ -267,10 +316,15 @@ function sourceSymbolLocator(symbol, symbols, filePath, lookup) {
|
|
|
267
316
|
return symbol.name;
|
|
268
317
|
if (lookup?.has(`${filePath}:${qualified}`))
|
|
269
318
|
return qualified;
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
319
|
+
// Another title sync places on this declaration alone: a Terraform
|
|
320
|
+
// address, a bare name no other declaration shares, a class named like
|
|
321
|
+
// its constructors.
|
|
322
|
+
for (const title of [...declarationTitleAliases(symbol), symbol.name]) {
|
|
323
|
+
if (!lookup?.has(`${filePath}:${title}`))
|
|
324
|
+
continue;
|
|
325
|
+
const match = matchDeclarationTitle(title, symbols, filePath);
|
|
326
|
+
if ("symbol" in match && match.symbol === symbol)
|
|
327
|
+
return title;
|
|
328
|
+
}
|
|
275
329
|
return qualified;
|
|
276
330
|
}
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,CAoBhE;AAGD,eAAO,MAAM,QAAQ;;;;;CAKpB,CAAC"}
|
|
@@ -188,15 +188,13 @@ export function formatViolations(violations) {
|
|
|
188
188
|
return "";
|
|
189
189
|
const total = violations.length;
|
|
190
190
|
const minLinks = violations[0]?.requiredLinks ?? 0;
|
|
191
|
-
const lines = [
|
|
192
|
-
|
|
191
|
+
const lines = [
|
|
192
|
+
`Traceability failed: ${total} changed symbol(s) have fewer than ${minLinks} requirement link(s) (minLinks=${minLinks}):`,
|
|
193
|
+
];
|
|
193
194
|
for (const v of violations) {
|
|
194
|
-
|
|
195
|
-
const name = `${v.name}()`;
|
|
196
|
-
// Suggest adding requirement links with role-specific guidance
|
|
197
|
-
const suggestion = "Add ownership: implements: REQ-<area>-<behavior> (production code), use covered_by for production coverage, or executable_for for executable test code";
|
|
198
|
-
lines.push(`${loc} ${name} -> ${suggestion}`);
|
|
195
|
+
lines.push(` ${v.file}:${v.line} ${v.name} (${v.currentLinks} of ${v.requiredLinks} link(s))`);
|
|
199
196
|
}
|
|
197
|
+
lines.push("To link a symbol, give it an owning requirement, stage the change and re-run kibi check --staged:", ` - Add a .kb/symbols.yaml entry with kb_upsert (CLI: kibi upsert --input -): title as listed above (e.g. ${JSON.stringify(violations[0]?.name ?? "")}), sourceFile, and the relationship implements -> REQ-<area>-<behavior> for production code or executable_for -> TEST-<area>-<behavior> for executable test code. Stage .kb/symbols.yaml with the code.`, " - Or write a comment `implements REQ-<area>-<behavior>` directly above the declaration.", " If no requirement fits, find one with kb_search or create it with kb_upsert (type req) first; production symbols also need covered_by -> TEST-<area>-<behavior>.");
|
|
200
198
|
return lines.join("\n");
|
|
201
199
|
}
|
|
202
200
|
// Test helpers for edge-case coverage
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first sentence of prose, used to title a requirement. A sentence ends
|
|
3
|
+
* at `.`, `!` or `?` followed by whitespace and an uppercase letter, or at
|
|
4
|
+
* the end of the text, so the dot in a path (`src/match.c`), a dotted name
|
|
5
|
+
* (`console.log`, `exec.Command`), a version (`v1.2`), an abbreviation or
|
|
6
|
+
* backticks never cuts it. A closing dot is dropped.
|
|
7
|
+
*/
|
|
8
|
+
export declare function firstSentence(text: string): string;
|
|
9
|
+
//# sourceMappingURL=first-sentence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"first-sentence.d.ts","sourceRoot":"","sources":["../../src/utils/first-sentence.ts"],"names":[],"mappings":"AAyBA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmBlD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
/** Abbreviations whose dot does not end a sentence. */
|
|
19
|
+
const ABBREVIATION = /(?:^|[\s(])(?:e\.g|i\.e|etc|vs|cf|approx)\.$/i;
|
|
20
|
+
/** After a sentence end: whitespace, then an uppercase letter (maybe quoted). */
|
|
21
|
+
const NEXT_SENTENCE = /^\s+["'(“]?\p{Lu}/u;
|
|
22
|
+
// implements REQ-mcp-semantic-advisor-preflight
|
|
23
|
+
/**
|
|
24
|
+
* The first sentence of prose, used to title a requirement. A sentence ends
|
|
25
|
+
* at `.`, `!` or `?` followed by whitespace and an uppercase letter, or at
|
|
26
|
+
* the end of the text, so the dot in a path (`src/match.c`), a dotted name
|
|
27
|
+
* (`console.log`, `exec.Command`), a version (`v1.2`), an abbreviation or
|
|
28
|
+
* backticks never cuts it. A closing dot is dropped.
|
|
29
|
+
*/
|
|
30
|
+
export function firstSentence(text) {
|
|
31
|
+
const value = text.trim();
|
|
32
|
+
let quoted = false;
|
|
33
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
34
|
+
const char = value[index] ?? "";
|
|
35
|
+
if (char === "`") {
|
|
36
|
+
quoted = !quoted;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (quoted || (char !== "." && char !== "!" && char !== "?"))
|
|
40
|
+
continue;
|
|
41
|
+
const rest = value.slice(index + 1);
|
|
42
|
+
const atEnd = rest.trim() === "";
|
|
43
|
+
if (!atEnd && !NEXT_SENTENCE.test(rest))
|
|
44
|
+
continue;
|
|
45
|
+
if (char === "." && ABBREVIATION.test(value.slice(0, index + 1)))
|
|
46
|
+
continue;
|
|
47
|
+
return (char === "." ? value.slice(0, index) : value.slice(0, index + 1))
|
|
48
|
+
.replace(/\.+$/, "")
|
|
49
|
+
.trim();
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
@@ -18,6 +18,12 @@ export declare const GENERATED_RULES: readonly [{
|
|
|
18
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
19
|
readonly enforcementClass: "canonical";
|
|
20
20
|
readonly category: "traceability";
|
|
21
|
+
}, {
|
|
22
|
+
readonly name: "symbol-coordinates-missing";
|
|
23
|
+
readonly description: "Advisory: an active symbol names a source file that exists, but Kibi could not place it there (the declaration was not found, the file is unreadable to the analyzer, or a declared plugin or grammar pack is missing), so its link is checked against no code";
|
|
24
|
+
readonly enforcementClass: "advisory";
|
|
25
|
+
readonly category: "traceability";
|
|
26
|
+
readonly diagnosticSeverity: "warning";
|
|
21
27
|
}, {
|
|
22
28
|
readonly name: "no-dangling-refs";
|
|
23
29
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkalB,CAAC"}
|
|
@@ -32,6 +32,13 @@ export const GENERATED_RULES = [
|
|
|
32
32
|
enforcementClass: "canonical",
|
|
33
33
|
category: "traceability",
|
|
34
34
|
},
|
|
35
|
+
{
|
|
36
|
+
name: "symbol-coordinates-missing",
|
|
37
|
+
description: "Advisory: an active symbol names a source file that exists, but Kibi could not place it there (the declaration was not found, the file is unreadable to the analyzer, or a declared plugin or grammar pack is missing), so its link is checked against no code",
|
|
38
|
+
enforcementClass: "advisory",
|
|
39
|
+
category: "traceability",
|
|
40
|
+
diagnosticSeverity: "warning",
|
|
41
|
+
},
|
|
35
42
|
{
|
|
36
43
|
name: "no-dangling-refs",
|
|
37
44
|
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.40.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -239,14 +239,14 @@
|
|
|
239
239
|
"fast-glob": "^3.2.12",
|
|
240
240
|
"gray-matter": "^4.0.3",
|
|
241
241
|
"js-yaml": "^4.1.0",
|
|
242
|
-
"kibi-core": "^0.
|
|
243
|
-
"kibi-plugin-builtin": "^0.5.
|
|
244
|
-
"kibi-plugin-sdk": "^0.
|
|
242
|
+
"kibi-core": "^0.29.0",
|
|
243
|
+
"kibi-plugin-builtin": "^0.5.4",
|
|
244
|
+
"kibi-plugin-sdk": "^0.6.0",
|
|
245
245
|
"kibi-swipl": "^1.0.1",
|
|
246
246
|
"saxes": "^6.0.0",
|
|
247
247
|
"ignore": "^5.3.0",
|
|
248
248
|
"yaml": "^2.9.1",
|
|
249
|
-
"kibi-agent-core": "^0.11.
|
|
249
|
+
"kibi-agent-core": "^0.11.1"
|
|
250
250
|
},
|
|
251
251
|
"devDependencies": {
|
|
252
252
|
"@types/node": "latest",
|
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
collectSourcePathDanglingViolations,
|
|
62
62
|
} from "./source-path-dangling.js";
|
|
63
63
|
import { collectSourceRelationshipParityViolations } from "./source-relationship-parity.js";
|
|
64
|
+
import { collectSymbolCoordinatesMissingViolations } from "./symbol-coordinates-check.js";
|
|
64
65
|
import { collectSymbolGranularityViolations } from "./symbol-granularity-check.js";
|
|
65
66
|
import type { OperationResult } from "./types.js";
|
|
66
67
|
import { readWorkspaceSnapshot } from "./workspace-snapshot.js";
|
|
@@ -414,6 +415,12 @@ export async function executeCheck(
|
|
|
414
415
|
rulesAllowlist,
|
|
415
416
|
analysisCacheDir(context, workspaceRoot, options),
|
|
416
417
|
);
|
|
418
|
+
const symbolCoordinateFindings =
|
|
419
|
+
await collectSymbolCoordinatesMissingViolations(
|
|
420
|
+
prolog,
|
|
421
|
+
workspaceRoot,
|
|
422
|
+
rulesAllowlist,
|
|
423
|
+
);
|
|
417
424
|
const originReviewFindings = await collectOriginReviewViolations(
|
|
418
425
|
prolog,
|
|
419
426
|
rulesAllowlist,
|
|
@@ -449,6 +456,7 @@ export async function executeCheck(
|
|
|
449
456
|
...sourceRelationshipParityViolations,
|
|
450
457
|
...sourcePathFindings,
|
|
451
458
|
...symbolGranularityFindings,
|
|
459
|
+
...symbolCoordinateFindings,
|
|
452
460
|
...predicateConformanceFindings,
|
|
453
461
|
...originReviewFindings,
|
|
454
462
|
...entityContextFindings,
|
|
@@ -46,6 +46,12 @@ export type EvidenceFloorRow = Readonly<{
|
|
|
46
46
|
proofGaps: readonly string[];
|
|
47
47
|
/** The tests and symbols behind the gaps; empty lists are left out. */
|
|
48
48
|
gapDetails?: Readonly<Record<string, readonly string[]>>;
|
|
49
|
+
/** Claims a proof refuted: the test, the claim and what the test read. */
|
|
50
|
+
refutations?: readonly Readonly<{
|
|
51
|
+
test: string;
|
|
52
|
+
claimKey: string;
|
|
53
|
+
reason: string;
|
|
54
|
+
}>[];
|
|
49
55
|
}>;
|
|
50
56
|
|
|
51
57
|
type EvidenceFloorPayload = {
|
|
@@ -100,7 +106,28 @@ export function gapDetailLines(row: EvidenceFloorRow): string[] {
|
|
|
100
106
|
);
|
|
101
107
|
}
|
|
102
108
|
|
|
109
|
+
/** Gaps a refuted claim leaves that a derived proof would have covered. */
|
|
110
|
+
const REFUTATION_GAPS = new Set(["happy_path_only", "uncovered_case"]);
|
|
111
|
+
|
|
112
|
+
/** Each refuted claim in plain words: which test refutes it and why. */
|
|
113
|
+
// implements REQ-kibi-claim-bound-proofs
|
|
114
|
+
export function refutationText(row: EvidenceFloorRow): string | undefined {
|
|
115
|
+
const refutations = row.refutations ?? [];
|
|
116
|
+
if (refutations.length === 0) return undefined;
|
|
117
|
+
return refutations
|
|
118
|
+
.map(
|
|
119
|
+
(refutation) =>
|
|
120
|
+
`${refutation.test} refutes ${refutation.claimKey}: ${refutation.reason}`,
|
|
121
|
+
)
|
|
122
|
+
.join("; ");
|
|
123
|
+
}
|
|
124
|
+
|
|
103
125
|
function suggestionFor(row: EvidenceFloorRow): string {
|
|
126
|
+
const refuted = refutationText(row);
|
|
127
|
+
if (refuted !== undefined) {
|
|
128
|
+
const following = row.proofGaps.filter((gap) => REFUTATION_GAPS.has(gap));
|
|
129
|
+
return `The code breaks a claim of ${row.id} (${refuted}). Fix the code so it keeps the claim, or, with the person who owns the rule, change the requirement, then run kibi prove${following.length > 0 ? `; ${following.join(" and ")} ${following.length > 1 ? "clear" : "clears"} once the proof derives the claim` : ""}. kibi proof explain ${row.id} shows each claim's bindings.`;
|
|
130
|
+
}
|
|
104
131
|
if (row.status === "pending_reproof")
|
|
105
132
|
return `Run kibi prove for the tests behind ${row.id} so its receipts match the current code, then rerun kibi check --fresh-evidence.`;
|
|
106
133
|
switch (row.requiredEvidence) {
|
|
@@ -127,7 +154,7 @@ export function evidenceFloorViolations(
|
|
|
127
154
|
description:
|
|
128
155
|
row.status === "pending_reproof"
|
|
129
156
|
? `${row.id} is ${row.evidenceLevel} but needs ${row.requiredEvidence} (${floorSource(row)}); its receipts are older than the code`
|
|
130
|
-
: `${row.id} is ${row.evidenceLevel} but needs ${row.requiredEvidence} (${floorSource(row)})`,
|
|
157
|
+
: `${row.id} is ${row.evidenceLevel} but needs ${row.requiredEvidence} (${floorSource(row)})${refutationText(row) === undefined ? "" : `; its proof refutes a claim: ${refutationText(row)}`}`,
|
|
131
158
|
suggestion: suggestionFor(row),
|
|
132
159
|
evidence: {
|
|
133
160
|
evidenceLevel: row.evidenceLevel,
|
|
@@ -137,6 +164,7 @@ export function evidenceFloorViolations(
|
|
|
137
164
|
proofStatus: row.proofStatus,
|
|
138
165
|
proofGaps: row.proofGaps,
|
|
139
166
|
...(row.gapDetails === undefined ? {} : { gapDetails: row.gapDetails }),
|
|
167
|
+
...(row.refutations?.length ? { refutations: row.refutations } : {}),
|
|
140
168
|
},
|
|
141
169
|
}));
|
|
142
170
|
}
|
|
@@ -14,6 +14,7 @@ export const SELECTABLE_RULE_NAMES: readonly string[] = [
|
|
|
14
14
|
"symbol-coverage",
|
|
15
15
|
"symbol-traceability",
|
|
16
16
|
"symbol-granularity",
|
|
17
|
+
"symbol-coordinates-missing",
|
|
17
18
|
"no-dangling-refs",
|
|
18
19
|
"source-relationship-parity",
|
|
19
20
|
"branch-store-not-compiled",
|
|
@@ -73,4 +74,4 @@ export const SELECTABLE_RULE_NAMES: readonly string[] = [
|
|
|
73
74
|
// implements REQ-006
|
|
74
75
|
/** Comma-separated "Allowed:" fragment for the kb_check rules description. */
|
|
75
76
|
export const SELECTABLE_RULES_ALLOWED =
|
|
76
|
-
"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, scenario-outcome-mismatch, 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, decision-firm-edited, supersedes-reason-missing, supersedes-reason-acknowledged, policy-ownership, policy-markers, kb-consistency, kb-consistency-unresolved, evidence-floor, proof-contract-claims";
|
|
77
|
+
"must-priority-coverage, symbol-coverage, symbol-traceability, symbol-granularity, symbol-coordinates-missing, 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, scenario-outcome-mismatch, 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, decision-firm-edited, supersedes-reason-missing, supersedes-reason-acknowledged, policy-ownership, policy-markers, kb-consistency, kb-consistency-unresolved, evidence-floor, proof-contract-claims";
|
|
@@ -37,6 +37,32 @@ const ENTITY_TYPES = [
|
|
|
37
37
|
] as const;
|
|
38
38
|
const ENTITY_TYPE_SET: ReadonlySet<string> = new Set(ENTITY_TYPES);
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* The engine names the attached store by its directory key (a SHA-256 of the
|
|
42
|
+
* branch name); meta.branch carries the branch name, as kb_status does, and
|
|
43
|
+
* the key moves to meta.branchStoreKey.
|
|
44
|
+
*/
|
|
45
|
+
// implements REQ-kibi-verification-evidence-contract
|
|
46
|
+
export function withBranchName<
|
|
47
|
+
T extends { readonly meta?: Readonly<Record<string, unknown>> },
|
|
48
|
+
>(payload: T, context: Pick<OperationContext, "branchAttachment">): T {
|
|
49
|
+
const meta = payload.meta;
|
|
50
|
+
const name = context.branchAttachment?.kbBranch;
|
|
51
|
+
if (meta === undefined || name === undefined || meta.branch === name)
|
|
52
|
+
return payload;
|
|
53
|
+
const key = typeof meta.branch === "string" ? meta.branch : undefined;
|
|
54
|
+
return {
|
|
55
|
+
...payload,
|
|
56
|
+
meta: {
|
|
57
|
+
...meta,
|
|
58
|
+
branch: name,
|
|
59
|
+
...(key !== undefined && key !== "unknown"
|
|
60
|
+
? { branchStoreKey: key }
|
|
61
|
+
: {}),
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
40
66
|
export type FindGapsInput = {
|
|
41
67
|
readonly type?: string;
|
|
42
68
|
readonly missingRelationships?: readonly string[];
|
|
@@ -161,7 +187,10 @@ export async function executeFindGaps(
|
|
|
161
187
|
: `Found ${payload.count ?? rows.length} gap rows. Showing ${rows.length}: ${rows.map((row) => row.id).join(", ")}.${stubNote}`,
|
|
162
188
|
},
|
|
163
189
|
],
|
|
164
|
-
structuredContent:
|
|
190
|
+
structuredContent: withBranchName(
|
|
191
|
+
confirmMetaFreshness(payload, context.workspaceRoot),
|
|
192
|
+
context,
|
|
193
|
+
),
|
|
165
194
|
};
|
|
166
195
|
} catch (error) {
|
|
167
196
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -418,7 +447,10 @@ export async function executeCoverage(
|
|
|
418
447
|
const migrationPlan = statusPlan
|
|
419
448
|
? mergeMigrationPlans([statusPlan, coveragePlan])
|
|
420
449
|
: coveragePlan;
|
|
421
|
-
const confirmed =
|
|
450
|
+
const confirmed = withBranchName(
|
|
451
|
+
confirmMetaFreshness(payload, context.workspaceRoot),
|
|
452
|
+
context,
|
|
453
|
+
);
|
|
422
454
|
const enrichedPayload = {
|
|
423
455
|
...confirmed,
|
|
424
456
|
rows,
|
|
@@ -581,7 +613,10 @@ export async function executeGraph(
|
|
|
581
613
|
: `Graph traversal returned ${nodes.length} nodes and ${edges.length} edges from ${input.seedIds.join(", ")}.`,
|
|
582
614
|
},
|
|
583
615
|
],
|
|
584
|
-
structuredContent:
|
|
616
|
+
structuredContent: withBranchName(
|
|
617
|
+
confirmMetaFreshness(payload, context.workspaceRoot),
|
|
618
|
+
context,
|
|
619
|
+
),
|
|
585
620
|
};
|
|
586
621
|
} catch (error) {
|
|
587
622
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
SemanticAdvisorReceipt,
|
|
8
8
|
} from "../../../operations/semantic-advisor/types.js";
|
|
9
9
|
import { publicCapabilityStamp } from "../../../plugins/compose-semantic-classifier.js";
|
|
10
|
+
import { firstSentence } from "../../../utils/first-sentence.js";
|
|
10
11
|
import type { OperationContext, PrologPort } from "../runtime-types.js";
|
|
11
12
|
import type { OperationSpec } from "../types.js";
|
|
12
13
|
|
|
@@ -59,7 +60,7 @@ export async function executeSemanticAdvisor(
|
|
|
59
60
|
): Promise<SemanticAdvisorOperationResult> {
|
|
60
61
|
const text = requiredText(args.text);
|
|
61
62
|
const id = optionalString(args.id) ?? "REQ-SEMANTIC-ADVISOR-PREVIEW";
|
|
62
|
-
const title = optionalString(args.title) ?? text
|
|
63
|
+
const title = optionalString(args.title) ?? (firstSentence(text) || text);
|
|
63
64
|
const source = optionalString(args.source) ?? "mcp://kibi/semantic-advisor";
|
|
64
65
|
const orchestrated = await analyzeSemanticAdvisorInputWithPlugins(
|
|
65
66
|
{
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync, statSync } from "node:fs";
|
|
20
|
+
import * as path from "node:path";
|
|
21
|
+
import { parseEntityFromList, parseListOfLists } from "../../prolog/codec.js";
|
|
22
|
+
import type { Violation } from "../../utils/rule-registry.js";
|
|
23
|
+
import type { PrologPort } from "./runtime-types.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A symbol whose source file exists but which Kibi could not place in it is
|
|
27
|
+
* linked to no code: impact review, coverage and proofs never see it there.
|
|
28
|
+
* kibi sync names it in its refresh output only, so the check reports every
|
|
29
|
+
* such symbol as a warning (advisory, non-blocking).
|
|
30
|
+
*/
|
|
31
|
+
// implements REQ-cli-check, REQ-generated-coordinate-persistence
|
|
32
|
+
export const SYMBOL_COORDINATES_MISSING_RULE = "symbol-coordinates-missing";
|
|
33
|
+
|
|
34
|
+
const SYMBOL_PLACEMENT_QUERY =
|
|
35
|
+
"findall([Id,symbol,Fields], (kb_entity(Id, symbol, Props), include([Key=_]>>memberchk(Key, [title,status,sourceFile,sourceLine]), Props, Fields)), Rows)";
|
|
36
|
+
|
|
37
|
+
const INACTIVE_STATUSES = new Set([
|
|
38
|
+
"deprecated",
|
|
39
|
+
"removed",
|
|
40
|
+
"retired",
|
|
41
|
+
"superseded",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
// implements REQ-cli-check
|
|
45
|
+
export async function collectSymbolCoordinatesMissingViolations(
|
|
46
|
+
prolog: Pick<PrologPort, "query">,
|
|
47
|
+
workspaceRoot: string,
|
|
48
|
+
rules: ReadonlySet<string>,
|
|
49
|
+
): Promise<Violation[]> {
|
|
50
|
+
if (!rules.has(SYMBOL_COORDINATES_MISSING_RULE)) return [];
|
|
51
|
+
const rows = await prolog.query(SYMBOL_PLACEMENT_QUERY);
|
|
52
|
+
if (!rows.success)
|
|
53
|
+
throw new Error(
|
|
54
|
+
`symbol-coordinates-missing symbol projection failed: ${rows.error ?? "unknown error"}`,
|
|
55
|
+
);
|
|
56
|
+
const violations: Violation[] = [];
|
|
57
|
+
for (const row of parseListOfLists(rows.bindings.Rows ?? "[]")) {
|
|
58
|
+
const entity = parseEntityFromList(row);
|
|
59
|
+
const id = typeof entity.id === "string" ? entity.id : "";
|
|
60
|
+
const sourceFile =
|
|
61
|
+
typeof entity.sourceFile === "string" ? entity.sourceFile : "";
|
|
62
|
+
const status = typeof entity.status === "string" ? entity.status : "";
|
|
63
|
+
if (id === "" || sourceFile === "" || INACTIVE_STATUSES.has(status))
|
|
64
|
+
continue;
|
|
65
|
+
if (entity.sourceLine !== undefined && entity.sourceLine !== null) continue;
|
|
66
|
+
const absolute = path.resolve(workspaceRoot, sourceFile);
|
|
67
|
+
if (!existsSync(absolute) || !statSync(absolute).isFile()) continue;
|
|
68
|
+
const title = typeof entity.title === "string" ? entity.title : id;
|
|
69
|
+
violations.push({
|
|
70
|
+
rule: SYMBOL_COORDINATES_MISSING_RULE,
|
|
71
|
+
entityId: id,
|
|
72
|
+
source: sourceFile,
|
|
73
|
+
description: `${id} (${title}) has no symbol coordinates in ${sourceFile}: Kibi could not place it, so its links are checked against no code`,
|
|
74
|
+
suggestion:
|
|
75
|
+
"Run kibi sync --refresh-symbol-coordinates and read its failed lines; use the name the extractor gives the declaration, install the plugins and grammar packs kibi doctor names, or add symbol_role and granularity_reason for a file-level link.",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return violations;
|
|
79
|
+
}
|
|
@@ -99,13 +99,21 @@ export type ProofContract = Readonly<{
|
|
|
99
99
|
| "state_machine"
|
|
100
100
|
| "regex"
|
|
101
101
|
| "source";
|
|
102
|
-
domain_source?: Readonly<{
|
|
102
|
+
domain_source?: Readonly<{
|
|
103
|
+
symbol_id: string;
|
|
104
|
+
export?: string;
|
|
105
|
+
field?: string;
|
|
106
|
+
/** A SWI-Prolog predicate's argument (1-based) that names each member. */
|
|
107
|
+
arg?: number;
|
|
108
|
+
}>;
|
|
103
109
|
witness?: Readonly<{ path: string }>;
|
|
104
110
|
constraint_source?: Readonly<{ fact_ids: readonly string[] }>;
|
|
105
111
|
value_source?: readonly (
|
|
106
112
|
| Readonly<{
|
|
107
113
|
symbol_id: string;
|
|
108
114
|
export?: string;
|
|
115
|
+
/** A SWI-Prolog predicate's argument (1-based) that holds the value. */
|
|
116
|
+
arg?: number;
|
|
109
117
|
/** value (default): the export is a number; count: the size of a list, set, map or object. */
|
|
110
118
|
measure?: "value" | "count";
|
|
111
119
|
}>
|
|
@@ -130,12 +138,20 @@ export type ProofContract = Readonly<{
|
|
|
130
138
|
domains?: Readonly<
|
|
131
139
|
Record<
|
|
132
140
|
string,
|
|
133
|
-
| Readonly<{ symbol_id: string; export?: string }>
|
|
141
|
+
| Readonly<{ symbol_id: string; export?: string; arg?: number }>
|
|
134
142
|
| Readonly<{ source: ProofSourceSpec }>
|
|
135
143
|
>
|
|
136
144
|
>;
|
|
137
|
-
initial_source?: Readonly<{
|
|
138
|
-
|
|
145
|
+
initial_source?: Readonly<{
|
|
146
|
+
symbol_id: string;
|
|
147
|
+
export?: string;
|
|
148
|
+
arg?: number;
|
|
149
|
+
}>;
|
|
150
|
+
pattern_source?: Readonly<{
|
|
151
|
+
symbol_id: string;
|
|
152
|
+
export?: string;
|
|
153
|
+
arg?: number;
|
|
154
|
+
}>;
|
|
139
155
|
/** kind source: the project source Kibi reads. */
|
|
140
156
|
source?: ProofSourceSpec;
|
|
141
157
|
}>;
|
|
@@ -151,7 +167,8 @@ export type ProofSourceSpec = Readonly<Record<string, unknown>> &
|
|
|
151
167
|
| "references"
|
|
152
168
|
| "schema"
|
|
153
169
|
| "routes"
|
|
154
|
-
| "validator"
|
|
170
|
+
| "validator"
|
|
171
|
+
| "prolog";
|
|
155
172
|
}>;
|
|
156
173
|
|
|
157
174
|
export type ProofBinding = Readonly<{
|