@shrkcrft/quality-gates 0.1.0-alpha.18 → 0.1.0-alpha.20

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,8 +1,19 @@
1
+ import type { IArchGateOptions } from '../schema/arch-gate-options.js';
1
2
  import type { IGateResult } from '../schema/quality-gate.js';
2
3
  /**
3
- * Architecture-guard gate. Pass when zero error-severity violations.
4
- * Warnings (fat barrels, 2-node cycles, adapter leaks) are reported
5
- * as `warn` the gate doesn't fail, but the human sees them.
4
+ * Architecture-guard gate.
5
+ *
6
+ * When a frozen baseline exists and `baselineRelative` is not disabled (the
7
+ * DEFAULT), the gate fails only on NEW architecture errors — violations absent
8
+ * from the baseline — and surfaces the total pre-existing debt as informational.
9
+ * This stops the gate from being a perpetual red on baseline debt the current
10
+ * diff never introduced (an agent learns to ignore a gate that's always red).
11
+ *
12
+ * When no baseline is frozen (or `baselineRelative: false`), it preserves the
13
+ * legacy behavior — fail on ANY error — so nothing regresses for projects that
14
+ * never opted in, and a clean-tree CI demand stays expressible.
15
+ *
16
+ * Warnings never fail the gate; they are reported as `warn`.
6
17
  */
7
- export declare function archGate(projectRoot: string): IGateResult;
18
+ export declare function archGate(projectRoot: string, options?: IArchGateOptions): IGateResult;
8
19
  //# sourceMappingURL=arch-gate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"arch-gate.d.ts","sourceRoot":"","sources":["../../src/gates/arch-gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAgDzD"}
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;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,WAAW,CA6FzF"}
@@ -1,10 +1,20 @@
1
- import { runArchCheck } from '@shrkcrft/architecture-guard';
1
+ import { ArchReportStore, diffSnapshots, runArchCheck, snapshotFromReport, violationId, } from '@shrkcrft/architecture-guard';
2
2
  /**
3
- * Architecture-guard gate. Pass when zero error-severity violations.
4
- * Warnings (fat barrels, 2-node cycles, adapter leaks) are reported
5
- * as `warn` the gate doesn't fail, but the human sees them.
3
+ * Architecture-guard gate.
4
+ *
5
+ * When a frozen baseline exists and `baselineRelative` is not disabled (the
6
+ * DEFAULT), the gate fails only on NEW architecture errors — violations absent
7
+ * from the baseline — and surfaces the total pre-existing debt as informational.
8
+ * This stops the gate from being a perpetual red on baseline debt the current
9
+ * diff never introduced (an agent learns to ignore a gate that's always red).
10
+ *
11
+ * When no baseline is frozen (or `baselineRelative: false`), it preserves the
12
+ * legacy behavior — fail on ANY error — so nothing regresses for projects that
13
+ * never opted in, and a clean-tree CI demand stays expressible.
14
+ *
15
+ * Warnings never fail the gate; they are reported as `warn`.
6
16
  */
