@shrkcrft/quality-gates 0.1.0-alpha.25 → 0.1.0-alpha.26
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arch-gate.d.ts","sourceRoot":"","sources":["../../src/gates/arch-gate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"arch-gate.d.ts","sourceRoot":"","sources":["../../src/gates/arch-gate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,WAAW,CAyKzF"}
|
package/dist/gates/arch-gate.js
CHANGED
|
@@ -39,16 +39,49 @@ export function archGate(projectRoot, options = {}) {
|
|
|
39
39
|
const delta = diffSnapshots(baseline, current);
|
|
40
40
|
const newIds = new Set(delta.newViolationIds);
|
|
41
41
|
const newViolations = report.violations.filter((v) => newIds.has(violationId(v)));
|
|
42
|
-
const newErrors = newViolations.filter((v) => v.severity === 'error').length;
|
|
43
|
-
const newWarnings = newViolations.filter((v) => v.severity === 'warning').length;
|
|
44
42
|
const baselineErrors = baseline.countsBySeverity.error;
|
|
43
|
+
// §3.1 — change-scoped attribution. "NEW since baseline" is a drift measure
|
|
44
|
+
// against a frozen (possibly months-old) snapshot; on its own it red-fails
|
|
45
|
+
// on structural debt the current change never touched, training the agent to
|
|
46
|
+
// ignore the gate. Redefine the BLOCKING set as the intersection of the
|
|
47
|
+
// baseline-new violations with the files the working diff actually touched
|
|
48
|
+
// (diff vs HEAD). A NEW violation whose origin file is NOT in the changed set
|
|
49
|
+
// becomes INFORMATIONAL baseline drift — it never flips the exit code
|
|
50
|
+
// (mirroring how baseline debt is already non-blocking). When no changed set
|
|
51
|
+
// is supplied (`changedFiles === undefined`), fall back to the legacy
|
|
52
|
+
// behaviour where any NEW violation blocks.
|
|
53
|
+
const scoped = options.changedFiles !== undefined;
|
|
54
|
+
const changedSet = new Set((options.changedFiles ?? []).map(normaliseRel));
|
|
55
|
+
const attributable = scoped
|
|
56
|
+
? newViolations.filter((v) => fileInChangedSet(v.file, changedSet))
|
|
57
|
+
: newViolations;
|
|
58
|
+
const drift = scoped
|
|
59
|
+
? newViolations.filter((v) => !fileInChangedSet(v.file, changedSet))
|
|
60
|
+
: [];
|
|
61
|
+
const newErrors = attributable.filter((v) => v.severity === 'error').length;
|
|
62
|
+
const newWarnings = attributable.filter((v) => v.severity === 'warning').length;
|
|
63
|
+
const driftErrors = drift.filter((v) => v.severity === 'error').length;
|
|
64
|
+
const driftWarnings = drift.filter((v) => v.severity === 'warning').length;
|
|
65
|
+
const driftNote = scoped && driftErrors + driftWarnings > 0
|
|
66
|
+
? ` Baseline drift in untouched files: ${driftErrors} error(s), ${driftWarnings} warning(s) (informational — re-freeze with \`shrk gate baseline --refreeze\`).`
|
|
67
|
+
: '';
|
|
45
68
|
if (newErrors > 0) {
|
|
46
69
|
return {
|
|
47
70
|
id: 'arch',
|
|
48
71
|
label: 'Architecture',
|
|
49
72
|
status: 'fail',
|
|
50
|
-
message: `${newErrors} NEW architecture error(s)
|
|
51
|
-
details: {
|
|
73
|
+
message: `${newErrors} NEW architecture error(s) introduced by this change (baseline debt: ${baselineErrors}, informational).${driftNote}`,
|
|
74
|
+
details: {
|
|
75
|
+
newErrors,
|
|
76
|
+
newWarnings,
|
|
77
|
+
baselineErrors,
|
|
78
|
+
totalErrors: errors,
|
|
79
|
+
driftErrors,
|
|
80
|
+
driftWarnings,
|
|
81
|
+
changeScoped: scoped,
|
|
82
|
+
newViolationIds: attributable.map(violationId),
|
|
83
|
+
driftViolationIds: drift.map(violationId),
|
|
84
|
+
},
|
|
52
85
|
nextCommands: ['shrk arch check', 'shrk arch baseline show'],
|
|
53
86
|
durationMs: Date.now() - start,
|
|
54
87
|
};
|
|
@@ -58,20 +91,41 @@ export function archGate(projectRoot, options = {}) {
|
|
|
58
91
|
id: 'arch',
|
|
59
92
|
label: 'Architecture',
|
|
60
93
|
status: 'warn',
|
|
61
|
-
message: `${newWarnings} new architecture warning(s)
|
|
62
|
-
details: {
|
|
94
|
+
message: `${newWarnings} new architecture warning(s) introduced by this change (baseline debt: ${baselineErrors} error(s), informational).${driftNote}`,
|
|
95
|
+
details: {
|
|
96
|
+
newWarnings,
|
|
97
|
+
baselineErrors,
|
|
98
|
+
totalErrors: errors,
|
|
99
|
+
totalWarnings: warnings,
|
|
100
|
+
driftErrors,
|
|
101
|
+
driftWarnings,
|
|
102
|
+
changeScoped: scoped,
|
|
103
|
+
},
|
|
63
104
|
nextCommands: ['shrk arch check'],
|
|
64
105
|
durationMs: Date.now() - start,
|
|
65
106
|
};
|
|
66
107
|
}
|
|
108
|
+
// No change-attributable NEW violation. This is a PASS for THIS change even
|
|
109
|
+
// when baseline drift persists in untouched files — surface that drift as
|
|
110
|
+
// informational rather than reporting a misleading all-clear over zero
|
|
111
|
+
// attributed errors (the honest exit-code posture).
|
|
112
|
+
const passMessage = scoped && driftErrors + driftWarnings > 0
|
|
113
|
+
? `No change-attributable architecture errors (baseline debt: ${baselineErrors} error(s), informational).${driftNote}`
|
|
114
|
+
: errors > 0
|
|
115
|
+
? `No NEW architecture violations (baseline debt: ${baselineErrors} error(s), informational).`
|
|
116
|
+
: 'No architecture violations.';
|
|
67
117
|
return {
|
|
68
118
|
id: 'arch',
|
|
69
119
|
label: 'Architecture',
|
|
70
120
|
status: 'pass',
|
|
71
|
-
message:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
121
|
+
message: passMessage,
|
|
122
|
+
details: {
|
|
123
|
+
baselineErrors,
|
|
124
|
+
totalErrors: errors,
|
|
125
|
+
driftErrors,
|
|
126
|
+
driftWarnings,
|
|
127
|
+
changeScoped: scoped,
|
|
128
|
+
},
|
|
75
129
|
durationMs: Date.now() - start,
|
|
76
130
|
};
|
|
77
131
|
}
|
|
@@ -125,3 +179,26 @@ export function archGate(projectRoot, options = {}) {
|
|
|
125
179
|
durationMs: Date.now() - start,
|
|
126
180
|
};
|
|
127
181
|
}
|
|
182
|
+
/** Slash-normalise a project-relative path and strip a leading `./`. */
|
|
183
|
+
function normaliseRel(input) {
|
|
184
|
+
return input.split(/[\\/]/).join('/').replace(/^\.\//, '');
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* True when a violation's origin file is in the changed-file set. Both sides are
|
|
188
|
+
* project-relative; the suffix match is a safety net for callers that pass
|
|
189
|
+
* changed paths with a differing prefix depth (mirrors the boundary changed-only
|
|
190
|
+
* filter's fuzzy tail match).
|
|
191
|
+
*/
|
|
192
|
+
function fileInChangedSet(file, changedSet) {
|
|
193
|
+
if (changedSet.size === 0)
|
|
194
|
+
return false;
|
|
195
|
+
const norm = normaliseRel(file);
|
|
196
|
+
if (changedSet.has(norm))
|
|
197
|
+
return true;
|
|
198
|
+
for (const cf of changedSet) {
|
|
199
|
+
if (cf.length > 0 && (norm === cf || norm.endsWith('/' + cf) || cf.endsWith('/' + norm))) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
@@ -12,5 +12,20 @@ export interface IArchGateOptions {
|
|
|
12
12
|
* behavior; useful for a clean-tree CI demand).
|
|
13
13
|
*/
|
|
14
14
|
baselineRelative?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Change-scoped attribution set: the project-relative ('/'-normalised) files
|
|
17
|
+
* the working diff actually touched (diff vs HEAD). When provided (even an
|
|
18
|
+
* empty array), a NEW-since-baseline violation is BLOCKING only if its origin
|
|
19
|
+
* file is in this set; a NEW violation in an untouched file is demoted to
|
|
20
|
+
* INFORMATIONAL "baseline drift" and never flips the exit code. This is the
|
|
21
|
+
* fix for §3.1: "NEW" must mean *introduced by this change*, not drift against
|
|
22
|
+
* a frozen (possibly months-old) baseline.
|
|
23
|
+
*
|
|
24
|
+
* `undefined` disables change-scoped attribution (legacy behaviour: any
|
|
25
|
+
* NEW-since-baseline violation blocks). An empty array means "clean tree — no
|
|
26
|
+
* change-attributable violations" (a pass for THIS change), keeping the
|
|
27
|
+
* informational drift line visible.
|
|
28
|
+
*/
|
|
29
|
+
changedFiles?: readonly string[];
|
|
15
30
|
}
|
|
16
31
|
//# sourceMappingURL=arch-gate-options.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arch-gate-options.d.ts","sourceRoot":"","sources":["../../src/schema/arch-gate-options.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"arch-gate-options.d.ts","sourceRoot":"","sources":["../../src/schema/arch-gate-options.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shrkcrft/quality-gates",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.26",
|
|
4
4
|
"description": "SharkCraft quality gates: single aggregator that runs the code-intelligence checks (graph index, boundaries, architecture, impact summary) and emits one pass/fail report. The pre-merge gate for AI-agent-authored changes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SharkCraft contributors",
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@shrkcrft/core": "^0.1.0-alpha.
|
|
47
|
-
"@shrkcrft/graph": "^0.1.0-alpha.
|
|
48
|
-
"@shrkcrft/architecture-guard": "^0.1.0-alpha.
|
|
49
|
-
"@shrkcrft/impact-engine": "^0.1.0-alpha.
|
|
50
|
-
"@shrkcrft/api-surface-diff": "^0.1.0-alpha.
|
|
51
|
-
"@shrkcrft/boundaries": "^0.1.0-alpha.
|
|
52
|
-
"@shrkcrft/structural-search": "^0.1.0-alpha.
|
|
53
|
-
"@shrkcrft/context-planner": "^0.1.0-alpha.
|
|
54
|
-
"@shrkcrft/inspector": "^0.1.0-alpha.
|
|
46
|
+
"@shrkcrft/core": "^0.1.0-alpha.26",
|
|
47
|
+
"@shrkcrft/graph": "^0.1.0-alpha.26",
|
|
48
|
+
"@shrkcrft/architecture-guard": "^0.1.0-alpha.26",
|
|
49
|
+
"@shrkcrft/impact-engine": "^0.1.0-alpha.26",
|
|
50
|
+
"@shrkcrft/api-surface-diff": "^0.1.0-alpha.26",
|
|
51
|
+
"@shrkcrft/boundaries": "^0.1.0-alpha.26",
|
|
52
|
+
"@shrkcrft/structural-search": "^0.1.0-alpha.26",
|
|
53
|
+
"@shrkcrft/context-planner": "^0.1.0-alpha.26",
|
|
54
|
+
"@shrkcrft/inspector": "^0.1.0-alpha.26"
|
|
55
55
|
},
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public"
|