kibi-cli 0.8.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.d.ts.map +1 -1
- package/dist/cli.js +11 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +229 -4
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +11 -14
- package/dist/commands/migrate.d.ts +9 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +183 -0
- 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/check-types.d.ts +3 -1
- package/dist/public/check-types.d.ts.map +1 -1
- package/dist/public/check-types.js +1 -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/public/ignore-policy.d.ts +10 -0
- package/dist/public/ignore-policy.d.ts.map +1 -0
- package/dist/public/ignore-policy.js +219 -0
- package/dist/public/schema-version.d.ts +3 -0
- package/dist/public/schema-version.d.ts.map +1 -0
- package/dist/public/schema-version.js +1 -0
- 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/config.d.ts +1 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +35 -22
- 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/dist/utils/rule-registry.d.ts.map +1 -1
- package/dist/utils/rule-registry.js +6 -0
- package/dist/utils/schema-version.d.ts +14 -0
- package/dist/utils/schema-version.d.ts.map +1 -0
- package/dist/utils/schema-version.js +59 -0
- package/dist/utils/strict-modeling.d.ts +64 -0
- package/dist/utils/strict-modeling.d.ts.map +1 -0
- package/dist/utils/strict-modeling.js +371 -0
- package/package.json +13 -3
- package/schema/config.json +8 -1
- package/src/public/check-types.ts +15 -1
- package/src/public/extractors/manifest.ts +2 -0
- package/src/public/ignore-policy.ts +229 -0
- package/src/public/schema-version.ts +6 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import ignore from "ignore";
|
|
4
|
+
const HARD_DENYLIST = [
|
|
5
|
+
".kb",
|
|
6
|
+
".git",
|
|
7
|
+
"node_modules",
|
|
8
|
+
"vendor",
|
|
9
|
+
"third_party",
|
|
10
|
+
".sisyphus",
|
|
11
|
+
".opencode",
|
|
12
|
+
];
|
|
13
|
+
function readIgnoreFileLines(filePath) {
|
|
14
|
+
if (!existsSync(filePath))
|
|
15
|
+
return [];
|
|
16
|
+
try {
|
|
17
|
+
const content = readFileSync(filePath, "utf8");
|
|
18
|
+
return content
|
|
19
|
+
.split(/\r?\n/)
|
|
20
|
+
.map((l) => l.trim())
|
|
21
|
+
.filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function toPosix(p) {
|
|
28
|
+
return p.split(path.sep).join("/");
|
|
29
|
+
}
|
|
30
|
+
// implements REQ-001
|
|
31
|
+
export function createRepoIgnorePolicy(workspaceRoot) {
|
|
32
|
+
const root = path.resolve(workspaceRoot);
|
|
33
|
+
// Load root .gitignore
|
|
34
|
+
const rootGitignorePath = path.join(root, ".gitignore");
|
|
35
|
+
const rootGitPatterns = readIgnoreFileLines(rootGitignorePath);
|
|
36
|
+
// Load .git/info/exclude
|
|
37
|
+
const gitInfoExcludePath = path.join(root, ".git", "info", "exclude");
|
|
38
|
+
const gitInfoPatterns = readIgnoreFileLines(gitInfoExcludePath);
|
|
39
|
+
// Find nested .gitignore files (skip scanning inside hard denylist directories)
|
|
40
|
+
const nestedPatterns = new Map();
|
|
41
|
+
function walk(dirAbs) {
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = readdirSync(dirAbs, { withFileTypes: true });
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const ent of entries) {
|
|
50
|
+
const name = String(ent.name);
|
|
51
|
+
const abs = path.join(dirAbs, name);
|
|
52
|
+
if (ent.isDirectory()) {
|
|
53
|
+
// avoid descending into common heavy or control directories
|
|
54
|
+
if (HARD_DENYLIST.includes(name))
|
|
55
|
+
continue;
|
|
56
|
+
// also avoid .git itself to prevent reading internal excludes as nested
|
|
57
|
+
if (name === ".git")
|
|
58
|
+
continue;
|
|
59
|
+
walk(abs);
|
|
60
|
+
}
|
|
61
|
+
else if (ent.isFile()) {
|
|
62
|
+
if (name === ".gitignore") {
|
|
63
|
+
// skip root .gitignore (we already loaded it)
|
|
64
|
+
if (path.resolve(dirAbs) === root)
|
|
65
|
+
continue;
|
|
66
|
+
const patterns = readIgnoreFileLines(abs);
|
|
67
|
+
const relDir = path.relative(root, dirAbs) || ".";
|
|
68
|
+
nestedPatterns.set(toPosix(relDir), patterns);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
walk(root);
|
|
74
|
+
// Create ignore instances
|
|
75
|
+
const rootIgnore = ignore();
|
|
76
|
+
if (rootGitPatterns.length > 0)
|
|
77
|
+
rootIgnore.add(rootGitPatterns);
|
|
78
|
+
const gitInfoIgnore = ignore();
|
|
79
|
+
if (gitInfoPatterns.length > 0)
|
|
80
|
+
gitInfoIgnore.add(gitInfoPatterns);
|
|
81
|
+
const nestedIgnoreMap = new Map();
|
|
82
|
+
for (const [dirRel, pats] of nestedPatterns.entries()) {
|
|
83
|
+
const ig = ignore();
|
|
84
|
+
if (pats.length > 0)
|
|
85
|
+
ig.add(pats);
|
|
86
|
+
nestedIgnoreMap.set(dirRel, ig);
|
|
87
|
+
}
|
|
88
|
+
// Prepare nested directories sorted by specificity (longest first)
|
|
89
|
+
const nestedDirsSorted = Array.from(nestedIgnoreMap.keys()).sort((a, b) => b.length - a.length);
|
|
90
|
+
function isPathOutsideWorkspace(absPath) {
|
|
91
|
+
const rel = path.relative(root, absPath);
|
|
92
|
+
// path.relative returns paths starting with '..' for outside
|
|
93
|
+
return rel === "" ? false : rel.split(path.sep)[0] === "..";
|
|
94
|
+
}
|
|
95
|
+
function matchesHardDeny(relPosix) {
|
|
96
|
+
const segments = relPosix.split("/").filter(Boolean);
|
|
97
|
+
for (const deny of HARD_DENYLIST) {
|
|
98
|
+
if (segments.includes(deny))
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
function isIgnoredInternal(inputPath) {
|
|
104
|
+
// Resolve to absolute and relative path inside workspace
|
|
105
|
+
const abs = path.isAbsolute(inputPath) ? path.resolve(inputPath) : path.resolve(root, inputPath);
|
|
106
|
+
if (path.isAbsolute(inputPath) && isPathOutsideWorkspace(abs)) {
|
|
107
|
+
return { ignored: true, reason: "outside_workspace" };
|
|
108
|
+
}
|
|
109
|
+
const rel = path.relative(root, abs) || ".";
|
|
110
|
+
const relPosix = toPosix(rel);
|
|
111
|
+
// Hard denylist always wins
|
|
112
|
+
if (matchesHardDeny(relPosix))
|
|
113
|
+
return { ignored: true, reason: "hard_deny" };
|
|
114
|
+
// Root .gitignore
|
|
115
|
+
try {
|
|
116
|
+
if (rootGitPatterns.length > 0 && rootIgnore.ignores(relPosix)) {
|
|
117
|
+
return { ignored: true, reason: "gitignored" };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
// ignore errors from library usage; continue
|
|
122
|
+
}
|
|
123
|
+
// .git/info/exclude
|
|
124
|
+
try {
|
|
125
|
+
if (gitInfoPatterns.length > 0 && gitInfoIgnore.ignores(relPosix)) {
|
|
126
|
+
return { ignored: true, reason: "git_info_exclude" };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
// noop
|
|
131
|
+
}
|
|
132
|
+
// Nested .gitignore (apply relative to their directory)
|
|
133
|
+
for (const dirRel of nestedDirsSorted) {
|
|
134
|
+
// dirRel is '.' for nested at root which we skipped, so dirRel will be like 'docs'
|
|
135
|
+
if (dirRel === ".")
|
|
136
|
+
continue;
|
|
137
|
+
if (relPosix === dirRel || relPosix.startsWith(dirRel + "/")) {
|
|
138
|
+
const sub = relPosix === dirRel ? "." : relPosix.slice(dirRel.length + 1);
|
|
139
|
+
const ig = nestedIgnoreMap.get(dirRel);
|
|
140
|
+
try {
|
|
141
|
+
if (ig && ig.ignores(sub))
|
|
142
|
+
return { ignored: true, reason: "gitignored" };
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
// noop
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { ignored: false };
|
|
150
|
+
}
|
|
151
|
+
function getFastGlobIgnoreGlobs() {
|
|
152
|
+
const globs = [];
|
|
153
|
+
// Hard denylist globs
|
|
154
|
+
for (const d of HARD_DENYLIST) {
|
|
155
|
+
// match directory and its contents anywhere
|
|
156
|
+
globs.push(`**/${d}/**`);
|
|
157
|
+
globs.push(`**/${d}`);
|
|
158
|
+
}
|
|
159
|
+
// Root .gitignore patterns (convert to simple globs)
|
|
160
|
+
for (const p of rootGitPatterns) {
|
|
161
|
+
if (!p || p.startsWith("#") || p.startsWith("!"))
|
|
162
|
+
continue;
|
|
163
|
+
let pat = p;
|
|
164
|
+
if (pat.startsWith("/"))
|
|
165
|
+
pat = pat.slice(1);
|
|
166
|
+
if (pat.includes("/")) {
|
|
167
|
+
// anchored path
|
|
168
|
+
globs.push(`**/${toPosix(pat)}`);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
globs.push(`**/${pat}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// .git/info/exclude patterns
|
|
175
|
+
for (const p of gitInfoPatterns) {
|
|
176
|
+
if (!p || p.startsWith("#") || p.startsWith("!"))
|
|
177
|
+
continue;
|
|
178
|
+
let pat = p;
|
|
179
|
+
if (pat.startsWith("/"))
|
|
180
|
+
pat = pat.slice(1);
|
|
181
|
+
if (pat.includes("/")) {
|
|
182
|
+
globs.push(`**/${toPosix(pat)}`);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
globs.push(`**/${pat}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Nested .gitignore patterns - prefix with directory path
|
|
189
|
+
// Use the raw patterns collected in nestedPatterns so we scope patterns
|
|
190
|
+
// to the nested directory instead of ignoring the entire directory.
|
|
191
|
+
// Debug: print nested patterns and the computed globs to help diagnosing test failures.
|
|
192
|
+
for (const [dirRel, patterns] of nestedPatterns.entries()) {
|
|
193
|
+
for (const p of patterns) {
|
|
194
|
+
if (!p || p.startsWith("#") || p.startsWith("!"))
|
|
195
|
+
continue;
|
|
196
|
+
let pat = p;
|
|
197
|
+
if (pat.startsWith("/"))
|
|
198
|
+
pat = pat.slice(1);
|
|
199
|
+
const prefix = dirRel === "." ? "" : `${dirRel}/`;
|
|
200
|
+
if (pat.includes("/")) {
|
|
201
|
+
globs.push(`**/${prefix}${toPosix(pat)}`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
globs.push(`**/${prefix}${pat}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return Array.from(new Set(globs));
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
isIgnored(inputPath) {
|
|
212
|
+
return isIgnoredInternal(inputPath).ignored;
|
|
213
|
+
},
|
|
214
|
+
getFastGlobIgnoreGlobs,
|
|
215
|
+
explain(inputPath) {
|
|
216
|
+
return isIgnoredInternal(inputPath);
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-version.d.ts","sourceRoot":"","sources":["../../src/public/schema-version.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LATEST_KB_SCHEMA_VERSION, getSchemaVersionStatus, normalizeSchemaVersion, } from "../utils/schema-version.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
|
+
}
|