@eduardbar/drift 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gga +50 -0
- package/.github/actions/drift-review/README.md +62 -0
- package/.github/actions/drift-review/action.yml +148 -0
- package/.github/actions/drift-scan/README.md +28 -32
- package/.github/actions/drift-scan/action.yml +78 -14
- package/.github/workflows/publish-vscode.yml +1 -3
- package/.github/workflows/publish.yml +8 -0
- package/.github/workflows/quality.yml +15 -0
- package/.github/workflows/reusable-quality-checks.yml +95 -0
- package/.github/workflows/review-pr.yml +33 -41
- package/AGENTS.md +75 -251
- package/CHANGELOG.md +41 -0
- package/README.md +177 -43
- package/benchmarks/fixtures/critical/drift.config.ts +21 -0
- package/benchmarks/fixtures/critical/src/app/user-service.ts +30 -0
- package/benchmarks/fixtures/critical/src/domain/entities.ts +19 -0
- package/benchmarks/fixtures/critical/src/domain/policies.ts +22 -0
- package/benchmarks/fixtures/critical/src/index.ts +10 -0
- package/benchmarks/fixtures/critical/src/infra/memory-user-repo.ts +14 -0
- package/benchmarks/perf-budget.v1.json +27 -0
- package/dist/benchmark.d.ts +1 -1
- package/dist/benchmark.js +83 -52
- package/dist/cli.js +243 -8
- package/dist/config.js +16 -2
- package/dist/diff.js +42 -50
- package/dist/doctor.d.ts +26 -0
- package/dist/doctor.js +140 -0
- package/dist/format.d.ts +17 -0
- package/dist/format.js +45 -0
- package/dist/guard-baseline.d.ts +12 -0
- package/dist/guard-baseline.js +57 -0
- package/dist/guard-metrics.d.ts +6 -0
- package/dist/guard-metrics.js +39 -0
- package/dist/guard-types.d.ts +58 -0
- package/dist/guard-types.js +2 -0
- package/dist/guard.d.ts +16 -0
- package/dist/guard.js +178 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +4 -1
- package/dist/init.d.ts +15 -0
- package/dist/init.js +273 -0
- package/dist/map-cycles.d.ts +2 -0
- package/dist/map-cycles.js +34 -0
- package/dist/map-svg.d.ts +19 -0
- package/dist/map-svg.js +97 -0
- package/dist/map.js +78 -138
- package/dist/metrics.js +70 -55
- package/dist/output-metadata.d.ts +15 -0
- package/dist/output-metadata.js +19 -0
- package/dist/plugins-capabilities.d.ts +4 -0
- package/dist/plugins-capabilities.js +21 -0
- package/dist/plugins-messages.d.ts +10 -0
- package/dist/plugins-messages.js +16 -0
- package/dist/plugins-rules.d.ts +9 -0
- package/dist/plugins-rules.js +137 -0
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +45 -142
- package/dist/reporter-constants.d.ts +16 -0
- package/dist/reporter-constants.js +39 -0
- package/dist/reporter.d.ts +3 -3
- package/dist/reporter.js +35 -55
- package/dist/review.d.ts +2 -1
- package/dist/review.js +2 -1
- package/dist/rules/phase3-configurable.js +23 -15
- package/dist/saas/constants.d.ts +15 -0
- package/dist/saas/constants.js +48 -0
- package/dist/saas/dashboard.d.ts +8 -0
- package/dist/saas/dashboard.js +132 -0
- package/dist/saas/errors.d.ts +19 -0
- package/dist/saas/errors.js +37 -0
- package/dist/saas/helpers.d.ts +21 -0
- package/dist/saas/helpers.js +110 -0
- package/dist/saas/ingest.d.ts +3 -0
- package/dist/saas/ingest.js +249 -0
- package/dist/saas/organization.d.ts +5 -0
- package/dist/saas/organization.js +82 -0
- package/dist/saas/plan-change.d.ts +10 -0
- package/dist/saas/plan-change.js +15 -0
- package/dist/saas/store.d.ts +21 -0
- package/dist/saas/store.js +159 -0
- package/dist/saas/types.d.ts +191 -0
- package/dist/saas/types.js +2 -0
- package/dist/saas.d.ts +8 -218
- package/dist/saas.js +7 -761
- package/dist/sarif.d.ts +74 -0
- package/dist/sarif.js +122 -0
- package/dist/trust-advanced.d.ts +14 -0
- package/dist/trust-advanced.js +65 -0
- package/dist/trust-kpi-fs.d.ts +3 -0
- package/dist/trust-kpi-fs.js +141 -0
- package/dist/trust-kpi-parse.d.ts +7 -0
- package/dist/trust-kpi-parse.js +186 -0
- package/dist/trust-kpi-types.d.ts +16 -0
- package/dist/trust-kpi-types.js +2 -0
- package/dist/trust-kpi.d.ts +1 -3
- package/dist/trust-kpi.js +6 -266
- package/dist/trust-policy.d.ts +32 -0
- package/dist/trust-policy.js +160 -0
- package/dist/trust-render.d.ts +9 -0
- package/dist/trust-render.js +54 -0
- package/dist/trust-scoring.d.ts +9 -0
- package/dist/trust-scoring.js +208 -0
- package/dist/trust.d.ts +5 -32
- package/dist/trust.js +29 -432
- package/dist/types/app.d.ts +30 -0
- package/dist/types/app.js +2 -0
- package/dist/types/config.d.ts +25 -0
- package/dist/types/config.js +2 -0
- package/dist/types/core.d.ts +100 -0
- package/dist/types/core.js +2 -0
- package/dist/types/diff.d.ts +55 -0
- package/dist/types/diff.js +2 -0
- package/dist/types/plugin.d.ts +41 -0
- package/dist/types/plugin.js +2 -0
- package/dist/types/trust.d.ts +120 -0
- package/dist/types/trust.js +2 -0
- package/dist/types.d.ts +8 -365
- package/docs/AGENTS.md +1 -1
- package/docs/release-notes-draft.md +40 -0
- package/docs/rules-catalog.md +49 -0
- package/docs/trust-core-release-checklist.md +37 -5
- package/package.json +11 -4
- package/packages/vscode-drift/src/code-actions.ts +1 -1
- package/schemas/drift-ai-output.v1.json +162 -0
- package/schemas/drift-doctor.v1.json +57 -0
- package/schemas/drift-guard.v1.json +298 -0
- package/schemas/drift-report.v1.json +151 -0
- package/schemas/drift-trust.v1.json +131 -0
- package/scripts/check-docs-drift.mjs +154 -0
- package/scripts/check-performance-budget.mjs +360 -0
- package/scripts/check-runtime-policy.mjs +66 -0
- package/scripts/smoke-repo.mjs +394 -0
- package/src/benchmark.ts +92 -53
- package/src/cli.ts +285 -13
- package/src/config.ts +19 -2
- package/src/diff.ts +57 -48
- package/src/doctor.ts +185 -0
- package/src/format.ts +81 -0
- package/src/guard-baseline.ts +74 -0
- package/src/guard-metrics.ts +52 -0
- package/src/guard-types.ts +66 -0
- package/src/guard.ts +248 -0
- package/src/index.ts +36 -0
- package/src/init.ts +298 -0
- package/src/map-cycles.ts +38 -0
- package/src/map-svg.ts +124 -0
- package/src/map.ts +111 -142
- package/src/metrics.ts +78 -59
- package/src/output-metadata.ts +32 -0
- package/src/plugins-capabilities.ts +36 -0
- package/src/plugins-messages.ts +35 -0
- package/src/plugins-rules.ts +296 -0
- package/src/plugins.ts +76 -283
- package/src/reporter-constants.ts +46 -0
- package/src/reporter.ts +64 -65
- package/src/review.ts +4 -2
- package/src/rules/phase3-configurable.ts +39 -26
- package/src/saas/constants.ts +56 -0
- package/src/saas/dashboard.ts +172 -0
- package/src/saas/errors.ts +45 -0
- package/src/saas/helpers.ts +140 -0
- package/src/saas/ingest.ts +278 -0
- package/src/saas/organization.ts +99 -0
- package/src/saas/plan-change.ts +19 -0
- package/src/saas/store.ts +172 -0
- package/src/saas/types.ts +216 -0
- package/src/saas.ts +49 -1031
- package/src/sarif.ts +232 -0
- package/src/trust-advanced.ts +99 -0
- package/src/trust-kpi-fs.ts +169 -0
- package/src/trust-kpi-parse.ts +219 -0
- package/src/trust-kpi-types.ts +19 -0
- package/src/trust-kpi.ts +8 -316
- package/src/trust-policy.ts +246 -0
- package/src/trust-render.ts +61 -0
- package/src/trust-scoring.ts +231 -0
- package/src/trust.ts +62 -576
- package/src/types/app.ts +30 -0
- package/src/types/config.ts +27 -0
- package/src/types/core.ts +105 -0
- package/src/types/diff.ts +61 -0
- package/src/types/plugin.ts +46 -0
- package/src/types/trust.ts +134 -0
- package/src/types.ts +79 -409
- package/tests/ci-quality-matrix.test.ts +37 -0
- package/tests/ci-smoke-gate.test.ts +26 -0
- package/tests/ci-version-alignment.test.ts +93 -0
- package/tests/cli-sarif.test.ts +92 -0
- package/tests/docs-drift-check.test.ts +115 -0
- package/tests/format.test.ts +157 -0
- package/tests/new-features.test.ts +11 -3
- package/tests/perf-budget-check.test.ts +146 -0
- package/tests/phase1-init-doctor-guard.test.ts +301 -0
- package/tests/runtime-policy-alignment.test.ts +46 -0
- package/tests/sarif.test.ts +160 -0
- package/tests/trust-kpi.test.ts +31 -4
- package/tests/trust.test.ts +18 -0
- package/vitest.config.ts +2 -0
package/README.md
CHANGED
|
@@ -51,12 +51,63 @@ npm install --save-dev @eduardbar/drift
|
|
|
51
51
|
|
|
52
52
|
- Product requirements and roadmap: [`docs/PRD.md`](./docs/PRD.md)
|
|
53
53
|
- Trust core release checklist: [`docs/trust-core-release-checklist.md`](./docs/trust-core-release-checklist.md)
|
|
54
|
+
- Rules catalog (source-of-truth snapshot): [`docs/rules-catalog.md`](./docs/rules-catalog.md)
|
|
54
55
|
- Contributor/agent workflow guide: [`docs/AGENTS.md`](./docs/AGENTS.md)
|
|
55
56
|
|
|
56
57
|
---
|
|
57
58
|
|
|
58
59
|
## Commands
|
|
59
60
|
|
|
61
|
+
### `drift init`
|
|
62
|
+
|
|
63
|
+
Scaffold first-run setup for drift in an existing repository.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
drift init
|
|
67
|
+
drift init --preset node-backend
|
|
68
|
+
drift init --preset react-app --ci --baseline
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Flag | Description |
|
|
72
|
+
|------|-------------|
|
|
73
|
+
| `--preset <type>` | Generate `drift.config.ts` using one of: `node-backend`, `react-app`, `hexagonal`, `monorepo` |
|
|
74
|
+
| `--ci` | Generate `.github/workflows/drift-review.yml` |
|
|
75
|
+
| `--baseline` | Generate `drift-baseline.json` from the current scan |
|
|
76
|
+
|
|
77
|
+
`drift init` is non-destructive for pre-existing targets: drift skips generation when an output file already exists and prints a warning.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### `drift doctor`
|
|
82
|
+
|
|
83
|
+
Run environment diagnostics for Node/runtime and project readiness.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
drift doctor
|
|
87
|
+
drift doctor --json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
| Flag | Description |
|
|
91
|
+
|------|-------------|
|
|
92
|
+
| `--json` | Output structured doctor report JSON |
|
|
93
|
+
|
|
94
|
+
Checks include Node major version support (`>=20`), `package.json`, ESM mode, `tsconfig.json`, source file count, optional `--low-memory` recommendation, and `drift.config.*` detection.
|
|
95
|
+
|
|
96
|
+
When `--json` is enabled, output includes `$schema` and `toolVersion` metadata and follows [`schemas/drift-doctor.v1.json`](./schemas/drift-doctor.v1.json).
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Repository smoke (local source CLI)
|
|
101
|
+
|
|
102
|
+
Run a non-destructive end-to-end smoke suite against any repository path using local source commands (`node --import tsx ./src/cli.ts ...`).
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm run smoke:repo -- ../target-repo --base origin/main
|
|
106
|
+
npm run smoke:repo -- ../target-repo --dry-run
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The script writes JSON + Markdown summaries and command logs under `.drift-smoke/<repo>-<timestamp>/`.
|
|
110
|
+
|
|
60
111
|
### `drift scan [path]`
|
|
61
112
|
|
|
62
113
|
Scan a directory and print a scored report to stdout.
|
|
@@ -77,16 +128,19 @@ drift scan ./src --low-memory --max-file-size-kb 1024
|
|
|
77
128
|
| Flag | Description |
|
|
78
129
|
|------|-------------|
|
|
79
130
|
| `--output <file>` | Write Markdown report to a file instead of stdout |
|
|
131
|
+
| `--format <type>` | Output format: `console\|json\|markdown\|ai\|sarif` |
|
|
80
132
|
| `--json` | Output raw `DriftReport` JSON |
|
|
81
133
|
| `--ai` | Output structured JSON optimized for LLM consumption (Claude, GPT, etc.) |
|
|
82
134
|
| `--fix` | Print inline fix suggestions for each detected issue |
|
|
83
|
-
| `--min-score <n>` | Exit with code 1 if the overall score
|
|
135
|
+
| `--min-score <n>` | Exit with code 1 if the overall score strictly exceeds this threshold |
|
|
84
136
|
| `--low-memory` | Analyze files in bounded chunks to reduce peak RAM |
|
|
85
137
|
| `--chunk-size <n>` | Files per chunk in low-memory mode (default: 40) |
|
|
86
138
|
| `--max-files <n>` | Soft cap on analyzed files; extra files are skipped with diagnostics |
|
|
87
139
|
| `--max-file-size-kb <n>` | Skip oversized files with diagnostics to avoid OOM |
|
|
88
140
|
| `--with-semantic-duplication` | Keep semantic duplication rule enabled in low-memory mode |
|
|
89
141
|
|
|
142
|
+
`--min-score` currently fails when `totalScore > n` (strictly greater than), matching CLI behavior.
|
|
143
|
+
|
|
90
144
|
**Example output:**
|
|
91
145
|
|
|
92
146
|
```
|
|
@@ -113,6 +167,35 @@ drift scan ./src --low-memory --max-file-size-kb 1024
|
|
|
113
167
|
|
|
114
168
|
---
|
|
115
169
|
|
|
170
|
+
### `drift guard [path]`
|
|
171
|
+
|
|
172
|
+
Evaluate drift regression guardrails against a git diff (`--base`) or a baseline file/object.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
drift guard ./src --base origin/main
|
|
176
|
+
drift guard ./src --baseline drift-baseline.json
|
|
177
|
+
drift guard ./src --base origin/main --budget 3 --by-severity error=0,warning=2,info=5
|
|
178
|
+
drift guard ./src --base origin/main --json
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
| Flag | Description |
|
|
182
|
+
|------|-------------|
|
|
183
|
+
| `--base <ref>` | Diff mode: compare current state vs git ref |
|
|
184
|
+
| `--baseline <file>` | Baseline mode: compare against baseline JSON (default file name: `drift-baseline.json`) |
|
|
185
|
+
| `--budget <n>` | Maximum allowed score delta |
|
|
186
|
+
| `--by-severity <spec>` | Severity thresholds as CSV `key=value` pairs (`error`, `warning`, `info`) |
|
|
187
|
+
| `--json` | Output full `GuardResult` JSON |
|
|
188
|
+
| `--low-memory` / `--chunk-size <n>` / `--max-files <n>` / `--max-file-size-kb <n>` / `--with-semantic-duplication` | Analysis resource controls shared with scan/diff/report/badge/ci/trust |
|
|
189
|
+
|
|
190
|
+
Behavior:
|
|
191
|
+
- With `--base`, guard enforces no-regression checks for score and total issues, then applies optional budget/severity thresholds.
|
|
192
|
+
- Without `--base`, guard requires a baseline (inline or file) and compares only against available baseline anchors.
|
|
193
|
+
- Exit code is `1` when any guard check fails.
|
|
194
|
+
|
|
195
|
+
When `--json` is enabled, output includes `$schema` and `toolVersion` metadata and follows [`schemas/drift-guard.v1.json`](./schemas/drift-guard.v1.json).
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
116
199
|
### `drift diff [ref]`
|
|
117
200
|
|
|
118
201
|
Compare the current project state against any git ref. Defaults to `HEAD~1`.
|
|
@@ -122,6 +205,7 @@ drift diff # HEAD vs HEAD~1
|
|
|
122
205
|
drift diff HEAD~3 # HEAD vs 3 commits ago
|
|
123
206
|
drift diff main # HEAD vs branch main
|
|
124
207
|
drift diff abc1234 # HEAD vs a specific commit
|
|
208
|
+
drift diff --format sarif # Output SARIF for code scanning
|
|
125
209
|
drift diff --json # Output raw JSON diff
|
|
126
210
|
```
|
|
127
211
|
|
|
@@ -129,6 +213,7 @@ drift diff --json # Output raw JSON diff
|
|
|
129
213
|
|
|
130
214
|
| Flag | Description |
|
|
131
215
|
|------|-------------|
|
|
216
|
+
| `--format <type>` | Output format: `console\|json\|sarif` |
|
|
132
217
|
| `--json` | Output raw JSON diff |
|
|
133
218
|
|
|
134
219
|
Shows score delta, issues introduced, and issues resolved since the given ref.
|
|
@@ -143,12 +228,14 @@ Review drift against a git base ref and output a PR-ready markdown comment.
|
|
|
143
228
|
drift review --base origin/main
|
|
144
229
|
drift review --base main --comment
|
|
145
230
|
drift review --base HEAD~3 --json
|
|
231
|
+
drift review --base origin/main --format sarif
|
|
146
232
|
drift review --base origin/main --fail-on 5
|
|
147
233
|
```
|
|
148
234
|
|
|
149
235
|
| Flag | Description |
|
|
150
236
|
|------|-------------|
|
|
151
237
|
| `--base <ref>` | Git base ref to compare against (default: `origin/main`) |
|
|
238
|
+
| `--format <type>` | Output format: `console\|json\|markdown\|sarif` |
|
|
152
239
|
| `--json` | Output structured review JSON |
|
|
153
240
|
| `--comment` | Print only the markdown body for PR comments |
|
|
154
241
|
| `--fail-on <n>` | Exit code 1 when score delta is greater than or equal to `n` |
|
|
@@ -167,11 +254,12 @@ drift trust ./src
|
|
|
167
254
|
drift trust ./src --json
|
|
168
255
|
drift trust ./src --base origin/main
|
|
169
256
|
drift trust ./src --base origin/main --markdown
|
|
257
|
+
drift trust ./src --format sarif
|
|
170
258
|
drift trust ./src --markdown --output trust.md
|
|
171
259
|
drift trust ./src --min-trust 45
|
|
172
260
|
drift trust ./src --max-risk HIGH
|
|
173
261
|
drift trust ./src --branch main
|
|
174
|
-
drift trust ./src --branch release/v1.
|
|
262
|
+
drift trust ./src --branch release/v1.5.0 --policy-pack strict --explain-policy
|
|
175
263
|
drift trust ./src --advanced-trust
|
|
176
264
|
drift trust ./src --advanced-trust --previous-trust ./artifacts/prev-trust.json
|
|
177
265
|
drift trust ./src --advanced-trust --history-file ./drift-history.json --markdown
|
|
@@ -180,6 +268,7 @@ drift trust ./src --advanced-trust --history-file ./drift-history.json --markdow
|
|
|
180
268
|
| Flag | Description |
|
|
181
269
|
|------|-------------|
|
|
182
270
|
| `--base <ref>` | Compare against a git base ref and include deterministic diff-aware penalties/bonuses |
|
|
271
|
+
| `--format <type>` | Output format: `console\|json\|markdown\|sarif` |
|
|
183
272
|
| `--json` | Output structured trust JSON (`trust_score`, `merge_risk`, `top_reasons`, `fix_priorities`, optional `diff_context`) |
|
|
184
273
|
| `--markdown` | Output PR-ready markdown trust summary |
|
|
185
274
|
| `--output <file>` | Write selected trust output format to file |
|
|
@@ -308,6 +397,7 @@ Emit GitHub Actions annotations and a step summary. Designed to run inside a CI
|
|
|
308
397
|
```bash
|
|
309
398
|
drift ci # scan current directory
|
|
310
399
|
drift ci ./src
|
|
400
|
+
drift ci ./src --format sarif
|
|
311
401
|
drift ci ./src --min-score 60
|
|
312
402
|
```
|
|
313
403
|
|
|
@@ -315,7 +405,8 @@ drift ci ./src --min-score 60
|
|
|
315
405
|
|
|
316
406
|
| Flag | Description |
|
|
317
407
|
|------|-------------|
|
|
318
|
-
| `--
|
|
408
|
+
| `--format <type>` | Output format: `console\|json\|sarif` |
|
|
409
|
+
| `--min-score <n>` | Exit with code 1 if the overall score strictly exceeds this threshold |
|
|
319
410
|
|
|
320
411
|
Outputs `::error` and `::warning` annotations visible in the PR diff. Writes a markdown summary to `$GITHUB_STEP_SUMMARY`.
|
|
321
412
|
|
|
@@ -427,36 +518,28 @@ export default {
|
|
|
427
518
|
|
|
428
519
|
## Rules
|
|
429
520
|
|
|
430
|
-
|
|
521
|
+
drift currently defines **35 rule IDs** in `RULE_WEIGHTS` (`src/analyzer.ts`): core detections, configurable architecture checks, AI/meta aggregation, plugin diagnostics, and analysis guardrail diagnostics.
|
|
522
|
+
|
|
523
|
+
For the complete up-to-date catalog (id, severity, weight, phase/origin, note), see [`docs/rules-catalog.md`](./docs/rules-catalog.md).
|
|
524
|
+
|
|
525
|
+
Highlights:
|
|
431
526
|
|
|
432
527
|
| Rule | Severity | Weight | What it detects |
|
|
433
528
|
|------|----------|--------|-----------------|
|
|
434
|
-
| `large-file` | error | 20 | Files exceeding 300 lines
|
|
435
|
-
| `
|
|
436
|
-
| `
|
|
437
|
-
| `
|
|
438
|
-
| `
|
|
439
|
-
| `
|
|
440
|
-
| `
|
|
441
|
-
| `
|
|
442
|
-
| `
|
|
443
|
-
| `
|
|
444
|
-
| `
|
|
445
|
-
| `
|
|
446
|
-
| `
|
|
447
|
-
| `
|
|
448
|
-
| `promise-style-mix` | warning | 7 | `async/await` and `.then()` / `.catch()` used together in the same file — AI combines styles inconsistently |
|
|
449
|
-
| `unused-export` | warning | 8 | Named exports that are never imported anywhere in the project — cross-file dead code ESLint cannot detect |
|
|
450
|
-
| `dead-file` | warning | 10 | Files never imported by any other file in the project — invisible dead code |
|
|
451
|
-
| `unused-dependency` | warning | 6 | Packages listed in `package.json` with no corresponding import in source files |
|
|
452
|
-
| `cross-boundary-import` | warning | 10 | Imports that cross module boundaries outside the allowed list — requires `drift.config.ts` |
|
|
453
|
-
| `hardcoded-config` | warning | 10 | Hardcoded URLs, IP addresses, secrets, or connection strings — AI skips environment variable abstraction |
|
|
454
|
-
| `inconsistent-error-handling` | warning | 8 | Mixed `try/catch` and `.catch()` patterns in the same file — AI combines approaches without a consistent strategy |
|
|
455
|
-
| `unnecessary-abstraction` | warning | 7 | Wrapper functions or helpers that add no logic over what they wrap — AI over-engineers simple calls |
|
|
456
|
-
| `naming-inconsistency` | warning | 6 | Mixed `camelCase` and `snake_case` in the same module — AI forgets project conventions mid-generation |
|
|
457
|
-
| `semantic-duplication` | warning | 12 | Functions with structurally identical logic despite different names — detected via AST fingerprinting, not text comparison |
|
|
458
|
-
| `no-return-type` | info | 5 | Functions missing an explicit return type annotation |
|
|
459
|
-
| `magic-number` | info | 3 | Numeric literals used directly in logic without a named constant |
|
|
529
|
+
| `large-file` | error | 20 | Files exceeding 300 lines |
|
|
530
|
+
| `duplicate-function-name` | error | 18 | Same function name repeated across a file |
|
|
531
|
+
| `high-complexity` | error | 15 | Cyclomatic complexity above threshold |
|
|
532
|
+
| `layer-violation` | error | 16 | Invalid layer import direction (requires `layers` config) |
|
|
533
|
+
| `cross-boundary-import` | warning | 10 | Invalid module boundary import (requires `modules`/legacy boundaries config) |
|
|
534
|
+
| `controller-no-db` | warning | 11 | Controller touching DB directly (configurable architecture rule) |
|
|
535
|
+
| `service-no-http` | warning | 11 | Service layer coupled to HTTP concerns (configurable architecture rule) |
|
|
536
|
+
| `max-function-lines` | warning | 9 | Function line cap enforced by `architectureRules.maxFunctionLines` |
|
|
537
|
+
| `semantic-duplication` | warning | 12 | AST-level semantic function duplication |
|
|
538
|
+
| `ai-code-smell` | warning | 12 | Aggregated AI-smell signal from multiple heuristics in a file |
|
|
539
|
+
| `plugin-error` | warning | 4 | Plugin load/contract/runtime failure surfaced as issue |
|
|
540
|
+
| `plugin-warning` | info | 0 | Non-fatal plugin validation warning |
|
|
541
|
+
| `analysis-skip-max-files` | info | 0 | File skipped by `maxFiles` guardrail |
|
|
542
|
+
| `analysis-skip-file-size` | info | 0 | File skipped by `maxFileSizeKb` guardrail |
|
|
460
543
|
|
|
461
544
|
---
|
|
462
545
|
|
|
@@ -476,7 +559,7 @@ export default {
|
|
|
476
559
|
|
|
477
560
|
## Configuration
|
|
478
561
|
|
|
479
|
-
drift runs with zero configuration. Architectural rules (`layer-violation`, `cross-boundary-import`)
|
|
562
|
+
drift runs with zero configuration. Architectural rules (`layer-violation`, `cross-boundary-import`) and configurable architecture checks use `drift.config.ts` (or `.js` / `.json`) at project root:
|
|
480
563
|
|
|
481
564
|
```typescript
|
|
482
565
|
import type { DriftConfig } from '@eduardbar/drift'
|
|
@@ -509,22 +592,16 @@ export default {
|
|
|
509
592
|
{ name: 'app', patterns: ['src/app/**'], canImportFrom: ['domain'] },
|
|
510
593
|
{ name: 'infra', patterns: ['src/infra/**'], canImportFrom: ['domain', 'app'] },
|
|
511
594
|
],
|
|
512
|
-
|
|
595
|
+
modules: [
|
|
513
596
|
{ name: 'auth', root: 'src/modules/auth', allowedExternalImports: ['src/shared'] },
|
|
514
597
|
{ name: 'billing', root: 'src/modules/billing', allowedExternalImports: ['src/shared'] },
|
|
515
598
|
],
|
|
516
|
-
exclude: [
|
|
517
|
-
'src/generated/**',
|
|
518
|
-
'**/*.spec.ts',
|
|
519
|
-
],
|
|
520
|
-
rules: {
|
|
521
|
-
'large-file': { threshold: 400 }, // override default 300
|
|
522
|
-
'magic-number': 'off', // disable a rule
|
|
523
|
-
},
|
|
524
599
|
} satisfies DriftConfig
|
|
525
600
|
```
|
|
526
601
|
|
|
527
|
-
|
|
602
|
+
For backwards compatibility, `moduleBoundaries` and `boundaries` are normalized to `modules`.
|
|
603
|
+
|
|
604
|
+
Without a config file, `layer-violation`, `cross-boundary-import`, and configurable architecture checks (`controller-no-db`, `service-no-http`, `max-function-lines`) are skipped. All other rules run with defaults.
|
|
528
605
|
|
|
529
606
|
### Memory guardrails (recommended for large repos)
|
|
530
607
|
|
|
@@ -563,7 +640,7 @@ jobs:
|
|
|
563
640
|
run: npx @eduardbar/drift scan ./src --min-score 60
|
|
564
641
|
```
|
|
565
642
|
|
|
566
|
-
Exit code is `1` if the score
|
|
643
|
+
Exit code is `1` if the score is strictly greater than `--min-score`. Exit code `0` otherwise.
|
|
567
644
|
|
|
568
645
|
### Annotations and step summary with `drift ci`
|
|
569
646
|
|
|
@@ -586,6 +663,29 @@ jobs:
|
|
|
586
663
|
|
|
587
664
|
`drift ci` emits `::error` and `::warning` annotations that appear inline in the PR diff and writes a formatted summary to `$GITHUB_STEP_SUMMARY`. Use this when you want visibility beyond a pass/fail exit code.
|
|
588
665
|
|
|
666
|
+
### Required quality checks for merge and release
|
|
667
|
+
|
|
668
|
+
This repository enforces `.github/workflows/quality.yml` on `pull_request` and `push` to `main`/`master`.
|
|
669
|
+
|
|
670
|
+
The workflow runs a required Node.js matrix (`20`, `22`) and executes these checks in each matrix job:
|
|
671
|
+
- `npm ci`
|
|
672
|
+
- `npm run check:runtime-policy`
|
|
673
|
+
- `npm run check:docs-drift`
|
|
674
|
+
- `npm test`
|
|
675
|
+
- `npm run test:coverage`
|
|
676
|
+
- `npm run build`
|
|
677
|
+
- `npm run smoke:repo -- --base HEAD --out .drift-smoke/ci-node-<node>`
|
|
678
|
+
|
|
679
|
+
Additionally, Node 20 runs a deterministic performance regression gate:
|
|
680
|
+
- `npm run check:perf-budget -- --out .drift-perf/ci-node-20/benchmark-latest.json`
|
|
681
|
+
- Budget source of truth: [`benchmarks/perf-budget.v1.json`](./benchmarks/perf-budget.v1.json)
|
|
682
|
+
|
|
683
|
+
`check:docs-drift` enforces documentation source-of-truth contracts: package version claims come from `package.json` and rule catalog/count claims come from `RULE_WEIGHTS` in `src/analyzer.ts`.
|
|
684
|
+
|
|
685
|
+
Coverage artifacts and smoke E2E artifacts are uploaded from each matrix run to support traceability and CI debugging. The perf gate also uploads `.drift-perf/...` artifacts on Node 20.
|
|
686
|
+
|
|
687
|
+
`publish.yml` now runs `release-verify` before publish, reusing the same quality workflow contract. `publish` only runs after `release-verify` passes.
|
|
688
|
+
|
|
589
689
|
### Auto PR comment with `drift review`
|
|
590
690
|
|
|
591
691
|
The repository includes `.github/workflows/review-pr.yml`, which:
|
|
@@ -595,11 +695,45 @@ The repository includes `.github/workflows/review-pr.yml`, which:
|
|
|
595
695
|
- enforces a trust baseline gate with `drift trust-gate drift-trust.json --min-trust 45 --max-risk HIGH`
|
|
596
696
|
- uploads `drift trust --json` as `drift-trust-json-pr-<PR_NUMBER>-run-<RUN_ATTEMPT>` for manual KPI tracking
|
|
597
697
|
- publishes a compact trust KPI summary in `$GITHUB_STEP_SUMMARY` (score, merge risk, new/resolved issues when diff context is available)
|
|
698
|
+
- generates `drift.sarif` via `drift scan --format sarif`, uploads it to GitHub Code Scanning on non-fork PRs, and stores the SARIF file as workflow artifact for traceability
|
|
699
|
+
|
|
700
|
+
Quick local SARIF export:
|
|
701
|
+
|
|
702
|
+
```bash
|
|
703
|
+
drift scan ./src --format sarif > drift.sarif
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
Example GitHub Code Scanning upload with SARIF:
|
|
707
|
+
|
|
708
|
+
```yaml
|
|
709
|
+
- name: Generate drift SARIF
|
|
710
|
+
run: npx @eduardbar/drift scan ./src --format sarif > drift.sarif
|
|
711
|
+
|
|
712
|
+
- name: Upload SARIF to Code Scanning
|
|
713
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
714
|
+
with:
|
|
715
|
+
sarif_file: drift.sarif
|
|
716
|
+
```
|
|
598
717
|
|
|
599
718
|
Default gate behavior in this repo:
|
|
600
719
|
- fail when trust is below 45
|
|
601
720
|
- fail when merge risk is above `HIGH` (that means `CRITICAL` is blocked)
|
|
602
721
|
|
|
722
|
+
### GitHub Action contract (v2)
|
|
723
|
+
|
|
724
|
+
This repo also ships reusable local composite actions:
|
|
725
|
+
|
|
726
|
+
- `./.github/actions/drift-scan`: score-focused scan gate using `npx @eduardbar/drift@<version>` (no global install).
|
|
727
|
+
- `./.github/actions/drift-review`: trust/review/gate flow for PRs, aligned with `.github/workflows/review-pr.yml`.
|
|
728
|
+
|
|
729
|
+
`drift-scan` exposes machine-friendly outputs for CI composition:
|
|
730
|
+
- `score`, `grade`, `errors`, `warnings`, `infos`, `total-issues`, `files-affected`, `top-rules`
|
|
731
|
+
|
|
732
|
+
`drift-review` exposes:
|
|
733
|
+
- `trust-score`, `merge-risk`, `new-issues`, `resolved-issues`, `trust-json`, `trust-markdown`, `review-markdown`
|
|
734
|
+
|
|
735
|
+
See `.github/actions/drift-scan/README.md` and `.github/actions/drift-review/README.md` for usage details.
|
|
736
|
+
|
|
603
737
|
---
|
|
604
738
|
|
|
605
739
|
## drift-ignore
|
|
@@ -673,7 +807,7 @@ See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) before participating.
|
|
|
673
807
|
| [`commander`](https://github.com/tj/commander.js) | CLI commands and flags |
|
|
674
808
|
| [`kleur`](https://github.com/lukeed/kleur) | Terminal colors (zero dependencies) |
|
|
675
809
|
|
|
676
|
-
**Runtime:** Node.js
|
|
810
|
+
**Runtime:** Node.js 20.x and 22.x (LTS) · TypeScript 5.x · ES Modules · Supports TypeScript (`.ts`, `.tsx`) and JavaScript (`.js`, `.jsx`) files
|
|
677
811
|
|
|
678
812
|
---
|
|
679
813
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const config = {
|
|
2
|
+
layers: [
|
|
3
|
+
{ name: 'domain', patterns: ['src/domain/**'], canImportFrom: [] },
|
|
4
|
+
{ name: 'app', patterns: ['src/app/**'], canImportFrom: ['domain'] },
|
|
5
|
+
{ name: 'infra', patterns: ['src/infra/**'], canImportFrom: ['domain', 'app'] },
|
|
6
|
+
],
|
|
7
|
+
architectureRules: {
|
|
8
|
+
controllerNoDb: true,
|
|
9
|
+
serviceNoHttp: true,
|
|
10
|
+
maxFunctionLines: 80,
|
|
11
|
+
},
|
|
12
|
+
performance: {
|
|
13
|
+
lowMemory: false,
|
|
14
|
+
chunkSize: 40,
|
|
15
|
+
maxFiles: 200,
|
|
16
|
+
maxFileSizeKb: 512,
|
|
17
|
+
includeSemanticDuplication: false,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default config
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { canAccessBilling, normalizeEmail, type User } from '../domain/entities.js'
|
|
2
|
+
|
|
3
|
+
export interface UserRepository {
|
|
4
|
+
findByEmail(email: string): Promise<User | undefined>
|
|
5
|
+
save(user: User): Promise<void>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class UserService {
|
|
9
|
+
constructor(private readonly repo: UserRepository) {}
|
|
10
|
+
|
|
11
|
+
async ensureUser(email: string): Promise<User> {
|
|
12
|
+
const normalizedEmail = normalizeEmail(email)
|
|
13
|
+
const existing = await this.repo.findByEmail(normalizedEmail)
|
|
14
|
+
if (existing) return existing
|
|
15
|
+
|
|
16
|
+
const created: User = {
|
|
17
|
+
id: `u-${normalizedEmail}`,
|
|
18
|
+
email: normalizedEmail,
|
|
19
|
+
active: true,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await this.repo.save(created)
|
|
23
|
+
return created
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async canAccessFeature(email: string): Promise<boolean> {
|
|
27
|
+
const user = await this.ensureUser(email)
|
|
28
|
+
return canAccessBilling(user)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type UserId = string
|
|
2
|
+
|
|
3
|
+
export interface User {
|
|
4
|
+
id: UserId
|
|
5
|
+
email: string
|
|
6
|
+
active: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function normalizeEmail(email: string): string {
|
|
10
|
+
return email.trim().toLowerCase()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isCompanyEmail(email: string): boolean {
|
|
14
|
+
return normalizeEmail(email).endsWith('@example.com')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function canAccessBilling(user: User): boolean {
|
|
18
|
+
return user.active && isCompanyEmail(user.email)
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface AccessPolicyInput {
|
|
2
|
+
score: number
|
|
3
|
+
isInternal: boolean
|
|
4
|
+
isTrial: boolean
|
|
5
|
+
hasPaymentIssue: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function computeTrustTier(input: AccessPolicyInput): 'low' | 'medium' | 'high' {
|
|
9
|
+
if (input.hasPaymentIssue) return 'low'
|
|
10
|
+
if (input.score >= 80 && input.isInternal) return 'high'
|
|
11
|
+
if (input.score >= 60 && !input.isTrial) return 'medium'
|
|
12
|
+
return 'low'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function canRunExpensiveChecks(input: AccessPolicyInput): boolean {
|
|
16
|
+
const tier = computeTrustTier(input)
|
|
17
|
+
return tier === 'high' || (tier === 'medium' && input.score >= 70)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function shouldNotifyOps(input: AccessPolicyInput): boolean {
|
|
21
|
+
return input.hasPaymentIssue || (!input.isInternal && input.score < 45)
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { UserService } from './app/user-service.js'
|
|
2
|
+
import { MemoryUserRepository } from './infra/memory-user-repo.js'
|
|
3
|
+
|
|
4
|
+
const service = new UserService(new MemoryUserRepository())
|
|
5
|
+
|
|
6
|
+
export async function runFixtureFlow(): Promise<boolean> {
|
|
7
|
+
return service.canAccessFeature('Demo.User@example.com')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
void runFixtureFlow()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { User } from '../domain/entities.js'
|
|
2
|
+
import type { UserRepository } from '../app/user-service.js'
|
|
3
|
+
|
|
4
|
+
export class MemoryUserRepository implements UserRepository {
|
|
5
|
+
private readonly users = new Map<string, User>()
|
|
6
|
+
|
|
7
|
+
async findByEmail(email: string): Promise<User | undefined> {
|
|
8
|
+
return this.users.get(email)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async save(user: User): Promise<void> {
|
|
12
|
+
this.users.set(user.email, user)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "drift-perf-budget/v1",
|
|
3
|
+
"budgetVersion": "2026-03-26",
|
|
4
|
+
"benchmark": {
|
|
5
|
+
"fixturePath": "benchmarks/fixtures/critical",
|
|
6
|
+
"warmupRuns": 1,
|
|
7
|
+
"measuredRuns": 3
|
|
8
|
+
},
|
|
9
|
+
"tolerance": {
|
|
10
|
+
"runtimePct": 30,
|
|
11
|
+
"memoryPct": 25
|
|
12
|
+
},
|
|
13
|
+
"tasks": {
|
|
14
|
+
"scan": {
|
|
15
|
+
"maxMedianMs": 3800,
|
|
16
|
+
"maxRssMb": 700
|
|
17
|
+
},
|
|
18
|
+
"review": {
|
|
19
|
+
"maxMedianMs": 6500,
|
|
20
|
+
"maxRssMb": 1300
|
|
21
|
+
},
|
|
22
|
+
"trust": {
|
|
23
|
+
"maxMedianMs": 6500,
|
|
24
|
+
"maxRssMb": 1300
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/benchmark.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare function runBenchmarkCli(argv?: string[]): Promise<void>;
|
|
2
2
|
//# sourceMappingURL=benchmark.d.ts.map
|