@kevinrabun/judges 3.54.0 → 3.56.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/CHANGELOG.md +24 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/api-versioning-audit.d.ts +6 -0
- package/dist/commands/api-versioning-audit.d.ts.map +1 -0
- package/dist/commands/api-versioning-audit.js +234 -0
- package/dist/commands/api-versioning-audit.js.map +1 -0
- package/dist/commands/boundary-enforce.d.ts +6 -0
- package/dist/commands/boundary-enforce.d.ts.map +1 -0
- package/dist/commands/boundary-enforce.js +256 -0
- package/dist/commands/boundary-enforce.js.map +1 -0
- package/dist/commands/build-optimize.d.ts +7 -0
- package/dist/commands/build-optimize.d.ts.map +1 -0
- package/dist/commands/build-optimize.js +257 -0
- package/dist/commands/build-optimize.js.map +1 -0
- package/dist/commands/commit-hygiene.d.ts +6 -0
- package/dist/commands/commit-hygiene.d.ts.map +1 -0
- package/dist/commands/commit-hygiene.js +176 -0
- package/dist/commands/commit-hygiene.js.map +1 -0
- package/dist/commands/deploy-readiness.d.ts +6 -0
- package/dist/commands/deploy-readiness.d.ts.map +1 -0
- package/dist/commands/deploy-readiness.js +212 -0
- package/dist/commands/deploy-readiness.js.map +1 -0
- package/dist/commands/error-taxonomy.d.ts +6 -0
- package/dist/commands/error-taxonomy.d.ts.map +1 -0
- package/dist/commands/error-taxonomy.js +227 -0
- package/dist/commands/error-taxonomy.js.map +1 -0
- package/dist/commands/log-quality.d.ts +6 -0
- package/dist/commands/log-quality.d.ts.map +1 -0
- package/dist/commands/log-quality.js +212 -0
- package/dist/commands/log-quality.js.map +1 -0
- package/dist/commands/migration-safety.d.ts +6 -0
- package/dist/commands/migration-safety.d.ts.map +1 -0
- package/dist/commands/migration-safety.js +257 -0
- package/dist/commands/migration-safety.js.map +1 -0
- package/dist/commands/null-safety-audit.d.ts +6 -0
- package/dist/commands/null-safety-audit.d.ts.map +1 -0
- package/dist/commands/null-safety-audit.js +222 -0
- package/dist/commands/null-safety-audit.js.map +1 -0
- package/dist/commands/observability-gap.d.ts +6 -0
- package/dist/commands/observability-gap.d.ts.map +1 -0
- package/dist/commands/observability-gap.js +212 -0
- package/dist/commands/observability-gap.js.map +1 -0
- package/dist/commands/ownership-map.d.ts +6 -0
- package/dist/commands/ownership-map.d.ts.map +1 -0
- package/dist/commands/ownership-map.js +229 -0
- package/dist/commands/ownership-map.js.map +1 -0
- package/dist/commands/retry-pattern-audit.d.ts +6 -0
- package/dist/commands/retry-pattern-audit.d.ts.map +1 -0
- package/dist/commands/retry-pattern-audit.js +216 -0
- package/dist/commands/retry-pattern-audit.js.map +1 -0
- package/dist/commands/rollback-safety.d.ts +5 -0
- package/dist/commands/rollback-safety.d.ts.map +1 -0
- package/dist/commands/rollback-safety.js +192 -0
- package/dist/commands/rollback-safety.js.map +1 -0
- package/dist/commands/secret-age.d.ts +6 -0
- package/dist/commands/secret-age.d.ts.map +1 -0
- package/dist/commands/secret-age.js +215 -0
- package/dist/commands/secret-age.js.map +1 -0
- package/dist/commands/test-isolation.d.ts +6 -0
- package/dist/commands/test-isolation.d.ts.map +1 -0
- package/dist/commands/test-isolation.js +235 -0
- package/dist/commands/test-isolation.js.map +1 -0
- package/dist/commands/test-quality.d.ts +6 -0
- package/dist/commands/test-quality.d.ts.map +1 -0
- package/dist/commands/test-quality.js +161 -0
- package/dist/commands/test-quality.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ownership map — generate and validate code ownership coverage from
|
|
3
|
+
* CODEOWNERS, git history, and module boundaries.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, readdirSync, statSync, existsSync } from "fs";
|
|
6
|
+
import { join, extname, relative } from "path";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
// ─── CODEOWNERS Parsing ─────────────────────────────────────────────────────
|
|
9
|
+
function parseCodeowners(dir) {
|
|
10
|
+
const candidates = [join(dir, "CODEOWNERS"), join(dir, ".github", "CODEOWNERS"), join(dir, "docs", "CODEOWNERS")];
|
|
11
|
+
for (const path of candidates) {
|
|
12
|
+
if (existsSync(path)) {
|
|
13
|
+
const content = readFileSync(path, "utf-8");
|
|
14
|
+
const entries = [];
|
|
15
|
+
for (const line of content.split("\n")) {
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
18
|
+
continue;
|
|
19
|
+
const parts = trimmed.split(/\s+/);
|
|
20
|
+
if (parts.length >= 2) {
|
|
21
|
+
entries.push({ pattern: parts[0], owners: parts.slice(1) });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return entries;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
30
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs", ".cs", ".rb"]);
|
|
31
|
+
function collectSourceFiles(dir, max = 500) {
|
|
32
|
+
const files = [];
|
|
33
|
+
function walk(d) {
|
|
34
|
+
if (files.length >= max)
|
|
35
|
+
return;
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = readdirSync(d);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
for (const e of entries) {
|
|
44
|
+
if (files.length >= max)
|
|
45
|
+
return;
|
|
46
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
47
|
+
continue;
|
|
48
|
+
const full = join(d, e);
|
|
49
|
+
try {
|
|
50
|
+
if (statSync(full).isDirectory())
|
|
51
|
+
walk(full);
|
|
52
|
+
else if (CODE_EXTS.has(extname(full)))
|
|
53
|
+
files.push(full);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* skip */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
walk(dir);
|
|
61
|
+
return files;
|
|
62
|
+
}
|
|
63
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
64
|
+
function matchesPattern(filepath, pattern) {
|
|
65
|
+
if (pattern.endsWith("*")) {
|
|
66
|
+
return filepath.startsWith(pattern.slice(0, -1));
|
|
67
|
+
}
|
|
68
|
+
if (pattern.startsWith("*")) {
|
|
69
|
+
return filepath.endsWith(pattern.slice(1));
|
|
70
|
+
}
|
|
71
|
+
if (pattern.includes("*")) {
|
|
72
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
73
|
+
return regex.test(filepath);
|
|
74
|
+
}
|
|
75
|
+
return filepath.startsWith(pattern) || filepath === pattern;
|
|
76
|
+
}
|
|
77
|
+
function getRecentAuthors(dir, filepath, months = 6) {
|
|
78
|
+
try {
|
|
79
|
+
const since = new Date();
|
|
80
|
+
since.setMonth(since.getMonth() - months);
|
|
81
|
+
const dateStr = since.toISOString().split("T")[0];
|
|
82
|
+
const output = execSync(`git log --since="${dateStr}" --format="%ae" -- "${filepath}"`, {
|
|
83
|
+
cwd: dir,
|
|
84
|
+
encoding: "utf-8",
|
|
85
|
+
timeout: 5000,
|
|
86
|
+
}).trim();
|
|
87
|
+
if (!output)
|
|
88
|
+
return [];
|
|
89
|
+
return [...new Set(output.split("\n"))];
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function analyze(dir) {
|
|
96
|
+
const gaps = [];
|
|
97
|
+
const entries = parseCodeowners(dir);
|
|
98
|
+
const files = collectSourceFiles(dir);
|
|
99
|
+
if (entries.length === 0) {
|
|
100
|
+
gaps.push({
|
|
101
|
+
path: "CODEOWNERS",
|
|
102
|
+
issue: "No CODEOWNERS file",
|
|
103
|
+
severity: "high",
|
|
104
|
+
detail: "Create a CODEOWNERS file to formalize code ownership",
|
|
105
|
+
});
|
|
106
|
+
return { gaps, stats: { total: files.length, owned: 0, orphaned: files.length, stale: 0 } };
|
|
107
|
+
}
|
|
108
|
+
let owned = 0;
|
|
109
|
+
let orphaned = 0;
|
|
110
|
+
let stale = 0;
|
|
111
|
+
// Check top-level directories for ownership
|
|
112
|
+
const dirs = new Set();
|
|
113
|
+
for (const f of files) {
|
|
114
|
+
const rel = relative(dir, f).replace(/\\/g, "/");
|
|
115
|
+
const topDir = rel.split("/")[0];
|
|
116
|
+
dirs.add(topDir);
|
|
117
|
+
// Check if file has an owner
|
|
118
|
+
const hasOwner = entries.some((e) => matchesPattern(rel, e.pattern));
|
|
119
|
+
if (hasOwner) {
|
|
120
|
+
owned++;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
orphaned++;
|
|
124
|
+
if (orphaned <= 15) {
|
|
125
|
+
gaps.push({
|
|
126
|
+
path: rel,
|
|
127
|
+
issue: "No code owner",
|
|
128
|
+
severity: "medium",
|
|
129
|
+
detail: "File has no matching CODEOWNERS entry",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (orphaned > 15) {
|
|
135
|
+
gaps.push({
|
|
136
|
+
path: "(multiple)",
|
|
137
|
+
issue: `${orphaned - 15} more unowned files`,
|
|
138
|
+
severity: "medium",
|
|
139
|
+
detail: "Add broader patterns to CODEOWNERS",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Check for stale owners (paths with owner but no recent commits)
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
const matchingFiles = files.filter((f) => matchesPattern(relative(dir, f).replace(/\\/g, "/"), entry.pattern));
|
|
145
|
+
if (matchingFiles.length === 0) {
|
|
146
|
+
gaps.push({
|
|
147
|
+
path: entry.pattern,
|
|
148
|
+
issue: "CODEOWNERS pattern matches no files",
|
|
149
|
+
severity: "low",
|
|
150
|
+
detail: `Pattern "${entry.pattern}" → ${entry.owners.join(", ")} matches nothing`,
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
// Sample one file for recency
|
|
155
|
+
const sample = matchingFiles[0];
|
|
156
|
+
const authors = getRecentAuthors(dir, sample);
|
|
157
|
+
if (authors.length === 0) {
|
|
158
|
+
stale++;
|
|
159
|
+
gaps.push({
|
|
160
|
+
path: entry.pattern,
|
|
161
|
+
issue: "Stale ownership",
|
|
162
|
+
severity: "medium",
|
|
163
|
+
detail: `No commits in 6 months for "${entry.pattern}" (owners: ${entry.owners.join(", ")})`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Check for overlapping patterns
|
|
168
|
+
for (let i = 0; i < entries.length; i++) {
|
|
169
|
+
for (let j = i + 1; j < entries.length; j++) {
|
|
170
|
+
const iFiles = files.filter((f) => matchesPattern(relative(dir, f).replace(/\\/g, "/"), entries[i].pattern));
|
|
171
|
+
const jFiles = files.filter((f) => matchesPattern(relative(dir, f).replace(/\\/g, "/"), entries[j].pattern));
|
|
172
|
+
const overlap = iFiles.filter((f) => jFiles.includes(f));
|
|
173
|
+
if (overlap.length > 0) {
|
|
174
|
+
gaps.push({
|
|
175
|
+
path: `${entries[i].pattern} ∩ ${entries[j].pattern}`,
|
|
176
|
+
issue: "Overlapping ownership patterns",
|
|
177
|
+
severity: "low",
|
|
178
|
+
detail: `${overlap.length} files match both patterns — later pattern takes precedence`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { gaps, stats: { total: files.length, owned, orphaned, stale } };
|
|
184
|
+
}
|
|
185
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
186
|
+
export function runOwnershipMap(argv) {
|
|
187
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
188
|
+
console.log(`
|
|
189
|
+
judges ownership-map — Generate and validate code ownership coverage
|
|
190
|
+
|
|
191
|
+
Usage:
|
|
192
|
+
judges ownership-map [dir]
|
|
193
|
+
judges ownership-map --format json
|
|
194
|
+
|
|
195
|
+
Options:
|
|
196
|
+
[dir] Directory to scan (default: .)
|
|
197
|
+
--format json JSON output
|
|
198
|
+
--help, -h Show this help
|
|
199
|
+
|
|
200
|
+
Checks: CODEOWNERS coverage, orphaned files, stale owners, overlapping patterns.
|
|
201
|
+
`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
205
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
206
|
+
const { gaps, stats } = analyze(dir);
|
|
207
|
+
const coveragePct = stats.total > 0 ? Math.round((stats.owned / stats.total) * 100) : 100;
|
|
208
|
+
const score = Math.max(0, coveragePct - stats.stale * 5);
|
|
209
|
+
if (format === "json") {
|
|
210
|
+
console.log(JSON.stringify({ gaps, stats, coverage: coveragePct, score, timestamp: new Date().toISOString() }, null, 2));
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
const badge = coveragePct >= 90 ? "✅ GOOD" : coveragePct >= 60 ? "⚠️ PARTIAL" : "❌ LOW";
|
|
214
|
+
console.log(`\n Ownership Coverage: ${badge} (${coveragePct}%)\n ──────────────────────────────`);
|
|
215
|
+
console.log(` Total files: ${stats.total} | Owned: ${stats.owned} | Orphaned: ${stats.orphaned} | Stale: ${stats.stale}\n`);
|
|
216
|
+
if (gaps.length === 0) {
|
|
217
|
+
console.log(" No ownership issues detected.\n");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
for (const g of gaps) {
|
|
221
|
+
const icon = g.severity === "high" ? "🔴" : g.severity === "medium" ? "🟡" : "🔵";
|
|
222
|
+
console.log(` ${icon} ${g.issue}`);
|
|
223
|
+
console.log(` ${g.path}`);
|
|
224
|
+
console.log(` ${g.detail}`);
|
|
225
|
+
}
|
|
226
|
+
console.log(`\n Score: ${score}/100\n`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=ownership-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ownership-map.js","sourceRoot":"","sources":["../../src/commands/ownership-map.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-pattern-audit.d.ts","sourceRoot":"","sources":["../../src/commands/retry-pattern-audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2LH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAiEzD"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry pattern audit — analyze retry, backoff, and circuit-breaker patterns
|
|
3
|
+
* for correctness and consistency.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
6
|
+
import { join, extname } from "path";
|
|
7
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
8
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs"]);
|
|
9
|
+
function collectFiles(dir, max = 300) {
|
|
10
|
+
const files = [];
|
|
11
|
+
function walk(d) {
|
|
12
|
+
if (files.length >= max)
|
|
13
|
+
return;
|
|
14
|
+
let entries;
|
|
15
|
+
try {
|
|
16
|
+
entries = readdirSync(d);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
for (const e of entries) {
|
|
22
|
+
if (files.length >= max)
|
|
23
|
+
return;
|
|
24
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
25
|
+
continue;
|
|
26
|
+
const full = join(d, e);
|
|
27
|
+
try {
|
|
28
|
+
if (statSync(full).isDirectory())
|
|
29
|
+
walk(full);
|
|
30
|
+
else if (CODE_EXTS.has(extname(full)))
|
|
31
|
+
files.push(full);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* skip */
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
walk(dir);
|
|
39
|
+
return files;
|
|
40
|
+
}
|
|
41
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
42
|
+
function analyzeFile(filepath) {
|
|
43
|
+
const issues = [];
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = readFileSync(filepath, "utf-8");
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return issues;
|
|
50
|
+
}
|
|
51
|
+
const lines = content.split("\n");
|
|
52
|
+
for (let i = 0; i < lines.length; i++) {
|
|
53
|
+
const line = lines[i];
|
|
54
|
+
// Unbounded retry loops
|
|
55
|
+
if (/while\s*\(\s*true\s*\)|for\s*\(\s*;\s*;\s*\)/.test(line)) {
|
|
56
|
+
const block = lines.slice(i, Math.min(i + 15, lines.length)).join("\n");
|
|
57
|
+
if (/retry|attempt|fetch|request|connect/i.test(block) &&
|
|
58
|
+
!/maxRetries|maxAttempts|retryLimit|MAX_RETRIES/i.test(block)) {
|
|
59
|
+
issues.push({
|
|
60
|
+
file: filepath,
|
|
61
|
+
line: i + 1,
|
|
62
|
+
issue: "Unbounded retry loop",
|
|
63
|
+
severity: "critical",
|
|
64
|
+
detail: "Infinite retry without max attempts — will loop forever on persistent failures",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Fixed delay (no backoff)
|
|
69
|
+
if (/(?:sleep|setTimeout|time\.Sleep|Thread\.sleep|delay)\s*\(\s*\d+\s*\)/i.test(line)) {
|
|
70
|
+
const block = lines.slice(Math.max(0, i - 5), Math.min(i + 5, lines.length)).join("\n");
|
|
71
|
+
if (/retry|attempt|retries/i.test(block) && !/exponential|backoff|Math\.pow|Math\.min|\*\s*2/i.test(block)) {
|
|
72
|
+
issues.push({
|
|
73
|
+
file: filepath,
|
|
74
|
+
line: i + 1,
|
|
75
|
+
issue: "Fixed retry delay",
|
|
76
|
+
severity: "high",
|
|
77
|
+
detail: "Use exponential backoff with jitter to avoid thundering herd",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Missing jitter
|
|
82
|
+
if (/exponential|backoff|Math\.pow.*2/i.test(line)) {
|
|
83
|
+
const block = lines.slice(i, Math.min(i + 5, lines.length)).join("\n");
|
|
84
|
+
if (!/jitter|Math\.random|random|rand\b/i.test(block)) {
|
|
85
|
+
issues.push({
|
|
86
|
+
file: filepath,
|
|
87
|
+
line: i + 1,
|
|
88
|
+
issue: "Backoff without jitter",
|
|
89
|
+
severity: "medium",
|
|
90
|
+
detail: "Add random jitter to prevent synchronized retry storms",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Retrying non-idempotent operations
|
|
95
|
+
if (/retry.*(?:POST|PUT|DELETE|INSERT|UPDATE|create|write)/i.test(line) ||
|
|
96
|
+
/(?:POST|create|write).*retry/i.test(line)) {
|
|
97
|
+
issues.push({
|
|
98
|
+
file: filepath,
|
|
99
|
+
line: i + 1,
|
|
100
|
+
issue: "Retrying non-idempotent operation",
|
|
101
|
+
severity: "high",
|
|
102
|
+
detail: "POSTs/writes may duplicate data on retry — ensure idempotency keys",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// Hardcoded retry counts
|
|
106
|
+
if (/(?:maxRetries|retryCount|attempts)\s*[:=]\s*\d{2,}/i.test(line)) {
|
|
107
|
+
issues.push({
|
|
108
|
+
file: filepath,
|
|
109
|
+
line: i + 1,
|
|
110
|
+
issue: "High retry count",
|
|
111
|
+
severity: "medium",
|
|
112
|
+
detail: "Large retry counts delay failure detection — consider circuit breaker",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Catching all errors for retry (no differentiation)
|
|
116
|
+
if (/catch\s*\(\s*\w*\s*\)/.test(line)) {
|
|
117
|
+
const block = lines.slice(i, Math.min(i + 8, lines.length)).join("\n");
|
|
118
|
+
if (/retry|continue|attempt/i.test(block) &&
|
|
119
|
+
!/status|code|instanceof|isRetryable|isTransient|4\d{2}|5\d{2}/i.test(block)) {
|
|
120
|
+
issues.push({
|
|
121
|
+
file: filepath,
|
|
122
|
+
line: i + 1,
|
|
123
|
+
issue: "Retrying all errors indiscriminately",
|
|
124
|
+
severity: "high",
|
|
125
|
+
detail: "Only retry transient errors (5xx, timeouts) — 4xx errors won't resolve on retry",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Missing circuit breaker for external calls
|
|
130
|
+
if (/(?:axios|fetch|http\.request|httpClient)\s*\(/i.test(line)) {
|
|
131
|
+
const fileContent = content;
|
|
132
|
+
if (/retry|retries|maxAttempts/i.test(fileContent) &&
|
|
133
|
+
!/circuit.?breaker|CircuitBreaker|opossum|cockatiel|polly/i.test(fileContent)) {
|
|
134
|
+
if (i === lines.findIndex((l) => /(?:axios|fetch|http\.request|httpClient)\s*\(/i.test(l))) {
|
|
135
|
+
issues.push({
|
|
136
|
+
file: filepath,
|
|
137
|
+
line: i + 1,
|
|
138
|
+
issue: "Retries without circuit breaker",
|
|
139
|
+
severity: "medium",
|
|
140
|
+
detail: "Add circuit breaker to fail fast when downstream is unhealthy",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Timeout not set
|
|
146
|
+
if (/(?:axios|fetch|http\.request|httpClient)\s*\(/i.test(line)) {
|
|
147
|
+
const block = lines.slice(i, Math.min(i + 5, lines.length)).join("\n");
|
|
148
|
+
if (!/timeout|signal|AbortController|deadline/i.test(block)) {
|
|
149
|
+
issues.push({
|
|
150
|
+
file: filepath,
|
|
151
|
+
line: i + 1,
|
|
152
|
+
issue: "External call without timeout",
|
|
153
|
+
severity: "high",
|
|
154
|
+
detail: "Set timeouts on all external calls — retries compound unbounded waits",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return issues;
|
|
160
|
+
}
|
|
161
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
162
|
+
export function runRetryPatternAudit(argv) {
|
|
163
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
164
|
+
console.log(`
|
|
165
|
+
judges retry-pattern-audit — Analyze retry, backoff, and circuit-breaker patterns
|
|
166
|
+
|
|
167
|
+
Usage:
|
|
168
|
+
judges retry-pattern-audit [dir]
|
|
169
|
+
judges retry-pattern-audit src/ --format json
|
|
170
|
+
|
|
171
|
+
Options:
|
|
172
|
+
[dir] Directory to scan (default: .)
|
|
173
|
+
--format json JSON output
|
|
174
|
+
--help, -h Show this help
|
|
175
|
+
|
|
176
|
+
Detects: unbounded retries, fixed delays, missing jitter, non-idempotent retries,
|
|
177
|
+
indiscriminate error catching, missing circuit breakers, missing timeouts.
|
|
178
|
+
`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
182
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
183
|
+
const files = collectFiles(dir);
|
|
184
|
+
const allIssues = [];
|
|
185
|
+
for (const f of files)
|
|
186
|
+
allIssues.push(...analyzeFile(f));
|
|
187
|
+
const critCount = allIssues.filter((i) => i.severity === "critical").length;
|
|
188
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
189
|
+
const score = allIssues.length === 0 ? 100 : Math.max(0, 100 - critCount * 25 - highCount * 10);
|
|
190
|
+
if (format === "json") {
|
|
191
|
+
console.log(JSON.stringify({
|
|
192
|
+
issues: allIssues,
|
|
193
|
+
score,
|
|
194
|
+
summary: { critical: critCount, high: highCount, total: allIssues.length },
|
|
195
|
+
timestamp: new Date().toISOString(),
|
|
196
|
+
}, null, 2));
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const badge = critCount > 0 ? "🚫 UNSAFE" : highCount > 0 ? "⚠️ FRAGILE" : "✅ RESILIENT";
|
|
200
|
+
console.log(`\n Retry Pattern Audit: ${badge} (${score}/100)\n ─────────────────────────────`);
|
|
201
|
+
if (allIssues.length === 0) {
|
|
202
|
+
console.log(" No retry pattern issues detected.\n");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
206
|
+
const icon = issue.severity === "critical" ? "🚫" : issue.severity === "high" ? "🔴" : "🟡";
|
|
207
|
+
console.log(` ${icon} [${issue.severity.toUpperCase()}] ${issue.issue}`);
|
|
208
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
209
|
+
console.log(` ${issue.detail}`);
|
|
210
|
+
}
|
|
211
|
+
if (allIssues.length > 25)
|
|
212
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
213
|
+
console.log(`\n Total: ${allIssues.length} | Critical: ${critCount} | High: ${highCount} | Score: ${score}/100\n`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=retry-pattern-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-pattern-audit.js","sourceRoot":"","sources":["../../src/commands/retry-pattern-audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollback-safety.d.ts","sourceRoot":"","sources":["../../src/commands/rollback-safety.ts"],"names":[],"mappings":"AAAA;;GAEG;AAiJH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqEtD"}
|