kibi-cli 2.22.1 → 2.24.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-operation-metadata.d.ts +1 -1
- package/dist/cli-operation-metadata.js +1 -1
- package/dist/cli-register-foundation.d.ts.map +1 -1
- package/dist/cli-register-foundation.js +1 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +51 -3
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +28 -0
- package/dist/commands/migrate.d.ts +10 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +81 -9
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +15 -1
- package/dist/decision-firmness.d.ts +16 -4
- package/dist/decision-firmness.d.ts.map +1 -1
- package/dist/decision-firmness.js +29 -6
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +39 -36
- package/dist/intent-search.d.ts +2 -0
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +43 -4
- package/dist/operations/bootstrap/activation.d.ts +7 -1
- package/dist/operations/bootstrap/activation.d.ts.map +1 -1
- package/dist/operations/bootstrap/activation.js +27 -2
- package/dist/operations/bootstrap/candidate-helpers.d.ts +6 -0
- package/dist/operations/bootstrap/candidate-helpers.d.ts.map +1 -1
- package/dist/operations/bootstrap/candidate-helpers.js +19 -0
- package/dist/operations/bootstrap/generate.d.ts.map +1 -1
- package/dist/operations/bootstrap/generate.js +56 -16
- package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance-actions.js +14 -2
- package/dist/operations/bootstrap/guidance.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance.js +6 -2
- package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
- package/dist/operations/bootstrap/intent-claims.js +2 -2
- package/dist/operations/bootstrap/markdown-candidates.d.ts.map +1 -1
- package/dist/operations/bootstrap/markdown-candidates.js +2 -2
- package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
- package/dist/operations/bootstrap/presentation.js +75 -9
- package/dist/operations/bootstrap/types.d.ts +6 -0
- package/dist/operations/bootstrap/types.d.ts.map +1 -1
- package/dist/operations/migration/firm-supersede.d.ts +19 -0
- package/dist/operations/migration/firm-supersede.d.ts.map +1 -0
- package/dist/operations/migration/firm-supersede.js +60 -0
- package/dist/operations/migration/kb-sources.d.ts +8 -5
- package/dist/operations/migration/kb-sources.d.ts.map +1 -1
- package/dist/operations/migration/kb-sources.js +50 -14
- package/dist/operations/migration/schema6.d.ts.map +1 -1
- package/dist/operations/migration/schema6.js +52 -2
- package/dist/operations/migration/source-paths.d.ts +18 -1
- package/dist/operations/migration/source-paths.d.ts.map +1 -1
- package/dist/operations/migration/source-paths.js +111 -16
- package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
- package/dist/operations/modeling/suggest-predicates.js +22 -7
- package/dist/operations/mutation/requirement-semantics.d.ts +19 -1
- package/dist/operations/mutation/requirement-semantics.d.ts.map +1 -1
- package/dist/operations/mutation/requirement-semantics.js +63 -2
- package/dist/operations/mutation/strict-fact.js +1 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +2 -1
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +22 -2
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +49 -17
- package/dist/public/impact/analyzer.d.ts.map +1 -1
- package/dist/public/impact/analyzer.js +7 -1
- package/dist/public/impact/diagnostics.d.ts.map +1 -1
- package/dist/public/impact/diagnostics.js +16 -3
- package/dist/public/impact/types.d.ts +2 -0
- package/dist/public/impact/types.d.ts.map +1 -1
- package/dist/public/operations/catalog.d.ts +8 -3
- 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 +10 -3
- package/dist/public/operations/check-helpers.d.ts +10 -0
- package/dist/public/operations/check-helpers.d.ts.map +1 -1
- package/dist/public/operations/check-helpers.js +54 -0
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +1 -0
- package/dist/public/operations/decision-firmness.d.ts +14 -0
- package/dist/public/operations/decision-firmness.d.ts.map +1 -1
- package/dist/public/operations/decision-firmness.js +60 -7
- package/dist/public/operations/discovery-executors.d.ts +18 -0
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +80 -8
- package/dist/public/operations/schema6-check-actions.d.ts +4 -0
- package/dist/public/operations/schema6-check-actions.d.ts.map +1 -1
- package/dist/public/operations/schema6-check-actions.js +39 -2
- package/dist/public/operations/source-path-dangling.d.ts +1 -1
- package/dist/public/operations/source-path-dangling.d.ts.map +1 -1
- package/dist/public/operations/source-path-dangling.js +11 -2
- package/dist/public/operations/specs/composite.d.ts +1 -1
- package/dist/public/operations/specs/discovery.d.ts +5 -0
- package/dist/public/operations/specs/discovery.d.ts.map +1 -1
- package/dist/public/operations/specs/discovery.js +5 -0
- package/dist/public/operations/specs/planning.d.ts +1 -1
- package/dist/public/operations/specs/planning.js +1 -1
- package/dist/public/operations/specs/reporting.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.js +6 -4
- package/dist/public/operations/specs/semantic.d.ts +1 -1
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +4 -2
- package/dist/public/operations/specs/upsert.d.ts +1 -1
- package/dist/public/operations/specs/upsert.js +1 -1
- package/dist/public/schemas/entity.js +1 -1
- package/dist/public/skills/kibi-bootstrap/SKILL.md +5 -4
- package/dist/public/skills/kibi-usage/SKILL.md +36 -16
- package/dist/public/skills/kibi-usage/resources/workflows.md +3 -1
- package/dist/schemas/entity.schema.json +1 -1
- package/dist/search-answer.d.ts +14 -0
- package/dist/search-answer.d.ts.map +1 -1
- package/dist/search-answer.js +51 -5
- package/dist/traceability/git-change-snapshot.d.ts +46 -0
- package/dist/traceability/git-change-snapshot.d.ts.map +1 -1
- package/dist/traceability/git-change-snapshot.js +187 -3
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
- package/dist/traceability/staged-diagnostics.js +5 -6
- package/dist/traceability/staged-inventory-drift.d.ts +24 -0
- package/dist/traceability/staged-inventory-drift.d.ts.map +1 -0
- package/dist/traceability/staged-inventory-drift.js +84 -0
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +1 -2
- package/dist/types/entities.d.ts +2 -2
- package/dist/types/entities.d.ts.map +1 -1
- package/dist/utils/content-freshness.d.ts +9 -0
- package/dist/utils/content-freshness.d.ts.map +1 -1
- package/dist/utils/content-freshness.js +33 -0
- package/dist/utils/kb-manifest.d.ts +7 -0
- package/dist/utils/kb-manifest.d.ts.map +1 -1
- package/dist/utils/kb-manifest.js +6 -0
- package/dist/utils/merge-driver-setup.d.ts +10 -0
- package/dist/utils/merge-driver-setup.d.ts.map +1 -0
- package/dist/utils/merge-driver-setup.js +69 -0
- package/dist/utils/rule-registry.generated.d.ts +1 -1
- package/dist/utils/rule-registry.generated.js +1 -1
- package/dist/utils/schema-version.d.ts +1 -1
- package/dist/utils/schema-version.d.ts.map +1 -1
- package/dist/utils/schema-version.js +3 -1
- package/package.json +2 -2
- package/src/public/impact/analyzer.ts +13 -1
- package/src/public/impact/diagnostics.ts +20 -3
- package/src/public/impact/types.ts +2 -0
- package/src/public/operations/check-executor.ts +16 -4
- package/src/public/operations/check-helpers.ts +65 -0
- package/src/public/operations/contracts.ts +1 -0
- package/src/public/operations/decision-firmness.ts +83 -10
- package/src/public/operations/discovery-executors.ts +124 -7
- package/src/public/operations/schema6-check-actions.ts +43 -2
- package/src/public/operations/source-path-dangling.ts +12 -2
- package/src/public/operations/specs/discovery.ts +6 -0
- package/src/public/operations/specs/planning.ts +1 -1
- package/src/public/operations/specs/reporting.ts +6 -4
- package/src/public/operations/specs/semantic.ts +4 -2
- package/src/public/operations/specs/upsert.ts +1 -1
- package/src/public/schemas/entity.ts +1 -1
- package/src/public/skills/kibi-bootstrap/SKILL.md +5 -4
- package/src/public/skills/kibi-usage/SKILL.md +36 -16
- package/src/public/skills/kibi-usage/resources/workflows.md +3 -1
- package/src/schemas/entity.schema.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
import { extractFromMarkdownString } from "../extractors/markdown.js";
|
|
19
|
+
import { analyzeSemanticAdvisorInput } from "../operations/semantic-advisor/analyze-prose.js";
|
|
20
|
+
import { validateSemanticInventoryBoundary } from "../operations/semantic-advisor/ingestion-boundary.js";
|
|
21
|
+
/** A staged requirement whose semantic inventory no longer matches its prose. */
|
|
22
|
+
// implements REQ-kibi-proposition-complete-ingestion
|
|
23
|
+
export const STAGED_INVENTORY_DRIFT_RULE = "semantic-inventory-drift";
|
|
24
|
+
function advertisesContract(entity) {
|
|
25
|
+
return (entity.semantic_inventory_version !== undefined ||
|
|
26
|
+
entity.semantic_source_hash !== undefined ||
|
|
27
|
+
entity.semantic_inventory !== undefined);
|
|
28
|
+
}
|
|
29
|
+
function payloadOf(result) {
|
|
30
|
+
return {
|
|
31
|
+
type: result.entity.type,
|
|
32
|
+
id: result.entity.id,
|
|
33
|
+
properties: result.entity,
|
|
34
|
+
relationships: result.relationships,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function baseResult(path, readBase) {
|
|
38
|
+
const content = readBase(path);
|
|
39
|
+
if (content === null)
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
return extractFromMarkdownString(content, path);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The staged requirements that declare a semantic inventory contract (now
|
|
50
|
+
* or in the base tree) whose inventory no longer matches their prose. The
|
|
51
|
+
* next `kibi sync` refuses them under the proposition-complete boundary;
|
|
52
|
+
* the gate applies the same test, so drift is caught at the commit that
|
|
53
|
+
* introduces it instead of after a later merge. Requirements without a
|
|
54
|
+
* contract are left to sync, which decides from its own baseline.
|
|
55
|
+
*/
|
|
56
|
+
// implements REQ-kibi-proposition-complete-ingestion
|
|
57
|
+
export function stagedInventoryDriftViolations(input) {
|
|
58
|
+
const violations = [];
|
|
59
|
+
for (const result of input.snapshotEntities) {
|
|
60
|
+
if (result.entity.type !== "req")
|
|
61
|
+
continue;
|
|
62
|
+
const source = result.entity.source;
|
|
63
|
+
if (!input.stagedPaths.has(source))
|
|
64
|
+
continue;
|
|
65
|
+
const payload = payloadOf(result);
|
|
66
|
+
const semantic = analyzeSemanticAdvisorInput({ payload });
|
|
67
|
+
const boundary = validateSemanticInventoryBoundary(payload, result.relationships, semantic.receipt);
|
|
68
|
+
if (boundary.errors.length === 0)
|
|
69
|
+
continue;
|
|
70
|
+
const base = baseResult(source, input.readBase);
|
|
71
|
+
const enforce = advertisesContract(result.entity) ||
|
|
72
|
+
(base !== null && advertisesContract(base.entity));
|
|
73
|
+
if (!enforce)
|
|
74
|
+
continue;
|
|
75
|
+
violations.push({
|
|
76
|
+
rule: STAGED_INVENTORY_DRIFT_RULE,
|
|
77
|
+
entityId: result.entity.id,
|
|
78
|
+
source,
|
|
79
|
+
errors: boundary.errors,
|
|
80
|
+
message: `${result.entity.id} (${source}): the semantic inventory does not match the requirement's prose, so the next kibi sync rejects it: ${boundary.errors.join("; ")}. Run 'kibi migrate' to re-derive it, or kb_model mode analyze with the complete prose and keep its inventory contract.`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return violations.sort((left, right) => left.entityId.localeCompare(right.entityId));
|
|
84
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staged-symbols-manifest.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-symbols-manifest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EACL,KAAK,wBAAwB,EAG9B,MAAM,qCAAqC,CAAC;AAI7C,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0ClD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,cAAc,CAAC;IACtD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,mCAAmC,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,qCAAqC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC5D,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AA6ED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,wBAAwB,GACpC,wBAAwB,GAAG,IAAI,CAOjC;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,GAAG,IAAI,CAMhB;
|
|
1
|
+
{"version":3,"file":"staged-symbols-manifest.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-symbols-manifest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EACL,KAAK,wBAAwB,EAG9B,MAAM,qCAAqC,CAAC;AAI7C,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0ClD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,cAAc,CAAC;IACtD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,mCAAmC,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,qCAAqC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC5D,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AA6ED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,wBAAwB,GACpC,wBAAwB,GAAG,IAAI,CAOjC;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,GAAG,IAAI,CAMhB;AAoTD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACnD,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,oBAAoB,CAAC;CAC7E,GAAG,+BAA+B,CAoLlC;AAED,wBAAgB,4CAA4C,CAAC,OAAO,EAAE;IACpE,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACpD,GAAG,qCAAqC,CAqExC"}
|
|
@@ -281,8 +281,7 @@ function diffSymbolEvidence(sourcePath, expectedSymbols, stagedSymbols) {
|
|
|
281
281
|
if (missing.length === 0 && extra.length === 0) {
|
|
282
282
|
return null;
|
|
283
283
|
}
|
|
284
|
-
// Keep the complete lists:
|
|
285
|
-
// accurate "… and N more"), while JSON evidence carries every name.
|
|
284
|
+
// Keep the complete lists: the gate output names every symbol.
|
|
286
285
|
return {
|
|
287
286
|
path: sourcePath,
|
|
288
287
|
expectedCount: expectedSymbols.length,
|
package/dist/types/entities.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export type Requirement = BaseEntity & {
|
|
|
67
67
|
approval_ref?: string;
|
|
68
68
|
/** Why the requirement exists; explanation, not checked meaning. */
|
|
69
69
|
rationale?: string;
|
|
70
|
-
/** How firmly it was decided; absent reads as default
|
|
70
|
+
/** How firmly it was decided; absent reads as `default` when an agent wrote it, else `firm`. */
|
|
71
71
|
firmness?: "firm" | "default" | "exploratory";
|
|
72
72
|
/** Exception requirements only: claim keys of the exempted requirement it waives. */
|
|
73
73
|
exempts_claims?: string[];
|
|
@@ -80,7 +80,7 @@ export type TestEntity = BaseEntity & TestVerificationFields & {
|
|
|
80
80
|
};
|
|
81
81
|
export type ADR = BaseEntity & {
|
|
82
82
|
type: "adr";
|
|
83
|
-
/** How firmly it was decided; absent reads as default
|
|
83
|
+
/** How firmly it was decided; absent reads as `default` when an agent wrote it, else `firm`. */
|
|
84
84
|
firmness?: "firm" | "default" | "exploratory";
|
|
85
85
|
};
|
|
86
86
|
export type Flag = BaseEntity & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../src/types/entities.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../src/types/entities.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,5 +9,14 @@ export declare function confirmSourceNewerReasons(workspaceRoot: string, storePa
|
|
|
9
9
|
kept: Reason[];
|
|
10
10
|
dropped: Reason[];
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* The `meta` freshness block reporting operations return (kb_graph,
|
|
14
|
+
* kb_find_gaps, kb_coverage) comes straight from Prolog, which compares file
|
|
15
|
+
* times only. Apply the same content confirmation kb_status does, so a
|
|
16
|
+
* checkout or `touch` that changed no byte is not reported as stale.
|
|
17
|
+
*/
|
|
18
|
+
export declare function confirmMetaFreshness<T extends {
|
|
19
|
+
readonly meta?: Readonly<Record<string, unknown>>;
|
|
20
|
+
}>(payload: T, workspaceRoot: string): T;
|
|
12
21
|
export {};
|
|
13
22
|
//# sourceMappingURL=content-freshness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-freshness.d.ts","sourceRoot":"","sources":["../../src/utils/content-freshness.ts"],"names":[],"mappings":"AAYA,uDAAuD;AACvD,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CAIxD,CAAC;AAEH,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAyJhD;;;GAGG;AAEH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBvC"}
|
|
1
|
+
{"version":3,"file":"content-freshness.d.ts","sourceRoot":"","sources":["../../src/utils/content-freshness.ts"],"names":[],"mappings":"AAYA,uDAAuD;AACvD,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CAIxD,CAAC;AAEH,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAyJhD;;;GAGG;AAEH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBvC;AAED;;;;;GAKG;AAEH,wBAAgB,oBAAoB,CAClC,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,EAC/D,OAAO,EAAE,CAAC,EAAE,aAAa,EAAE,MAAM,GAAG,CAAC,CAoCtC"}
|
|
@@ -169,3 +169,36 @@ export function confirmSourceNewerReasons(workspaceRoot, storePath, reasons) {
|
|
|
169
169
|
}
|
|
170
170
|
return { kept, dropped };
|
|
171
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* The `meta` freshness block reporting operations return (kb_graph,
|
|
174
|
+
* kb_find_gaps, kb_coverage) comes straight from Prolog, which compares file
|
|
175
|
+
* times only. Apply the same content confirmation kb_status does, so a
|
|
176
|
+
* checkout or `touch` that changed no byte is not reported as stale.
|
|
177
|
+
*/
|
|
178
|
+
// implements REQ-cli-status-pre-first-sync
|
|
179
|
+
export function confirmMetaFreshness(payload, workspaceRoot) {
|
|
180
|
+
const meta = payload.meta;
|
|
181
|
+
if (meta === undefined ||
|
|
182
|
+
typeof meta.kbPath !== "string" ||
|
|
183
|
+
!Array.isArray(meta.staleReasons))
|
|
184
|
+
return payload;
|
|
185
|
+
const reasons = meta.staleReasons.filter((reason) => record(reason) !== null);
|
|
186
|
+
const { kept, dropped } = confirmSourceNewerReasons(workspaceRoot, meta.kbPath, reasons);
|
|
187
|
+
if (dropped.length === 0)
|
|
188
|
+
return payload;
|
|
189
|
+
const allConfirmed = kept.length === 0 &&
|
|
190
|
+
meta.staleReasonsTruncated !== true &&
|
|
191
|
+
meta.syncState === "stale";
|
|
192
|
+
return {
|
|
193
|
+
...payload,
|
|
194
|
+
meta: {
|
|
195
|
+
...meta,
|
|
196
|
+
staleReasons: kept,
|
|
197
|
+
// A truncated list counts more reasons than it carries.
|
|
198
|
+
staleReasonCount: Math.max(kept.length, (typeof meta.staleReasonCount === "number"
|
|
199
|
+
? meta.staleReasonCount
|
|
200
|
+
: reasons.length) - dropped.length),
|
|
201
|
+
...(allConfirmed ? { syncState: "fresh", dirty: false } : {}),
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
@@ -26,6 +26,13 @@ export interface KbManifest {
|
|
|
26
26
|
* supersedes-reason-missing only while every edge it has is listed here.
|
|
27
27
|
*/
|
|
28
28
|
supersedesReasonAcknowledged?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Supersede edges (`FROM -> TO`) the schema 10 migration acknowledged:
|
|
31
|
+
* the target has no `firmness` and only reads as firm since firmness
|
|
32
|
+
* follows origin, and the superseder is not firm. Only these edges are
|
|
33
|
+
* exempt from decision-firm-superseded.
|
|
34
|
+
*/
|
|
35
|
+
firmSupersedeAcknowledged?: string[];
|
|
29
36
|
}
|
|
30
37
|
export declare function defaultKbManifest(): KbManifest;
|
|
31
38
|
export type KbManifestStatus = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kb-manifest.d.ts","sourceRoot":"","sources":["../../src/utils/kb-manifest.ts"],"names":[],"mappings":"AA8BA;;;;;;;GAOG;AAEH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,WAAW,GACX,gBAAgB,CAAC;AAErB,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,uBAAuB,EAAE,uBAAuB,CAAC;IACjD;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B;;;;OAIG;IACH,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"kb-manifest.d.ts","sourceRoot":"","sources":["../../src/utils/kb-manifest.ts"],"names":[],"mappings":"AA8BA;;;;;;;GAOG;AAEH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,WAAW,GACX,gBAAgB,CAAC;AAErB,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,uBAAuB,EAAE,uBAAuB,CAAC;IACjD;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B;;;;OAIG;IACH,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;IACxC;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,MAAM,EAAE,CAAC;CACtC;AAED,wBAAgB,iBAAiB,IAAI,UAAU,CAM9C;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAClE;IACE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACxC,QAAQ,EAAE,IAAI,CAAC;IACf,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAuEN,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,gBAAgB,CAyD5E;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAGvE;AAED,8EAA8E;AAC9E,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,CAEzE;AASD,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,UAAU,GACnB,MAAM,CAIR"}
|
|
@@ -72,6 +72,7 @@ function validateManifest(parsed) {
|
|
|
72
72
|
}
|
|
73
73
|
const acknowledged = parsed.contextAcknowledged;
|
|
74
74
|
const supersedesAcknowledged = parsed.supersedesReasonAcknowledged;
|
|
75
|
+
const firmAcknowledged = parsed.firmSupersedeAcknowledged;
|
|
75
76
|
return {
|
|
76
77
|
manifestVersion,
|
|
77
78
|
schemaVersion,
|
|
@@ -86,6 +87,11 @@ function validateManifest(parsed) {
|
|
|
86
87
|
supersedesReasonAcknowledged: supersedesAcknowledged.filter((id) => typeof id === "string"),
|
|
87
88
|
}
|
|
88
89
|
: {}),
|
|
90
|
+
...(Array.isArray(firmAcknowledged)
|
|
91
|
+
? {
|
|
92
|
+
firmSupersedeAcknowledged: firmAcknowledged.filter((id) => typeof id === "string"),
|
|
93
|
+
}
|
|
94
|
+
: {}),
|
|
89
95
|
};
|
|
90
96
|
}
|
|
91
97
|
export function readKbManifestStatus(workspaceRoot) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** How to route the authored manifests to `kibi merge-driver`. */
|
|
2
|
+
export declare const MERGE_DRIVER_SETUP = "Add to .gitattributes: \".kb/symbols.yaml merge=kibi\" and \".kb/relationships/*.yaml merge=kibi\"; then run: git config merge.kibi.driver \"npm exec --no -- kibi merge-driver %O %A %B\" (see docs/cli-reference.md#kibi-merge-driver-base-current-other).";
|
|
3
|
+
/**
|
|
4
|
+
* What keeps Git from resolving `.kb/symbols.yaml` with `kibi merge-driver`:
|
|
5
|
+
* the attribute that routes the file to it, and the driver command. Empty
|
|
6
|
+
* when both are set, and null outside a Git work tree or while the
|
|
7
|
+
* manifest holds no symbol records (nothing to conflict on yet).
|
|
8
|
+
*/
|
|
9
|
+
export declare function manifestMergeDriverGaps(cwd: string): string[] | null;
|
|
10
|
+
//# sourceMappingURL=merge-driver-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-driver-setup.d.ts","sourceRoot":"","sources":["../../src/utils/merge-driver-setup.ts"],"names":[],"mappings":"AAsBA,kEAAkE;AAElE,eAAO,MAAM,kBAAkB,iQAC2N,CAAC;AAwB3P;;;;;GAKG;AAEH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAiBpE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
import { execFileSync } from "node:child_process";
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
/** How to route the authored manifests to `kibi merge-driver`. */
|
|
22
|
+
// implements REQ-cli-doctor
|
|
23
|
+
export const MERGE_DRIVER_SETUP = 'Add to .gitattributes: ".kb/symbols.yaml merge=kibi" and ".kb/relationships/*.yaml merge=kibi"; then run: git config merge.kibi.driver "npm exec --no -- kibi merge-driver %O %A %B" (see docs/cli-reference.md#kibi-merge-driver-base-current-other).';
|
|
24
|
+
function gitOutput(cwd, args) {
|
|
25
|
+
try {
|
|
26
|
+
return execFileSync("git", [...args], {
|
|
27
|
+
cwd,
|
|
28
|
+
encoding: "utf8",
|
|
29
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
30
|
+
}).trim();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function hasSymbolRecords(cwd) {
|
|
37
|
+
try {
|
|
38
|
+
return /^\s*-\s+id\s*:/m.test(readFileSync(path.join(cwd, ".kb", "symbols.yaml"), "utf8"));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* What keeps Git from resolving `.kb/symbols.yaml` with `kibi merge-driver`:
|
|
46
|
+
* the attribute that routes the file to it, and the driver command. Empty
|
|
47
|
+
* when both are set, and null outside a Git work tree or while the
|
|
48
|
+
* manifest holds no symbol records (nothing to conflict on yet).
|
|
49
|
+
*/
|
|
50
|
+
// implements REQ-cli-doctor
|
|
51
|
+
export function manifestMergeDriverGaps(cwd) {
|
|
52
|
+
if (gitOutput(cwd, ["rev-parse", "--is-inside-work-tree"]) !== "true") {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (!hasSymbolRecords(cwd))
|
|
56
|
+
return null;
|
|
57
|
+
const attribute = gitOutput(cwd, [
|
|
58
|
+
"check-attr",
|
|
59
|
+
"merge",
|
|
60
|
+
"--",
|
|
61
|
+
".kb/symbols.yaml",
|
|
62
|
+
]);
|
|
63
|
+
const routed = attribute?.endsWith(": merge: kibi") === true;
|
|
64
|
+
const driver = gitOutput(cwd, ["config", "--get", "merge.kibi.driver"]);
|
|
65
|
+
return [
|
|
66
|
+
...(routed ? [] : [".kb/symbols.yaml is not routed to merge=kibi"]),
|
|
67
|
+
...(driver ? [] : ["merge.kibi.driver is not set"]),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const GENERATED_RULES: readonly [{
|
|
2
2
|
readonly name: "must-priority-coverage";
|
|
3
|
-
readonly description: "Every must-priority requirement must have a scenario and a test";
|
|
3
|
+
readonly description: "Every current must-priority requirement (not superseded, retired, or closed without the implemented tag) must have a scenario and a test";
|
|
4
4
|
readonly enforcementClass: "canonical";
|
|
5
5
|
readonly category: "coverage";
|
|
6
6
|
}, {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export const GENERATED_RULES = [
|
|
11
11
|
{
|
|
12
12
|
name: "must-priority-coverage",
|
|
13
|
-
description: "Every must-priority requirement must have a scenario and a test",
|
|
13
|
+
description: "Every current must-priority requirement (not superseded, retired, or closed without the implemented tag) must have a scenario and a test",
|
|
14
14
|
enforcementClass: "canonical",
|
|
15
15
|
category: "coverage",
|
|
16
16
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-version.d.ts","sourceRoot":"","sources":["../../src/utils/schema-version.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema-version.d.ts","sourceRoot":"","sources":["../../src/utils/schema-version.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAClC,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;CACxB;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,GACrD,IAAI,GACJ,SAAS,CAAC;AAGd,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAChD,MAAM,GAAG,IAAI,CAgBf;AAGD,wBAAgB,sBAAsB,CACpC,MAAM,CAAC,EAAE,mBAAmB,GAC3B,mBAAmB,CAgDrB"}
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
// semantic_text so context sections never change a requirement's meaning.
|
|
21
21
|
// Schema 9 adds decision firmness on requirements and ADRs and requires a
|
|
22
22
|
// "Why this changed" section on new supersedes.
|
|
23
|
-
|
|
23
|
+
// Schema 10 reads a requirement or ADR without firmness as firm unless an
|
|
24
|
+
// agent wrote it.
|
|
25
|
+
export const LATEST_KB_SCHEMA_VERSION = 10;
|
|
24
26
|
// implements REQ-003
|
|
25
27
|
export function normalizeSchemaVersion(schemaVersion) {
|
|
26
28
|
if (schemaVersion === undefined || schemaVersion === null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
"fast-glob": "^3.2.12",
|
|
236
236
|
"gray-matter": "^4.0.3",
|
|
237
237
|
"js-yaml": "^4.1.0",
|
|
238
|
-
"kibi-core": "^0.22.
|
|
238
|
+
"kibi-core": "^0.22.1",
|
|
239
239
|
"kibi-plugin-builtin": "^0.5.2",
|
|
240
240
|
"kibi-plugin-sdk": "^0.5.0",
|
|
241
241
|
"kibi-swipl": "^1.0.1",
|
|
@@ -11,6 +11,7 @@ import { captureStagedSnapshot } from "../../traceability/git-change-snapshot.js
|
|
|
11
11
|
import { readSnapshotKnowledge } from "../../traceability/snapshot-knowledge.js";
|
|
12
12
|
import type { ExtractedSymbol } from "../../traceability/symbol-extract.js";
|
|
13
13
|
import { extractSymbolsFromStagedFileAsync } from "../../traceability/symbol-extract.js";
|
|
14
|
+
import { isTraceabilityRelationshipType } from "../symbol-granularity.js";
|
|
14
15
|
import {
|
|
15
16
|
createSemanticReviewDiagnostics,
|
|
16
17
|
createSymbolGranularityDiagnostics,
|
|
@@ -153,7 +154,18 @@ export async function analyzeChangedFileImpact(
|
|
|
153
154
|
manifestResults: activeManifestResults,
|
|
154
155
|
symbolsByFile,
|
|
155
156
|
}),
|
|
156
|
-
...createSemanticReviewDiagnostics({
|
|
157
|
+
...createSemanticReviewDiagnostics({
|
|
158
|
+
symbolsByFile,
|
|
159
|
+
linkedSourceFiles: new Set(
|
|
160
|
+
activeManifestResults
|
|
161
|
+
.filter((result) =>
|
|
162
|
+
result.relationships.some((relationship) =>
|
|
163
|
+
isTraceabilityRelationshipType(relationship.type),
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
.map((result) => result.sourceFile as string),
|
|
167
|
+
),
|
|
168
|
+
}),
|
|
157
169
|
];
|
|
158
170
|
const maxDiagnostics = options.maxDiagnostics;
|
|
159
171
|
const cappedDiagnostics =
|
|
@@ -81,6 +81,12 @@ function getGranularSymbolsForSourceFile(
|
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function hasTraceabilityLinks(symbol: ExtractedSymbol): boolean {
|
|
85
|
+
return (symbol.relationships ?? []).some((relationship) =>
|
|
86
|
+
isTraceabilityRelationshipType(relationship.type),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
84
90
|
function formatRelationshipTargets(symbol: ExtractedSymbol): string {
|
|
85
91
|
const targets = [
|
|
86
92
|
...new Set(
|
|
@@ -183,15 +189,26 @@ export function createSemanticReviewDiagnostics(
|
|
|
183
189
|
if (!isChangedBehavioralReviewCandidate(symbol)) continue;
|
|
184
190
|
|
|
185
191
|
const linkedTargets = formatRelationshipTargets(symbol);
|
|
192
|
+
const file = symbol.location.file;
|
|
193
|
+
// In a file no requirement owns at all, the changed behavior has no
|
|
194
|
+
// recorded intent, so the review is "record it", not "compare it with
|
|
195
|
+
// its links". Elsewhere a coarser symbol of the file may still own it.
|
|
196
|
+
const unlinked =
|
|
197
|
+
!hasTraceabilityLinks(symbol) &&
|
|
198
|
+
!(options.linkedSourceFiles?.has(file) ?? false);
|
|
186
199
|
diagnostics.push({
|
|
187
200
|
id: "symbol_semantic_review_needed",
|
|
188
201
|
severity: "warning",
|
|
189
202
|
blocking: false,
|
|
190
203
|
category: "symbol",
|
|
191
|
-
files: [
|
|
204
|
+
files: [file],
|
|
192
205
|
docs: ["docs/symbol-traceability-taxonomy.md"],
|
|
193
|
-
message:
|
|
194
|
-
|
|
206
|
+
message: unlinked
|
|
207
|
+
? `Changed behavioral symbol ${symbol.name} is in a file no requirement owns, so no recorded intent covers the behavior it has after this change`
|
|
208
|
+
: `Changed behavioral symbol ${symbol.name} needs semantic review against linked Kibi coverage: ${linkedTargets}`,
|
|
209
|
+
suggestion: unlinked
|
|
210
|
+
? `If the change adds or alters behavior, record it with the change: kb_search({query:"<the behavior>", sourceLocations:[{path:"${file}"}]}) for a requirement that already describes it and link ${symbol.name} to it; if none does, kb_upsert a requirement with a scenario and link the symbol with implements. If behavior is unchanged (a refactor, renaming), say so in the final report instead.`
|
|
211
|
+
: `Use kb_search and kb_query for ${file}, then verify that ${linkedTargets} actually describe and test the changed behavior or UI copy. Update Kibi links or requirements/tests while the edit context is fresh.`,
|
|
195
212
|
});
|
|
196
213
|
}
|
|
197
214
|
}
|
|
@@ -49,6 +49,8 @@ export type SymbolGranularityDiagnosticsOptions = {
|
|
|
49
49
|
|
|
50
50
|
export type SemanticReviewDiagnosticsOptions = {
|
|
51
51
|
readonly symbolsByFile: SymbolsByFile;
|
|
52
|
+
/** Source files where some manifest symbol carries a traceability link. */
|
|
53
|
+
readonly linkedSourceFiles?: ReadonlySet<string>;
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
export type SymbolQualityDiagnosticsOptions = {
|
|
@@ -32,12 +32,14 @@ import {
|
|
|
32
32
|
buildSummary,
|
|
33
33
|
} from "./check-format-shared.js";
|
|
34
34
|
import {
|
|
35
|
+
SCOPED_CHECK_OMITTED_ID,
|
|
35
36
|
analyzeKbCheckImpact,
|
|
36
37
|
collectQueryPlanSafetyViolations,
|
|
37
38
|
getEffectiveRules,
|
|
38
39
|
partitionCheckFindings,
|
|
39
40
|
qualityDiagnosticsFromImpact,
|
|
40
41
|
resolveCheckRules,
|
|
42
|
+
scopeQualityDiagnosticsToImpact,
|
|
41
43
|
stagedEntityIdStyleDiagnostics,
|
|
42
44
|
} from "./check-helpers.js";
|
|
43
45
|
import { collectCheckPolicyViolations } from "./check-policy-rules.js";
|
|
@@ -430,8 +432,17 @@ export async function executeCheck(
|
|
|
430
432
|
now: context.clock(),
|
|
431
433
|
...maxDiagnosticsOption,
|
|
432
434
|
});
|
|
435
|
+
// A check scoped to sourceFiles answers about those files; the KB-wide
|
|
436
|
+
// advisory scan only lists what concerns them.
|
|
437
|
+
const advisoryQualityDiagnostics =
|
|
438
|
+
impactResult && args.sourceFiles !== undefined && !hasExplicitRules
|
|
439
|
+
? scopeQualityDiagnosticsToImpact(
|
|
440
|
+
partitioned.qualityDiagnostics,
|
|
441
|
+
impactResult,
|
|
442
|
+
)
|
|
443
|
+
: partitioned.qualityDiagnostics;
|
|
433
444
|
const qualityDiagnostics = [
|
|
434
|
-
...
|
|
445
|
+
...advisoryQualityDiagnostics,
|
|
435
446
|
...extraQualityDiagnostics,
|
|
436
447
|
];
|
|
437
448
|
|
|
@@ -439,9 +450,10 @@ export async function executeCheck(
|
|
|
439
450
|
const migrationPlan = await migrationPlanForCheck(
|
|
440
451
|
context,
|
|
441
452
|
violations,
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
453
|
+
// The omitted-findings count is a pointer, not a repair to plan.
|
|
454
|
+
qualityDiagnostics.filter(
|
|
455
|
+
(diagnostic) => diagnostic.id !== SCOPED_CHECK_OMITTED_ID,
|
|
456
|
+
) as unknown as readonly Readonly<Record<string, unknown>>[],
|
|
445
457
|
statusPlan,
|
|
446
458
|
);
|
|
447
459
|
return {
|
|
@@ -80,6 +80,71 @@ export function partitionCheckFindings(findings: readonly Violation[]): {
|
|
|
80
80
|
return { violations, qualityDiagnostics };
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export const SCOPED_CHECK_OMITTED_ID = "scoped_check_omitted_quality";
|
|
84
|
+
const OMITTED_RULES_SHOWN = 5;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Advisory rule findings of a check scoped to `sourceFiles` that concern
|
|
88
|
+
* those files: findings on an entity a changed file's symbols link to (or on
|
|
89
|
+
* those symbols) and findings whose source is one of the files. The KB-wide rest (unapproved requirements elsewhere,
|
|
90
|
+
* unrelated scenarios) is reduced to one info diagnostic with per-rule counts,
|
|
91
|
+
* so a check after a small edit answers about that edit; kb_check without
|
|
92
|
+
* sourceFiles still lists every finding.
|
|
93
|
+
*/
|
|
94
|
+
// implements REQ-cli-check
|
|
95
|
+
export function scopeQualityDiagnosticsToImpact(
|
|
96
|
+
diagnostics: readonly QualityDiagnostic[],
|
|
97
|
+
impactResult: ChangedFileImpactResult,
|
|
98
|
+
): QualityDiagnostic[] {
|
|
99
|
+
const normalizePath = (value: string) =>
|
|
100
|
+
value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
101
|
+
const files = new Set(impactResult.sourceFiles.map(normalizePath));
|
|
102
|
+
const entityIds = new Set<string>();
|
|
103
|
+
for (const linked of impactResult.linkedEntities) {
|
|
104
|
+
entityIds.add(linked.id);
|
|
105
|
+
entityIds.add(linked.sourceSymbolId);
|
|
106
|
+
}
|
|
107
|
+
const inScope = (diagnostic: QualityDiagnostic) =>
|
|
108
|
+
(diagnostic.entityId !== undefined && entityIds.has(diagnostic.entityId)) ||
|
|
109
|
+
(diagnostic.source !== undefined &&
|
|
110
|
+
files.has(normalizePath(diagnostic.source))) ||
|
|
111
|
+
(diagnostic.files ?? []).some((file) => files.has(normalizePath(file)));
|
|
112
|
+
|
|
113
|
+
const kept: QualityDiagnostic[] = [];
|
|
114
|
+
const omittedByRule = new Map<string, number>();
|
|
115
|
+
for (const diagnostic of diagnostics) {
|
|
116
|
+
if (inScope(diagnostic)) {
|
|
117
|
+
kept.push(diagnostic);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const rule = diagnostic.id.replace(/^rule\./, "");
|
|
121
|
+
omittedByRule.set(rule, (omittedByRule.get(rule) ?? 0) + 1);
|
|
122
|
+
}
|
|
123
|
+
if (omittedByRule.size === 0) return kept;
|
|
124
|
+
|
|
125
|
+
const omitted = [...omittedByRule.values()].reduce((a, b) => a + b, 0);
|
|
126
|
+
const rules = [...omittedByRule.entries()].sort(
|
|
127
|
+
(a, b) => b[1] - a[1] || a[0].localeCompare(b[0]),
|
|
128
|
+
);
|
|
129
|
+
const shown = rules
|
|
130
|
+
.slice(0, OMITTED_RULES_SHOWN)
|
|
131
|
+
.map(([rule, count]) => `${rule} ×${count}`);
|
|
132
|
+
if (rules.length > OMITTED_RULES_SHOWN) {
|
|
133
|
+
shown.push(`${rules.length - OMITTED_RULES_SHOWN} more rules`);
|
|
134
|
+
}
|
|
135
|
+
kept.push({
|
|
136
|
+
id: SCOPED_CHECK_OMITTED_ID,
|
|
137
|
+
severity: "info",
|
|
138
|
+
blocking: false,
|
|
139
|
+
category: "integrity",
|
|
140
|
+
message: `This check is scoped to ${files.size} source ${files.size === 1 ? "file" : "files"}; ${omitted} advisory ${omitted === 1 ? "finding" : "findings"} elsewhere in the KB ${omitted === 1 ? "is" : "are"} not listed (${shown.join(", ")}). None of them concerns the checked files.`,
|
|
141
|
+
suggestion:
|
|
142
|
+
"Run kb_check without sourceFiles for the full KB audit when it matters to the task.",
|
|
143
|
+
evidence: { omitted, byRule: Object.fromEntries(rules) },
|
|
144
|
+
});
|
|
145
|
+
return kept;
|
|
146
|
+
}
|
|
147
|
+
|
|
83
148
|
function resolveChecksPlPath(): string {
|
|
84
149
|
const override = getKbPlPathOverride();
|
|
85
150
|
return path.join(path.dirname(override ?? resolveKbPlPath()), "checks.pl");
|