kibi-cli 0.10.0 → 0.10.1
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.js +2 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +204 -3
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +11 -14
- package/dist/commands/sync/manifest.d.ts +8 -2
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +56 -11
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +9 -7
- package/dist/extractors/manifest.d.ts +30 -0
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +60 -7
- package/dist/extractors/symbol-coordinates.d.ts +15 -0
- package/dist/extractors/symbol-coordinates.d.ts.map +1 -0
- package/dist/extractors/symbol-coordinates.js +83 -0
- package/dist/public/extractors/manifest.d.ts +1 -1
- package/dist/public/extractors/manifest.d.ts.map +1 -1
- package/dist/public/extractors/manifest.js +1 -1
- package/dist/traceability/evidence-model.d.ts +142 -0
- package/dist/traceability/evidence-model.d.ts.map +1 -0
- package/dist/traceability/evidence-model.js +70 -0
- package/dist/traceability/git-staged.d.ts +1 -0
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +28 -3
- package/dist/traceability/staged-diagnostics.d.ts +25 -0
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -0
- package/dist/traceability/staged-diagnostics.js +67 -0
- package/dist/traceability/staged-impact-contract.d.ts +57 -0
- package/dist/traceability/staged-impact-contract.d.ts.map +1 -0
- package/dist/traceability/staged-impact-contract.js +202 -0
- package/dist/traceability/staged-symbols-manifest.d.ts +23 -0
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -0
- package/dist/traceability/staged-symbols-manifest.js +269 -0
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +33 -9
- package/dist/utils/manifest-paths.d.ts +8 -0
- package/dist/utils/manifest-paths.d.ts.map +1 -0
- package/dist/utils/manifest-paths.js +62 -0
- package/package.json +1 -1
- package/src/public/extractors/manifest.ts +2 -0
|
@@ -16,8 +16,11 @@
|
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
import { createHash } from "node:crypto";
|
|
19
|
-
import { readFileSync } from "node:fs";
|
|
19
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
20
|
+
import * as path from "node:path";
|
|
20
21
|
import { load as parseYAML } from "js-yaml";
|
|
22
|
+
import { DEFAULT_COORDINATES_PATH } from "../utils/manifest-paths.js";
|
|
23
|
+
import { mergeCoordinatesWithManifest, readCoordinateArtifact, } from "./symbol-coordinates.js";
|
|
21
24
|
export class ManifestError extends Error {
|
|
22
25
|
filePath;
|
|
23
26
|
constructor(message, filePath) {
|
|
@@ -26,6 +29,12 @@ export class ManifestError extends Error {
|
|
|
26
29
|
this.name = "ManifestError";
|
|
27
30
|
}
|
|
28
31
|
}
|
|
32
|
+
function getManifestSymbols(manifest, filePath) {
|
|
33
|
+
if (!manifest.symbols || !Array.isArray(manifest.symbols)) {
|
|
34
|
+
throw new ManifestError("No symbols array found in manifest", filePath);
|
|
35
|
+
}
|
|
36
|
+
return manifest.symbols;
|
|
37
|
+
}
|
|
29
38
|
function extractRelationships(id, symbol) {
|
|
30
39
|
const relationships = [];
|
|
31
40
|
if (Array.isArray(symbol.links)) {
|
|
@@ -66,10 +75,10 @@ function extractRelationships(id, symbol) {
|
|
|
66
75
|
return relationships;
|
|
67
76
|
}
|
|
68
77
|
function extractFromParsedManifest(manifest, filePath) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return
|
|
78
|
+
return extractFromManifestSymbolRecords(getManifestSymbols(manifest, filePath), filePath);
|
|
79
|
+
}
|
|
80
|
+
function extractFromManifestSymbolRecords(manifestSymbols, filePath) {
|
|
81
|
+
return manifestSymbols.map((symbol) => {
|
|
73
82
|
if (!symbol.title) {
|
|
74
83
|
throw new ManifestError("Missing required field: title", filePath);
|
|
75
84
|
}
|
|
@@ -96,6 +105,9 @@ function extractFromParsedManifest(manifest, filePath) {
|
|
|
96
105
|
};
|
|
97
106
|
});
|
|
98
107
|
}
|
|
108
|
+
function cloneManifestSymbols(manifest, filePath) {
|
|
109
|
+
return getManifestSymbols(manifest, filePath).map((symbol) => ({ ...symbol }));
|
|
110
|
+
}
|
|
99
111
|
// implements REQ-007
|
|
100
112
|
export function extractFromManifestString(content, filePath) {
|
|
101
113
|
try {
|
|
@@ -112,9 +124,50 @@ export function extractFromManifestString(content, filePath) {
|
|
|
112
124
|
throw error;
|
|
113
125
|
}
|
|
114
126
|
}
|
|
127
|
+
export function extractManifestSymbolRecordsString(content, filePath) {
|
|
128
|
+
try {
|
|
129
|
+
const manifest = parseYAML(content);
|
|
130
|
+
return cloneManifestSymbols(manifest, filePath);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error instanceof ManifestError) {
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
if (error instanceof Error) {
|
|
137
|
+
throw new ManifestError(`Failed to parse manifest: ${error.message}`, filePath);
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
115
142
|
export function extractFromManifest(filePath) {
|
|
116
|
-
|
|
117
|
-
|
|
143
|
+
return extractFromManifestSymbolRecords(readManifestWithCoordinateOverlay(filePath), filePath);
|
|
144
|
+
}
|
|
145
|
+
function resolveCoordinatesPath(manifestPath, coordinatesPath) {
|
|
146
|
+
if (coordinatesPath) {
|
|
147
|
+
return coordinatesPath;
|
|
148
|
+
}
|
|
149
|
+
return path.join(path.dirname(manifestPath), path.basename(DEFAULT_COORDINATES_PATH));
|
|
150
|
+
}
|
|
151
|
+
function readCoordinateArtifactFromFile(coordinatesPath) {
|
|
152
|
+
if (!existsSync(coordinatesPath)) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
return readCoordinateArtifact(readFileSync(coordinatesPath, "utf8"));
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (error instanceof Error) {
|
|
160
|
+
throw new ManifestError(`Failed to parse coordinate artifact: ${error.message}`, coordinatesPath);
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// implements REQ-core-extractors
|
|
166
|
+
export function readManifestWithCoordinateOverlay(manifestPath, coordinatesPath) {
|
|
167
|
+
const manifestContent = readFileSync(manifestPath, "utf8");
|
|
168
|
+
const manifestRecords = extractManifestSymbolRecordsString(manifestContent, manifestPath);
|
|
169
|
+
const coordinateArtifact = readCoordinateArtifactFromFile(resolveCoordinatesPath(manifestPath, coordinatesPath));
|
|
170
|
+
return mergeCoordinatesWithManifest(manifestRecords, coordinateArtifact);
|
|
118
171
|
}
|
|
119
172
|
function generateId(filePath, title) {
|
|
120
173
|
const hash = createHash("sha256");
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ManifestSymbolRecord } from "./manifest.js";
|
|
2
|
+
export interface SymbolCoordinatesRecord {
|
|
3
|
+
sourceFile: string;
|
|
4
|
+
sourceLine: number;
|
|
5
|
+
sourceColumn: number;
|
|
6
|
+
sourceEndLine: number;
|
|
7
|
+
sourceEndColumn: number;
|
|
8
|
+
}
|
|
9
|
+
export interface SymbolCoordinatesArtifact {
|
|
10
|
+
coordinates: Record<string, SymbolCoordinatesRecord>;
|
|
11
|
+
}
|
|
12
|
+
export declare function readCoordinateArtifact(content: string): SymbolCoordinatesArtifact;
|
|
13
|
+
export declare function writeCoordinateArtifact(coordinates: Record<string, SymbolCoordinatesRecord>): string;
|
|
14
|
+
export declare function mergeCoordinatesWithManifest(symbolRecords: ManifestSymbolRecord[], coordinateArtifact: SymbolCoordinatesArtifact | null): ManifestSymbolRecord[];
|
|
15
|
+
//# sourceMappingURL=symbol-coordinates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbol-coordinates.d.ts","sourceRoot":"","sources":["../../src/extractors/symbol-coordinates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;CACtD;AA0DD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,GACd,yBAAyB,CAkB3B;AAGD,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,GACnD,MAAM,CAUR;AAGD,wBAAgB,4BAA4B,CAC1C,aAAa,EAAE,oBAAoB,EAAE,EACrC,kBAAkB,EAAE,yBAAyB,GAAG,IAAI,GACnD,oBAAoB,EAAE,CAoBxB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { dump as dumpYAML, load as parseYAML } from "js-yaml";
|
|
2
|
+
const SYMBOL_COORDINATES_COMMENT_BLOCK = `# symbol-coordinates.yaml
|
|
3
|
+
# GENERATED coordinate artifact — do not edit manually.
|
|
4
|
+
# Run \`kibi sync --refresh-symbol-coordinates\` to refresh.
|
|
5
|
+
`;
|
|
6
|
+
function isRecord(value) {
|
|
7
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
function normalizeCoordinateRecord(value) {
|
|
10
|
+
if (!isRecord(value)) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const { sourceColumn, sourceEndColumn, sourceEndLine, sourceFile, sourceLine } = value;
|
|
14
|
+
if (typeof sourceFile !== "string" ||
|
|
15
|
+
typeof sourceLine !== "number" ||
|
|
16
|
+
typeof sourceColumn !== "number" ||
|
|
17
|
+
typeof sourceEndLine !== "number" ||
|
|
18
|
+
typeof sourceEndColumn !== "number") {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
sourceFile,
|
|
23
|
+
sourceLine,
|
|
24
|
+
sourceColumn,
|
|
25
|
+
sourceEndLine,
|
|
26
|
+
sourceEndColumn,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function sortCoordinates(coordinates) {
|
|
30
|
+
const sortedCoordinates = {};
|
|
31
|
+
for (const symbolId of Object.keys(coordinates).sort((left, right) => left.localeCompare(right))) {
|
|
32
|
+
const record = normalizeCoordinateRecord(coordinates[symbolId]);
|
|
33
|
+
if (!record) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
sortedCoordinates[symbolId] = record;
|
|
37
|
+
}
|
|
38
|
+
return sortedCoordinates;
|
|
39
|
+
}
|
|
40
|
+
// implements REQ-core-extractors
|
|
41
|
+
export function readCoordinateArtifact(content) {
|
|
42
|
+
const parsed = parseYAML(content);
|
|
43
|
+
if (!isRecord(parsed) || !isRecord(parsed.coordinates)) {
|
|
44
|
+
return { coordinates: {} };
|
|
45
|
+
}
|
|
46
|
+
const coordinates = {};
|
|
47
|
+
for (const [symbolId, record] of Object.entries(parsed.coordinates)) {
|
|
48
|
+
const normalizedRecord = normalizeCoordinateRecord(record);
|
|
49
|
+
if (!normalizedRecord) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
coordinates[symbolId] = normalizedRecord;
|
|
53
|
+
}
|
|
54
|
+
return { coordinates };
|
|
55
|
+
}
|
|
56
|
+
// implements REQ-core-extractors
|
|
57
|
+
export function writeCoordinateArtifact(coordinates) {
|
|
58
|
+
const artifact = {
|
|
59
|
+
coordinates: sortCoordinates(coordinates),
|
|
60
|
+
};
|
|
61
|
+
return `${SYMBOL_COORDINATES_COMMENT_BLOCK}${dumpYAML(artifact, {
|
|
62
|
+
lineWidth: -1,
|
|
63
|
+
noRefs: true,
|
|
64
|
+
sortKeys: true,
|
|
65
|
+
})}`;
|
|
66
|
+
}
|
|
67
|
+
// implements REQ-core-extractors
|
|
68
|
+
export function mergeCoordinatesWithManifest(symbolRecords, coordinateArtifact) {
|
|
69
|
+
const coordinates = coordinateArtifact?.coordinates ?? {};
|
|
70
|
+
return symbolRecords.map((symbolRecord) => {
|
|
71
|
+
const legacyRecord = { ...symbolRecord };
|
|
72
|
+
const symbolId = typeof symbolRecord.id === "string" ? symbolRecord.id : undefined;
|
|
73
|
+
const coordinateRecord = symbolId ? coordinates[symbolId] : undefined;
|
|
74
|
+
if (!coordinateRecord) {
|
|
75
|
+
return legacyRecord;
|
|
76
|
+
}
|
|
77
|
+
const { coordinatesGeneratedAt: _coordinatesGeneratedAt, ...mergedRecord } = legacyRecord;
|
|
78
|
+
return {
|
|
79
|
+
...mergedRecord,
|
|
80
|
+
...coordinateRecord,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { extractFromManifest, extractFromManifestString, type ExtractionResult, type ManifestError, } from "../../extractors/manifest.js";
|
|
1
|
+
export { extractFromManifest, extractFromManifestString, readManifestWithCoordinateOverlay, type ExtractionResult, type ManifestError, type ManifestSymbolRecord, } from "../../extractors/manifest.js";
|
|
2
2
|
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/public/extractors/manifest.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,KAAK,gBAAgB,EACrB,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/public/extractors/manifest.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,iCAAiC,EACjC,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,oBAAoB,GAC1B,MAAM,8BAA8B,CAAC"}
|
|
@@ -15,4 +15,4 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
export { extractFromManifest, extractFromManifestString, } from "../../extractors/manifest.js";
|
|
18
|
+
export { extractFromManifest, extractFromManifestString, readManifestWithCoordinateOverlay, } from "../../extractors/manifest.js";
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable contract for staged Kibi impact evidence.
|
|
3
|
+
*
|
|
4
|
+
* This module is intentionally explicit and non-heuristic. Upstream staged-file
|
|
5
|
+
* analysis decides whether a source edit is behavior-changing; this contract only
|
|
6
|
+
* records that decision and the staged KB artifacts that cover it.
|
|
7
|
+
*/
|
|
8
|
+
/** User-facing KB schema documentation cited by staged diagnostics. */
|
|
9
|
+
export declare const KIBI_ENTITY_SCHEMA_DOC = "docs/entity-schema.md";
|
|
10
|
+
/** Canonical symbols manifest path used by staged traceability enforcement. */
|
|
11
|
+
export declare const KIBI_SYMBOLS_MANIFEST_PATH = "documentation/symbols.yaml";
|
|
12
|
+
/** Canonical symbol coordinates artifact used by staged traceability enforcement. */
|
|
13
|
+
export declare const KIBI_SYMBOL_COORDINATES_PATH = "documentation/symbol-coordinates.yaml";
|
|
14
|
+
/** Explicit declaration string for audited no-impact overrides. */
|
|
15
|
+
export declare const KIBI_NO_IMPACT_DECLARATION = "Kibi-Impact: none";
|
|
16
|
+
/** Canonical Kibi entity types that can provide staged evidence. */
|
|
17
|
+
export type KibiEntityType = "req" | "scenario" | "test" | "adr" | "flag" | "event" | "symbol" | "fact";
|
|
18
|
+
/**
|
|
19
|
+
* Explicit source-edit classification from upstream staged analysis.
|
|
20
|
+
*
|
|
21
|
+
* - `behavior_source_edit`: a supported staged source change already classified
|
|
22
|
+
* as behavior-changing or traceability-relevant by the caller.
|
|
23
|
+
* - `non_behavior_source_edit`: a supported staged source change that remains in
|
|
24
|
+
* scope for auditing but does not require KB changes.
|
|
25
|
+
*/
|
|
26
|
+
export type SourceChangeKind = "behavior_source_edit" | "non_behavior_source_edit";
|
|
27
|
+
/** One staged source file participating in Kibi impact evaluation. */
|
|
28
|
+
export interface KibiImpactSourceChange {
|
|
29
|
+
/** Repo-relative staged source path. */
|
|
30
|
+
path: string;
|
|
31
|
+
/** Explicit upstream classification; this module does not infer it. */
|
|
32
|
+
kind: SourceChangeKind;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Staged KB markdown evidence linked to one or more staged source files.
|
|
36
|
+
*
|
|
37
|
+
* Evidence is explicit only when the staged artifact names concrete KB entities
|
|
38
|
+
* and lists the staged source paths it is intended to cover.
|
|
39
|
+
*/
|
|
40
|
+
export interface KibiImpactKbArtifact {
|
|
41
|
+
/** Artifact category. Kept narrow to avoid heuristic interpretation. */
|
|
42
|
+
kind: "entity_markdown" | "symbols_manifest";
|
|
43
|
+
/** Repo-relative staged KB artifact path. */
|
|
44
|
+
path: string;
|
|
45
|
+
/** Canonical Kibi entity types present in the staged artifact. */
|
|
46
|
+
entityTypes: KibiEntityType[];
|
|
47
|
+
/** Concrete KB entities updated by the staged artifact. */
|
|
48
|
+
entityIds: string[];
|
|
49
|
+
/** Repo-relative source paths explicitly covered by this artifact. */
|
|
50
|
+
sourcePaths: string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Deterministic symbol coordinates artifact state for the staged change-set.
|
|
54
|
+
*
|
|
55
|
+
* - `not_required`: symbol extraction output did not change for the listed
|
|
56
|
+
* staged source paths.
|
|
57
|
+
* - `fresh`: a staged coordinate refresh covers the listed source paths.
|
|
58
|
+
* - `stale`: coordinate artifact content is reverted, outdated, or otherwise does not match
|
|
59
|
+
* the staged symbol extraction result.
|
|
60
|
+
* - `missing`: a refresh is required but no staged coordinate artifact exists.
|
|
61
|
+
*/
|
|
62
|
+
export interface KibiImpactSymbolsManifest {
|
|
63
|
+
/** Canonical repo-relative symbol coordinate artifact path. */
|
|
64
|
+
path: string;
|
|
65
|
+
/** Explicit manifest freshness state. */
|
|
66
|
+
state: "not_required" | "fresh" | "stale" | "missing";
|
|
67
|
+
/** Repo-relative staged source paths whose symbol output this state describes. */
|
|
68
|
+
sourcePaths: string[];
|
|
69
|
+
}
|
|
70
|
+
/** Supported audited reasons for a no-impact override. */
|
|
71
|
+
export type KibiNoImpactReason = "false_positive" | "non_behavioral_source_edit";
|
|
72
|
+
/**
|
|
73
|
+
* Explicit no-impact override record.
|
|
74
|
+
*
|
|
75
|
+
* Overrides are only valid for non-behavioral edits or classifier false
|
|
76
|
+
* positives. They never satisfy real behavior-changing source edits.
|
|
77
|
+
*/
|
|
78
|
+
export interface KibiNoImpactOverride {
|
|
79
|
+
/** Required literal declaration. */
|
|
80
|
+
declaration: typeof KIBI_NO_IMPACT_DECLARATION;
|
|
81
|
+
/** Repo-relative staged record path carrying the override. */
|
|
82
|
+
path: string;
|
|
83
|
+
/** Repo-relative source paths covered by the override record. */
|
|
84
|
+
sourcePaths: string[];
|
|
85
|
+
/** Audited reason for allowing the override. */
|
|
86
|
+
reason: KibiNoImpactReason;
|
|
87
|
+
/** Human-readable justification stored with the override record. */
|
|
88
|
+
rationale: string;
|
|
89
|
+
}
|
|
90
|
+
/** Explicit evidence state: staged KB artifacts are present. */
|
|
91
|
+
export interface KibiImpactKbChangesMode {
|
|
92
|
+
kind: "kb_changes";
|
|
93
|
+
/** Staged KB markdown artifacts linked to staged source changes. */
|
|
94
|
+
kbArtifacts: KibiImpactKbArtifact[];
|
|
95
|
+
}
|
|
96
|
+
/** Explicit evidence state: a staged no-impact override is being used. */
|
|
97
|
+
export interface KibiImpactNoImpactOverrideMode {
|
|
98
|
+
kind: "no_impact_override";
|
|
99
|
+
/** Staged override record for false positives or non-behavioral edits. */
|
|
100
|
+
override: KibiNoImpactOverride;
|
|
101
|
+
}
|
|
102
|
+
/** Explicit evidence state: no staged KB evidence or override exists. */
|
|
103
|
+
export interface KibiImpactMissingMode {
|
|
104
|
+
kind: "missing";
|
|
105
|
+
}
|
|
106
|
+
/** Discriminated union describing how the staged change-set is justified. */
|
|
107
|
+
export type KibiImpactMode = KibiImpactKbChangesMode | KibiImpactNoImpactOverrideMode | KibiImpactMissingMode;
|
|
108
|
+
/**
|
|
109
|
+
* Full evidence snapshot for staged Kibi impact enforcement.
|
|
110
|
+
*
|
|
111
|
+
* `sourceChanges` is always required so diagnostics can cite exact staged source
|
|
112
|
+
* files. `mode` captures whether those files are backed by KB changes, by an
|
|
113
|
+
* audited no-impact override, or by nothing. `symbolsManifest` records whether a
|
|
114
|
+
* staged manifest refresh is part of that evidence.
|
|
115
|
+
*/
|
|
116
|
+
export interface KibiImpactEvidence {
|
|
117
|
+
/** All staged source files in scope for Kibi impact enforcement. */
|
|
118
|
+
sourceChanges: KibiImpactSourceChange[];
|
|
119
|
+
/** Deterministic staged symbols manifest state for the same change-set. */
|
|
120
|
+
symbolsManifest: KibiImpactSymbolsManifest;
|
|
121
|
+
/** Explicit evidence mode for the staged change-set. */
|
|
122
|
+
mode: KibiImpactMode;
|
|
123
|
+
}
|
|
124
|
+
/** Returns staged behavior-changing source paths only. */
|
|
125
|
+
export declare function getBehaviorSourcePaths(evidence: KibiImpactEvidence): string[];
|
|
126
|
+
/** Returns staged source paths covered by explicit KB artifacts. */
|
|
127
|
+
export declare function getKbCoveredSourcePaths(evidence: KibiImpactEvidence): string[];
|
|
128
|
+
/** Returns staged source paths covered by a fresh symbol coordinate refresh. */
|
|
129
|
+
export declare function getFreshSymbolsManifestSourcePaths(evidence: KibiImpactEvidence): string[];
|
|
130
|
+
/**
|
|
131
|
+
* Returns all staged evidence file paths that a later CLI integration can cite
|
|
132
|
+
* in diagnostics or logs.
|
|
133
|
+
*/
|
|
134
|
+
export declare function getKbEvidencePaths(evidence: KibiImpactEvidence): string[];
|
|
135
|
+
/** True when a staged no-impact override includes a non-empty rationale. */
|
|
136
|
+
export declare function hasOverrideRationale(evidence: KibiImpactEvidence): boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Returns behavior-changing staged source files that still lack valid Kibi
|
|
139
|
+
* impact evidence.
|
|
140
|
+
*/
|
|
141
|
+
export declare function getMissingBehaviorSourcePaths(evidence: KibiImpactEvidence): string[];
|
|
142
|
+
//# sourceMappingURL=evidence-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence-model.d.ts","sourceRoot":"","sources":["../../src/traceability/evidence-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable contract for staged Kibi impact evidence.
|
|
3
|
+
*
|
|
4
|
+
* This module is intentionally explicit and non-heuristic. Upstream staged-file
|
|
5
|
+
* analysis decides whether a source edit is behavior-changing; this contract only
|
|
6
|
+
* records that decision and the staged KB artifacts that cover it.
|
|
7
|
+
*/
|
|
8
|
+
/** User-facing KB schema documentation cited by staged diagnostics. */
|
|
9
|
+
export const KIBI_ENTITY_SCHEMA_DOC = "docs/entity-schema.md";
|
|
10
|
+
/** Canonical symbols manifest path used by staged traceability enforcement. */
|
|
11
|
+
export const KIBI_SYMBOLS_MANIFEST_PATH = "documentation/symbols.yaml";
|
|
12
|
+
/** Canonical symbol coordinates artifact used by staged traceability enforcement. */
|
|
13
|
+
export const KIBI_SYMBOL_COORDINATES_PATH = "documentation/symbol-coordinates.yaml";
|
|
14
|
+
/** Explicit declaration string for audited no-impact overrides. */
|
|
15
|
+
export const KIBI_NO_IMPACT_DECLARATION = "Kibi-Impact: none";
|
|
16
|
+
function uniqueSorted(values) {
|
|
17
|
+
return Array.from(new Set(values)).sort();
|
|
18
|
+
}
|
|
19
|
+
/** Returns staged behavior-changing source paths only. */
|
|
20
|
+
export function getBehaviorSourcePaths(evidence) {
|
|
21
|
+
return evidence.sourceChanges
|
|
22
|
+
.filter((change) => change.kind === "behavior_source_edit")
|
|
23
|
+
.map((change) => change.path)
|
|
24
|
+
.sort();
|
|
25
|
+
}
|
|
26
|
+
/** Returns staged source paths covered by explicit KB artifacts. */
|
|
27
|
+
export function getKbCoveredSourcePaths(evidence) {
|
|
28
|
+
if (evidence.mode.kind !== "kb_changes") {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return uniqueSorted(evidence.mode.kbArtifacts.flatMap((artifact) => artifact.sourcePaths));
|
|
32
|
+
}
|
|
33
|
+
/** Returns staged source paths covered by a fresh symbol coordinate refresh. */
|
|
34
|
+
export function getFreshSymbolsManifestSourcePaths(evidence) {
|
|
35
|
+
if (evidence.symbolsManifest.state !== "fresh") {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
return [...evidence.symbolsManifest.sourcePaths].sort();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns all staged evidence file paths that a later CLI integration can cite
|
|
42
|
+
* in diagnostics or logs.
|
|
43
|
+
*/
|
|
44
|
+
export function getKbEvidencePaths(evidence) {
|
|
45
|
+
const paths = [];
|
|
46
|
+
if (evidence.mode.kind === "kb_changes") {
|
|
47
|
+
paths.push(...evidence.mode.kbArtifacts.map((artifact) => artifact.path));
|
|
48
|
+
}
|
|
49
|
+
if (evidence.symbolsManifest.state === "fresh") {
|
|
50
|
+
paths.push(evidence.symbolsManifest.path);
|
|
51
|
+
}
|
|
52
|
+
return uniqueSorted(paths);
|
|
53
|
+
}
|
|
54
|
+
/** True when a staged no-impact override includes a non-empty rationale. */
|
|
55
|
+
export function hasOverrideRationale(evidence) {
|
|
56
|
+
return (evidence.mode.kind === "no_impact_override" &&
|
|
57
|
+
evidence.mode.override.rationale.trim().length > 0);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returns behavior-changing staged source files that still lack valid Kibi
|
|
61
|
+
* impact evidence.
|
|
62
|
+
*/
|
|
63
|
+
export function getMissingBehaviorSourcePaths(evidence) {
|
|
64
|
+
const behaviorPaths = getBehaviorSourcePaths(evidence);
|
|
65
|
+
const coveredPaths = new Set([
|
|
66
|
+
...getKbCoveredSourcePaths(evidence),
|
|
67
|
+
...getFreshSymbolsManifestSourcePaths(evidence),
|
|
68
|
+
]);
|
|
69
|
+
return behaviorPaths.filter((path) => !coveredPaths.has(path));
|
|
70
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-staged.d.ts","sourceRoot":"","sources":["../../src/traceability/git-staged.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"git-staged.d.ts","sourceRoot":"","sources":["../../src/traceability/git-staged.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CAAC;AAalE;;GAEG;AAEH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAwB5C;AAsED;;GAEG;AACH,wBAAgB,kBAAkB,CAEhC,QAAQ,EAAE,MAAM,EAChB,SAAS,UAAQ,GAChB,SAAS,EAAE,CAuBb;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,IAAI,GAAE,MAAiB,GAAG,UAAU,EAAE,CAwGpE;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
2
|
import { isCliTraceOrDebugEnabled } from "../env.js";
|
|
3
|
+
import { loadConfig } from "../utils/config.js";
|
|
3
4
|
function runGit(cmd, exec) {
|
|
4
5
|
try {
|
|
5
6
|
return exec(cmd, { encoding: "utf8" });
|
|
@@ -47,8 +48,20 @@ const SUPPORTED_EXT = new Set([
|
|
|
47
48
|
".mjs",
|
|
48
49
|
".cjs",
|
|
49
50
|
]);
|
|
50
|
-
const SUPPORTED_MANIFEST = new Set([
|
|
51
|
-
|
|
51
|
+
const SUPPORTED_MANIFEST = new Set([
|
|
52
|
+
"symbols.yaml",
|
|
53
|
+
"symbols.yml",
|
|
54
|
+
"symbol-coordinates.yaml",
|
|
55
|
+
]);
|
|
56
|
+
const ENTITY_MARKDOWN_DIRS = [
|
|
57
|
+
"/requirements/",
|
|
58
|
+
"/scenarios/",
|
|
59
|
+
"/tests/",
|
|
60
|
+
"/facts/",
|
|
61
|
+
"/adr/",
|
|
62
|
+
"/flags/",
|
|
63
|
+
"/events/",
|
|
64
|
+
];
|
|
52
65
|
function shouldLogTraceDebug() {
|
|
53
66
|
return isCliTraceOrDebugEnabled();
|
|
54
67
|
}
|
|
@@ -72,13 +85,24 @@ function isEntityMarkdown(p) {
|
|
|
72
85
|
return false;
|
|
73
86
|
}
|
|
74
87
|
function isManifestFile(p) {
|
|
75
|
-
const base = p.split(/[
|
|
88
|
+
const base = p.split(/[\\/]/).pop();
|
|
76
89
|
if (!base)
|
|
77
90
|
return false;
|
|
78
91
|
for (const name of SUPPORTED_MANIFEST) {
|
|
79
92
|
if (base === name)
|
|
80
93
|
return true;
|
|
81
94
|
}
|
|
95
|
+
try {
|
|
96
|
+
const config = loadConfig(process.cwd());
|
|
97
|
+
if (config.paths.symbols) {
|
|
98
|
+
const configuredBase = config.paths.symbols.split(/[\\/]/).pop();
|
|
99
|
+
if (configuredBase && base === configuredBase)
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// ignore config read errors
|
|
105
|
+
}
|
|
82
106
|
return false;
|
|
83
107
|
}
|
|
84
108
|
/**
|
|
@@ -198,6 +222,7 @@ export function getStagedFiles(exec = execSync) {
|
|
|
198
222
|
status,
|
|
199
223
|
...(oldPath !== undefined ? { oldPath } : {}),
|
|
200
224
|
hunkRanges,
|
|
225
|
+
diffText,
|
|
201
226
|
content,
|
|
202
227
|
});
|
|
203
228
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type KibiImpactEvidence } from "./evidence-model.js";
|
|
2
|
+
export type KibiImpactDiagnosticId = "kibi_impact_evidence_missing" | "symbols_manifest_stale" | "kibi_impact_override_missing_rationale";
|
|
3
|
+
export interface KibiImpactDiagnostic {
|
|
4
|
+
/** Stable staged-enforcement diagnostic identifier. */
|
|
5
|
+
id: KibiImpactDiagnosticId;
|
|
6
|
+
/** Hard-gate severity for staged enforcement. */
|
|
7
|
+
severity: "error";
|
|
8
|
+
/** Repo-relative files that explain why the diagnostic fired. */
|
|
9
|
+
files: string[];
|
|
10
|
+
/** User-facing docs that explain the policy. */
|
|
11
|
+
docs: string[];
|
|
12
|
+
/** Exact CLI-facing diagnostic message. */
|
|
13
|
+
message: string;
|
|
14
|
+
/** Deterministic remediation guidance. */
|
|
15
|
+
suggestion: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Collects deterministic staged diagnostics for Kibi impact enforcement.
|
|
19
|
+
*
|
|
20
|
+
* This function assumes upstream staged analysis has already classified source
|
|
21
|
+
* files and manifest freshness. It only evaluates explicit predicates recorded in
|
|
22
|
+
* `KibiImpactEvidence`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function collectStagedKibiDiagnostics(evidence: KibiImpactEvidence): KibiImpactDiagnostic[];
|
|
25
|
+
//# sourceMappingURL=staged-diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staged-diagnostics.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,sBAAsB,GAC9B,8BAA8B,GAC9B,wBAAwB,GACxB,wCAAwC,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,EAAE,EAAE,sBAAsB,CAAC;IAC3B,iDAAiD;IACjD,QAAQ,EAAE,OAAO,CAAC;IAClB,iEAAiE;IACjE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gDAAgD;IAChD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;CACpB;AAsDD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,kBAAkB,GAC3B,oBAAoB,EAAE,CAoCxB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { KIBI_ENTITY_SCHEMA_DOC, KIBI_SYMBOL_COORDINATES_PATH, KIBI_SYMBOLS_MANIFEST_PATH, getBehaviorSourcePaths, getMissingBehaviorSourcePaths, hasOverrideRationale, } from "./evidence-model.js";
|
|
2
|
+
function formatFileList(paths) {
|
|
3
|
+
return paths.join(", ");
|
|
4
|
+
}
|
|
5
|
+
function createMissingEvidenceDiagnostic(paths) {
|
|
6
|
+
return {
|
|
7
|
+
id: "kibi_impact_evidence_missing",
|
|
8
|
+
severity: "error",
|
|
9
|
+
files: [...paths],
|
|
10
|
+
docs: [KIBI_ENTITY_SCHEMA_DOC],
|
|
11
|
+
message: `Behavior-changing staged files are missing Kibi impact evidence (see ${KIBI_ENTITY_SCHEMA_DOC}): ${formatFileList(paths)}`,
|
|
12
|
+
suggestion: `Query Kibi via MCP before deciding, then stage requirement/scenario/test/fact/symbol markdown evidence, staged authored ${KIBI_SYMBOLS_MANIFEST_PATH} metadata, or refreshed ${KIBI_SYMBOL_COORDINATES_PATH}. Re-run kibi check --staged after staging the evidence.`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function createSymbolsManifestStaleDiagnostic(paths) {
|
|
16
|
+
return {
|
|
17
|
+
id: "symbols_manifest_stale",
|
|
18
|
+
severity: "error",
|
|
19
|
+
files: [KIBI_SYMBOL_COORDINATES_PATH, ...paths],
|
|
20
|
+
docs: [KIBI_ENTITY_SCHEMA_DOC],
|
|
21
|
+
message: `${KIBI_SYMBOL_COORDINATES_PATH} is stale or missing for staged source files: ${formatFileList(paths)}`,
|
|
22
|
+
suggestion: `Run kibi sync --refresh-symbol-coordinates && git add ${KIBI_SYMBOL_COORDINATES_PATH} ${KIBI_SYMBOLS_MANIFEST_PATH}, then re-run kibi check --staged.`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function createMissingOverrideRationaleDiagnostic(evidence) {
|
|
26
|
+
if (evidence.mode.kind !== "no_impact_override") {
|
|
27
|
+
throw new Error("Override rationale diagnostic requires a no-impact override");
|
|
28
|
+
}
|
|
29
|
+
const paths = [...evidence.mode.override.sourcePaths].sort();
|
|
30
|
+
return {
|
|
31
|
+
id: "kibi_impact_override_missing_rationale",
|
|
32
|
+
severity: "error",
|
|
33
|
+
files: [evidence.mode.override.path, ...paths],
|
|
34
|
+
docs: [KIBI_ENTITY_SCHEMA_DOC],
|
|
35
|
+
message: `Kibi-Impact: none override is missing rationale for staged source files: ${formatFileList(paths)}`,
|
|
36
|
+
suggestion: "Add a non-empty rationale in the same staged override record, keep overrides limited to false positives or non-behavioral source edits, and re-run kibi check --staged.",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Collects deterministic staged diagnostics for Kibi impact enforcement.
|
|
41
|
+
*
|
|
42
|
+
* This function assumes upstream staged analysis has already classified source
|
|
43
|
+
* files and manifest freshness. It only evaluates explicit predicates recorded in
|
|
44
|
+
* `KibiImpactEvidence`.
|
|
45
|
+
*/
|
|
46
|
+
export function collectStagedKibiDiagnostics(evidence) {
|
|
47
|
+
const diagnostics = [];
|
|
48
|
+
if (evidence.mode.kind === "no_impact_override" &&
|
|
49
|
+
!hasOverrideRationale(evidence)) {
|
|
50
|
+
diagnostics.push(createMissingOverrideRationaleDiagnostic(evidence));
|
|
51
|
+
}
|
|
52
|
+
if ((evidence.symbolsManifest.state === "stale" ||
|
|
53
|
+
evidence.symbolsManifest.state === "missing") &&
|
|
54
|
+
evidence.symbolsManifest.sourcePaths.length > 0) {
|
|
55
|
+
diagnostics.push(createSymbolsManifestStaleDiagnostic([...evidence.symbolsManifest.sourcePaths].sort()));
|
|
56
|
+
}
|
|
57
|
+
const missingBehaviorPaths = getMissingBehaviorSourcePaths(evidence);
|
|
58
|
+
if (missingBehaviorPaths.length > 0) {
|
|
59
|
+
diagnostics.push(createMissingEvidenceDiagnostic(missingBehaviorPaths));
|
|
60
|
+
}
|
|
61
|
+
if (evidence.mode.kind === "no_impact_override" &&
|
|
62
|
+
evidence.mode.override.sourcePaths.length === 0 &&
|
|
63
|
+
getBehaviorSourcePaths(evidence).length === 0) {
|
|
64
|
+
return diagnostics;
|
|
65
|
+
}
|
|
66
|
+
return diagnostics;
|
|
67
|
+
}
|