7
- export function archGate(projectRoot) {
17
+ export function archGate(projectRoot, options = {}) {
8
18
  const start = Date.now();
9
19
  const report = runArchCheck({ projectRoot });
10
20
  if (report.diagnostics.some((d) => d.includes('code-graph store missing'))) {
@@ -19,18 +29,58 @@ export function archGate(projectRoot) {
19
29
  }
20
30
  const errors = report.countsBySeverity.error;
21
31
  const warnings = report.countsBySeverity.warning;
32
+ const baseline = options.baselineRelative === false ? undefined : new ArchReportStore(projectRoot).readBaseline();
33
+ if (baseline) {
34
+ const current = snapshotFromReport(report);
35
+ const delta = diffSnapshots(baseline, current);
36
+ const newIds = new Set(delta.newViolationIds);
37
+ const newViolations = report.violations.filter((v) => newIds.has(violationId(v)));
38
+ const newErrors = newViolations.filter((v) => v.severity === 'error').length;
39
+ const newWarnings = newViolations.filter((v) => v.severity === 'warning').length;
40
+ const baselineErrors = baseline.countsBySeverity.error;
41
+ if (newErrors > 0) {
42
+ return {
43
+ id: 'arch',
44
+ label: 'Architecture',
45
+ status: 'fail',
46
+ message: `${newErrors} NEW architecture error(s) since baseline (baseline debt: ${baselineErrors}, informational).`,
47
+ details: { newErrors, newWarnings, baselineErrors, totalErrors: errors, newViolationIds: delta.newViolationIds },
48
+ nextCommands: ['shrk arch check', 'shrk arch baseline show'],
49
+ durationMs: Date.now() - start,
50
+ };
51
+ }
52
+ if (newWarnings > 0) {
53
+ return {
54
+ id: 'arch',
55
+ label: 'Architecture',
56
+ status: 'warn',
57
+ message: `${newWarnings} new architecture warning(s) since baseline (baseline debt: ${baselineErrors} error(s), informational).`,
58
+ details: { newWarnings, baselineErrors, totalErrors: errors, totalWarnings: warnings },
59
+ nextCommands: ['shrk arch check'],
60
+ durationMs: Date.now() - start,
61
+ };
62
+ }
63
+ return {
64
+ id: 'arch',
65
+ label: 'Architecture',
66
+ status: 'pass',
67
+ message: errors > 0
68
+ ? `No NEW architecture violations (baseline debt: ${baselineErrors} error(s), informational).`
69
+ : 'No architecture violations.',
70
+ details: { baselineErrors, totalErrors: errors },
71
+ durationMs: Date.now() - start,
72
+ };
73
+ }
74
+ // No baseline (or baseline-relative disabled): fail on any error (legacy), but
75
+ // hint how to opt into NEW-only gating.
22
76
  if (errors > 0) {
23
77
  return {
24
78
  id: 'arch',
25
79
  label: 'Architecture',
26
80
  status: 'fail',
27
81
  message: `${errors} architecture error(s).`,
28
- details: {
29
- errors,
30
- warnings,
31
- kinds: report.countsByKind,
32
- },
33
- nextCommands: ['shrk arch check'],
82
+ details: { errors, warnings, kinds: report.countsByKind },
83
+ nextCommands: ['shrk arch check', 'shrk arch baseline write'],
34
84
  durationMs: Date.now() - start,
35
85
  };
36
86
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './schema/quality-gate.js';
2
+ export * from './schema/arch-gate-options.js';
2
3
  export * from './gates/graph-fresh-gate.js';
3
4
  export * from './gates/arch-gate.js';
4
5
  export * from './gates/impact-gate.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./schema/quality-gate.js";
2
+ export * from "./schema/arch-gate-options.js";
2
3
  export * from "./gates/graph-fresh-gate.js";
3
4
  export * from "./gates/arch-gate.js";
4
5
  export * from "./gates/impact-gate.js";
@@ -1,4 +1,5 @@
1
1
  import { type IApiDiffGateOptions } from '../gates/api-diff-gate.js';
2
+ import type { IArchGateOptions } from '../schema/arch-gate-options.js';
2
3
  import { type IGraphCyclesGateOptions } from '../gates/graph-cycles-gate.js';
3
4
  import { type IGraphUnresolvedGateOptions } from '../gates/graph-unresolved-gate.js';
4
5
  import { type IImpactGateOptions } from '../gates/impact-gate.js';
@@ -8,6 +9,8 @@ import { type IStructuralPatternsGateOptions } from '../gates/structural-pattern
8
9
  import { type IQualityGateReport } from '../schema/quality-gate.js';
9
10
  export interface IRunGatesOptions {
10
11
  projectRoot: string;
12
+ /** Optional architecture-gate config (baseline-relative toggle). */
13
+ arch?: IArchGateOptions;
11
14
  /** Optional impact-gate config (sinceRef, failOn). */
12
15
  impact?: IImpactGateOptions;
13
16
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"run-gates.d.ts","sourceRoot":"","sources":["../../src/runner/run-gates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAElF,OAAO,EAAmB,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAE9F,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAc,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,yCAAyC;IACzC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,6CAA6C;IAC7C,eAAe,CAAC,EAAE,2BAA2B,CAAC;IAC9C,4CAA4C;IAC5C,cAAc,CAAC,EAAE,0BAA0B,CAAC;IAC5C,gDAAgD;IAChD,kBAAkB,CAAC,EAAE,8BAA8B,CAAC;IACpD,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,4BAA4B,CAAC;IAChD,oCAAoC;IACpC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,kBAAkB,CAkD7E"}
1
+ {"version":3,"file":"run-gates.d.ts","sourceRoot":"","sources":["../../src/runner/run-gates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAElF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAmB,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAE9F,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAc,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,sDAAsD;IACtD,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,yCAAyC;IACzC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,6CAA6C;IAC7C,eAAe,CAAC,EAAE,2BAA2B,CAAC;IAC9C,4CAA4C;IAC5C,cAAc,CAAC,EAAE,0BAA0B,CAAC;IAC5C,gDAAgD;IAChD,kBAAkB,CAAC,EAAE,8BAA8B,CAAC;IACpD,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,4BAA4B,CAAC;IAChD,oCAAoC;IACpC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,kBAAkB,CAkD7E"}
@@ -40,7 +40,7 @@ export function runQualityGates(options) {
40
40
  gates.push(graphFreshGate(options.projectRoot));
41
41
  }
42
42
  if (!disabled.has('arch')) {
43
- gates.push(archGate(options.projectRoot));
43
+ gates.push(archGate(options.projectRoot, options.arch ?? {}));
44
44
  }
45
45
  if (!disabled.has('impact')) {
46
46
  gates.push(impactGate(options.projectRoot, options.impact ?? {}));
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Options for the architecture-guard gate.
3
+ */
4
+ export interface IArchGateOptions {
5
+ /**
6
+ * When true (the DEFAULT) and a frozen baseline exists, the gate fails only on
7
+ * NEW architecture errors (violations absent from the baseline) and surfaces
8
+ * the total pre-existing debt as informational — so the gate isn't a perpetual
9
+ * red on baseline debt the current diff never introduced.
10
+ *
11
+ * Set to false to fail on ANY error regardless of the baseline (the legacy
12
+ * behavior; useful for a clean-tree CI demand).
13
+ */
14
+ baselineRelative?: boolean;
15
+ }
16
+ //# sourceMappingURL=arch-gate-options.d.ts.map
@@ -0,0 +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;CAC5B"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@shrkcrft/quality-gates",
3
- "version": "0.1.0-alpha.18",
3
+ "version": "0.1.0-alpha.20",
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",
7
7
  "type": "module",
8
8
  "main": "./dist/index.js",
9
- "types": "./dist/index.d.d.ts",
9
+ "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./dist/index.d.ts",
@@ -43,14 +43,14 @@
43
43
  "typecheck": "tsc --noEmit -p tsconfig.json"
44
44
  },
45
45
  "dependencies": {
46
- "@shrkcrft/core": "^0.1.0-alpha.18",
47
- "@shrkcrft/graph": "^0.1.0-alpha.18",
48
- "@shrkcrft/architecture-guard": "^0.1.0-alpha.18",
49
- "@shrkcrft/impact-engine": "^0.1.0-alpha.18",
50
- "@shrkcrft/api-surface-diff": "^0.1.0-alpha.18",
51
- "@shrkcrft/boundaries": "^0.1.0-alpha.18",
52
- "@shrkcrft/structural-search": "^0.1.0-alpha.18",
53
- "@shrkcrft/context-planner": "^0.1.0-alpha.18"
46
+ "@shrkcrft/core": "^0.1.0-alpha.20",
47
+ "@shrkcrft/graph": "^0.1.0-alpha.20",
48
+ "@shrkcrft/architecture-guard": "^0.1.0-alpha.20",
49
+ "@shrkcrft/impact-engine": "^0.1.0-alpha.20",
50
+ "@shrkcrft/api-surface-diff": "^0.1.0-alpha.20",
51
+ "@shrkcrft/boundaries": "^0.1.0-alpha.20",
52
+ "@shrkcrft/structural-search": "^0.1.0-alpha.20",
53
+ "@shrkcrft/context-planner": "^0.1.0-alpha.20"
54
54
  },
55
55
  "publishConfig": {
56
56
  "access": "public"