clud-bug 0.6.29 → 0.6.30
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/bin/clud-bug.js +115 -25
- package/lib/skill-usage.js +170 -0
- package/package.json +1 -1
- package/templates/workflow-py.yml.tmpl +1 -1
- package/templates/workflow-ts.yml.tmpl +1 -1
- package/templates/workflow.yml.tmpl +1 -1
package/bin/clud-bug.js
CHANGED
|
@@ -33,6 +33,10 @@ function parseArgs(argv) {
|
|
|
33
33
|
repo: null, pr: null, limit: null, json: false,
|
|
34
34
|
// 0.0.O (v0.6.22): `clud-bug render` reads its payload from stdin.
|
|
35
35
|
stdin: false,
|
|
36
|
+
// v0.6.30: cross-review aggregation read source for `usage --health`.
|
|
37
|
+
// Defaults to true (artifact mode); `--no-artifacts` forces local
|
|
38
|
+
// .clud-bug.json read (matches v0.6.28 behavior).
|
|
39
|
+
artifacts: true,
|
|
36
40
|
};
|
|
37
41
|
for (let i = 0; i < argv.length; i++) {
|
|
38
42
|
const a = argv[i];
|
|
@@ -53,6 +57,7 @@ function parseArgs(argv) {
|
|
|
53
57
|
else if (a === '--json') args.json = true;
|
|
54
58
|
else if (a === '--stdin') args.stdin = true;
|
|
55
59
|
else if (a === '--health') args.health = true;
|
|
60
|
+
else if (a === '--no-artifacts') args.artifacts = false;
|
|
56
61
|
else args._.push(a);
|
|
57
62
|
}
|
|
58
63
|
return args;
|
|
@@ -80,13 +85,18 @@ Commands:
|
|
|
80
85
|
rate, 30-day rolling \$/LOC trend, per-repo/per-model
|
|
81
86
|
distributions, and outliers (> 2x org median).
|
|
82
87
|
Use --pr / --repo / --since / --limit / --json to filter.
|
|
83
|
-
usage --health Deterministic skill-health dashboard
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
decide
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
usage --health Deterministic skill-health dashboard. Renders archive-
|
|
89
|
+
candidate / stale / new / healthy status per skill, applying
|
|
90
|
+
the v0.6.28 thresholds (citations==0 + loads>=5 → archive
|
|
91
|
+
candidate; last cited >60d → stale; etc.). Read-only —
|
|
92
|
+
humans decide what to prune.
|
|
93
|
+
Read source (v0.6.30): by default, walks
|
|
94
|
+
\`clud-bug-skill-usage-pr-*\` workflow artifacts uploaded
|
|
95
|
+
by every clud-bug-review run and accumulates them into
|
|
96
|
+
one org-level snapshot. Pass \`--repo owner/name\` to
|
|
97
|
+
target a specific repo; otherwise infers from the local
|
|
98
|
+
git remote. \`--no-artifacts\` falls back to reading the
|
|
99
|
+
local \`.claude/skills/.clud-bug.json\` (v0.6.28 behavior).
|
|
90
100
|
eval Run the golden-set regression gate against the rendered review
|
|
91
101
|
prompt (must-contain / must-not-contain / byte-budget). Same as
|
|
92
102
|
\`node --test test/prompts.eval.test.js\` but works from any cwd.
|
|
@@ -985,39 +995,119 @@ async function runUsage(args) {
|
|
|
985
995
|
// v0.6.28 — `clud-bug usage --health` implementation. Reads the local
|
|
986
996
|
// .claude/skills/.clud-bug.json usage block, applies deterministic
|
|
987
997
|
// thresholds, renders a read-only dashboard. No I/O beyond the JSON
|
|
988
|
-
// read.
|
|
989
|
-
//
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
998
|
+
// read.
|
|
999
|
+
//
|
|
1000
|
+
// v0.6.30 — read accumulated usage from workflow artifacts (uploaded
|
|
1001
|
+
// by v0.6.29's post-step). Defaults to artifact mode when --repo is
|
|
1002
|
+
// passed OR an `owner/name` can be inferred from `git remote`. Falls
|
|
1003
|
+
// back to the local-file path otherwise. The `--no-artifacts` flag
|
|
1004
|
+
// forces the v0.6.28 local-only behavior (handy for tests + offline).
|
|
1005
|
+
async function runUsageHealth(args) {
|
|
993
1006
|
const { assessSkillHealth, formatHealthDashboard } = await import('../lib/skill-usage.js');
|
|
994
1007
|
|
|
995
|
-
|
|
1008
|
+
// Decide read source. Priority: explicit --no-artifacts → local;
|
|
1009
|
+
// explicit --repo OR inferred owner/repo → artifacts; else local.
|
|
1010
|
+
const wantArtifacts = args.artifacts !== false;
|
|
1011
|
+
let ownerRepo = null;
|
|
1012
|
+
if (wantArtifacts) {
|
|
1013
|
+
ownerRepo = args.repo || await inferOwnerRepoFromGit();
|
|
1014
|
+
}
|
|
996
1015
|
|
|
997
|
-
let
|
|
1016
|
+
let usage;
|
|
1017
|
+
let source;
|
|
1018
|
+
if (wantArtifacts && ownerRepo) {
|
|
1019
|
+
const result = await loadUsageFromArtifacts(ownerRepo, args);
|
|
1020
|
+
if (result) {
|
|
1021
|
+
usage = result.usage;
|
|
1022
|
+
source = `${result.artifactCount} artifact${result.artifactCount === 1 ? '' : 's'} from ${ownerRepo}`;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// Fallback to local .clud-bug.json (v0.6.28 behavior).
|
|
1027
|
+
if (usage == null) {
|
|
1028
|
+
const localResult = await loadUsageFromLocalFile();
|
|
1029
|
+
if (localResult == null) {
|
|
1030
|
+
// Both paths failed. The local helper has already written its
|
|
1031
|
+
// own stderr explanation; we just exit.
|
|
1032
|
+
process.exit(1);
|
|
1033
|
+
}
|
|
1034
|
+
usage = localResult;
|
|
1035
|
+
source = `local .clud-bug.json`;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const rows = assessSkillHealth(usage, new Date());
|
|
1039
|
+
process.stdout.write(formatHealthDashboard(rows) + '\n');
|
|
1040
|
+
|
|
1041
|
+
// Exit code semantics: 0 (informational). The dashboard is read-only;
|
|
1042
|
+
// archive-candidates being present is NOT a failure mode — humans
|
|
1043
|
+
// decide. CI gates should NOT block on this.
|
|
1044
|
+
ok(`skill health: ${rows.length} skill${rows.length === 1 ? '' : 's'} tracked (source: ${source})`);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// Helpers split out from runUsageHealth so the two read paths are
|
|
1048
|
+
// independently testable + composable in future commands.
|
|
1049
|
+
|
|
1050
|
+
async function loadUsageFromArtifacts(ownerRepo, args) {
|
|
1051
|
+
const { fetchUsageArtifacts, aggregateUsageStream } = await import('../lib/skill-usage.js');
|
|
1052
|
+
const [owner, repo] = ownerRepo.split('/');
|
|
1053
|
+
if (!owner || !repo) {
|
|
1054
|
+
process.stderr.write(`clud-bug usage --health: --repo must be in owner/name form, got "${ownerRepo}".\n`);
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
const since = parseSinceArg(args.since);
|
|
1058
|
+
let artifacts;
|
|
1059
|
+
try {
|
|
1060
|
+
artifacts = await fetchUsageArtifacts({ owner, repo, since });
|
|
1061
|
+
} catch (err) {
|
|
1062
|
+
process.stderr.write(`::notice::clud-bug usage --health: artifact fetch failed (${err.message}) — falling back to local .clud-bug.json\n`);
|
|
1063
|
+
return null;
|
|
1064
|
+
}
|
|
1065
|
+
if (artifacts.length === 0) {
|
|
1066
|
+
process.stderr.write(`::notice::clud-bug usage --health: no skill-usage artifacts found in ${ownerRepo} — falling back to local .clud-bug.json\n`);
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
return {
|
|
1070
|
+
usage: aggregateUsageStream(artifacts),
|
|
1071
|
+
artifactCount: artifacts.length,
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
async function loadUsageFromLocalFile() {
|
|
1076
|
+
const fs = await import('node:fs/promises');
|
|
1077
|
+
const path = await import('node:path');
|
|
1078
|
+
const jsonPath = path.resolve(process.cwd(), '.claude', 'skills', '.clud-bug.json');
|
|
998
1079
|
try {
|
|
999
1080
|
const raw = await fs.readFile(jsonPath, 'utf-8');
|
|
1000
|
-
parsed = JSON.parse(raw);
|
|
1081
|
+
const parsed = JSON.parse(raw);
|
|
1082
|
+
return (parsed && parsed.usage) ? parsed.usage : {};
|
|
1001
1083
|
} catch (err) {
|
|
1002
1084
|
if (err.code === 'ENOENT') {
|
|
1003
1085
|
process.stderr.write(
|
|
1004
1086
|
`clud-bug usage --health: no .claude/skills/.clud-bug.json found in ${process.cwd()}.\n` +
|
|
1005
|
-
`Run \`npx clud-bug init\` first to
|
|
1087
|
+
`Run \`npx clud-bug init\` first OR pass --repo owner/name to read from workflow artifacts.\n`
|
|
1006
1088
|
);
|
|
1007
|
-
|
|
1089
|
+
return null;
|
|
1008
1090
|
}
|
|
1009
1091
|
process.stderr.write(`clud-bug usage --health: failed to parse .clud-bug.json: ${err.message}\n`);
|
|
1010
|
-
|
|
1092
|
+
return null;
|
|
1011
1093
|
}
|
|
1094
|
+
}
|
|
1012
1095
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1096
|
+
async function inferOwnerRepoFromGit() {
|
|
1097
|
+
// `gh repo view --json nameWithOwner` reads the current dir's git
|
|
1098
|
+
// remote AND respects gh's config. Returns null on non-git dirs.
|
|
1099
|
+
const result = await ghJson(['repo', 'view', '--json', 'nameWithOwner']);
|
|
1100
|
+
return result && result.nameWithOwner ? result.nameWithOwner : null;
|
|
1101
|
+
}
|
|
1016
1102
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1103
|
+
function parseSinceArg(since) {
|
|
1104
|
+
if (!since) return null;
|
|
1105
|
+
if (since instanceof Date) return since;
|
|
1106
|
+
const m = String(since).match(/^(\d+)([dwmy])$/);
|
|
1107
|
+
if (!m) return null;
|
|
1108
|
+
const n = Number(m[1]);
|
|
1109
|
+
const unitMs = { d: 86400e3, w: 7 * 86400e3, m: 30 * 86400e3, y: 365 * 86400e3 }[m[2]];
|
|
1110
|
+
return new Date(Date.now() - n * unitMs);
|
|
1021
1111
|
}
|
|
1022
1112
|
|
|
1023
1113
|
// limit to 100 to avoid pagination explosions.
|
package/lib/skill-usage.js
CHANGED
|
@@ -260,3 +260,173 @@ export function formatHealthDashboard(rows) {
|
|
|
260
260
|
lines.push(' healthy = cited within 60 days');
|
|
261
261
|
return lines.join('\n');
|
|
262
262
|
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// v0.6.30 — cross-review aggregation
|
|
266
|
+
//
|
|
267
|
+
// The v0.6.29 workflow post-step uploads `.clud-bug.json` as a per-PR
|
|
268
|
+
// artifact named `clud-bug-skill-usage-pr-<N>` (90-day retention). This
|
|
269
|
+
// section walks the artifact stream + accumulates into one dashboard read.
|
|
270
|
+
//
|
|
271
|
+
// Artifact persistence design choice (recap from v0.6.29): we picked
|
|
272
|
+
// artifacts over commit-back-to-main because commit-back required
|
|
273
|
+
// `contents: write` permission expansion — v0.6.23 hit a regression
|
|
274
|
+
// from a similar expansion. Artifacts are GitHub-native, zero perm
|
|
275
|
+
// widening, zero commit noise. v0.6.30 reads them back here.
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Default `gh` runner — spawns the local gh CLI. Tests inject a mock.
|
|
280
|
+
*
|
|
281
|
+
* The runner has two methods:
|
|
282
|
+
* - json(args): returns parsed JSON stdout, or null on error.
|
|
283
|
+
* - run(args): returns {code, stdout, stderr}. For commands that
|
|
284
|
+
* download files etc. — no JSON parsing.
|
|
285
|
+
*/
|
|
286
|
+
async function defaultGhJson(args) {
|
|
287
|
+
const { spawn } = await import('node:child_process');
|
|
288
|
+
return new Promise((resolve) => {
|
|
289
|
+
const child = spawn('gh', args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
290
|
+
let stdout = '';
|
|
291
|
+
child.stdout.on('data', (d) => { stdout += d; });
|
|
292
|
+
child.on('error', () => resolve(null));
|
|
293
|
+
child.on('close', (code) => {
|
|
294
|
+
if (code !== 0) return resolve(null);
|
|
295
|
+
try { resolve(JSON.parse(stdout)); } catch { resolve(null); }
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function defaultGhRun(args) {
|
|
301
|
+
const { spawn } = await import('node:child_process');
|
|
302
|
+
return new Promise((resolve) => {
|
|
303
|
+
const child = spawn('gh', args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
304
|
+
let stdout = '';
|
|
305
|
+
let stderr = '';
|
|
306
|
+
child.stdout.on('data', (d) => { stdout += d; });
|
|
307
|
+
child.stderr.on('data', (d) => { stderr += d; });
|
|
308
|
+
child.on('error', () => resolve({ code: 1, stdout: '', stderr: 'gh not on PATH' }));
|
|
309
|
+
child.on('close', (code) => resolve({ code, stdout, stderr }));
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export const DEFAULT_GH_RUNNER = {
|
|
314
|
+
json: defaultGhJson,
|
|
315
|
+
run: defaultGhRun,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Fetch all per-PR skill-usage artifacts from a repo. Each artifact is
|
|
320
|
+
* downloaded to a temp dir, its `.clud-bug.json` is parsed, and the
|
|
321
|
+
* usage block is returned.
|
|
322
|
+
*
|
|
323
|
+
* @param {object} opts
|
|
324
|
+
* @param {string} opts.owner - GitHub repo owner.
|
|
325
|
+
* @param {string} opts.repo - GitHub repo name.
|
|
326
|
+
* @param {Date|null} opts.since - Filter to artifacts created on/after this date.
|
|
327
|
+
* @param {object} opts.ghRunner - Injected gh CLI runner (see DEFAULT_GH_RUNNER).
|
|
328
|
+
*
|
|
329
|
+
* @returns {Promise<{prNumber: number, artifactId: number, usage: object, fetchedAt: string}[]>}
|
|
330
|
+
*/
|
|
331
|
+
export async function fetchUsageArtifacts({ owner, repo, since = null, ghRunner = DEFAULT_GH_RUNNER }) {
|
|
332
|
+
if (!owner || !repo) {
|
|
333
|
+
throw new Error('fetchUsageArtifacts: owner + repo are required');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// List artifacts in one call. We deliberately do NOT use `--paginate`:
|
|
337
|
+
// `gh api --paginate --jq <expr>` applies the jq filter to EACH page
|
|
338
|
+
// independently and concatenates the outputs with newlines, which
|
|
339
|
+
// produces `[...]\n[...]` — invalid as a single JSON document.
|
|
340
|
+
// `JSON.parse` returns null and the dashboard silently shows nothing
|
|
341
|
+
// for repos with >30 artifacts (default page size). Caught by
|
|
342
|
+
// clud-bug-review on PR #127.
|
|
343
|
+
//
|
|
344
|
+
// `?per_page=100` covers up to 100 artifacts in one call. The 90-day
|
|
345
|
+
// artifact retention means most repos won't hit that ceiling (>100
|
|
346
|
+
// PR reviews in 90 days = >1/day sustained). If a future repo
|
|
347
|
+
// saturates this, paginate manually in v0.6.31+ (per_page=100 +
|
|
348
|
+
// explicit `?page=N` loop, parse each response as JSON, concatenate).
|
|
349
|
+
const list = await ghRunner.json([
|
|
350
|
+
'api',
|
|
351
|
+
`repos/${owner}/${repo}/actions/artifacts?per_page=100`,
|
|
352
|
+
'--jq',
|
|
353
|
+
'[.artifacts[] | select(.name | startswith("clud-bug-skill-usage-pr-")) | select(.expired == false) | {id, name, workflow_run_id: .workflow_run.id, created_at}]',
|
|
354
|
+
]);
|
|
355
|
+
|
|
356
|
+
// `--jq '[...]'` wraps the stream into a single array. If the runner
|
|
357
|
+
// returns null (404, no auth, etc.), bail to empty list.
|
|
358
|
+
if (!Array.isArray(list)) return [];
|
|
359
|
+
|
|
360
|
+
const filtered = since
|
|
361
|
+
? list.filter((a) => new Date(a.created_at) >= since)
|
|
362
|
+
: list;
|
|
363
|
+
|
|
364
|
+
const fs = await import('node:fs/promises');
|
|
365
|
+
const path = await import('node:path');
|
|
366
|
+
const os = await import('node:os');
|
|
367
|
+
|
|
368
|
+
const results = [];
|
|
369
|
+
for (const art of filtered) {
|
|
370
|
+
const prMatch = art.name.match(/^clud-bug-skill-usage-pr-(\d+)$/);
|
|
371
|
+
if (!prMatch) continue;
|
|
372
|
+
const prNumber = Number(prMatch[1]);
|
|
373
|
+
|
|
374
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'clud-bug-art-'));
|
|
375
|
+
try {
|
|
376
|
+
const dl = await ghRunner.run([
|
|
377
|
+
'run', 'download', String(art.workflow_run_id),
|
|
378
|
+
'-R', `${owner}/${repo}`,
|
|
379
|
+
'-n', art.name,
|
|
380
|
+
'-D', tmpDir,
|
|
381
|
+
]);
|
|
382
|
+
if (dl.code !== 0) continue;
|
|
383
|
+
|
|
384
|
+
// The workflow uploaded `.clud-bug.json` (single file under the
|
|
385
|
+
// path key). `gh run download -D <dir>` writes it to the dest as
|
|
386
|
+
// `<dir>/.clud-bug.json` (preserves the source path).
|
|
387
|
+
const jsonPath = path.join(tmpDir, '.clud-bug.json');
|
|
388
|
+
let parsed;
|
|
389
|
+
try {
|
|
390
|
+
const raw = await fs.readFile(jsonPath, 'utf-8');
|
|
391
|
+
parsed = JSON.parse(raw);
|
|
392
|
+
} catch {
|
|
393
|
+
continue; // artifact corrupted or layout unexpected
|
|
394
|
+
}
|
|
395
|
+
const usage = parsed && parsed.usage ? parsed.usage : {};
|
|
396
|
+
results.push({
|
|
397
|
+
prNumber,
|
|
398
|
+
artifactId: art.id,
|
|
399
|
+
usage,
|
|
400
|
+
fetchedAt: art.created_at,
|
|
401
|
+
});
|
|
402
|
+
} finally {
|
|
403
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return results;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Reduce an array of per-PR artifact records into a single accumulated
|
|
412
|
+
* usage block by left-folding `mergeSkillUsage` over them, ordered by
|
|
413
|
+
* `fetchedAt` ascending so the last_cited timestamp is deterministic.
|
|
414
|
+
*
|
|
415
|
+
* Because `mergeSkillUsage` is commutative for loads + citations counts
|
|
416
|
+
* AND keeps the LATEST timestamp it sees as last_cited (we sort
|
|
417
|
+
* ascending so newest wins on the final pass), out-of-order input
|
|
418
|
+
* produces an identical result.
|
|
419
|
+
*
|
|
420
|
+
* @param {{usage: object, fetchedAt: string}[]} artifacts
|
|
421
|
+
* @returns {object} accumulated usage block, shape matches mergeSkillUsage output
|
|
422
|
+
*/
|
|
423
|
+
export function aggregateUsageStream(artifacts) {
|
|
424
|
+
if (!Array.isArray(artifacts) || artifacts.length === 0) return {};
|
|
425
|
+
const sorted = [...artifacts].sort(
|
|
426
|
+
(a, b) => new Date(a.fetchedAt) - new Date(b.fetchedAt)
|
|
427
|
+
);
|
|
428
|
+
return sorted.reduce(
|
|
429
|
+
(acc, art) => mergeSkillUsage(acc, art.usage || {}, art.fetchedAt),
|
|
430
|
+
{}
|
|
431
|
+
);
|
|
432
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clud-bug",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.30",
|
|
4
4
|
"description": "Skill-driven Claude PR review. Ship a brand-voice skill, get brand reviews. Each finding cites the skill that motivated it. CLI installs the workflow + a baseline kit; add more from skills.sh.",
|
|
5
5
|
"homepage": "https://cludbug.dev",
|
|
6
6
|
"bugs": "https://github.com/thrillmade/clud-bug/issues",
|
|
@@ -363,7 +363,7 @@ jobs:
|
|
|
363
363
|
# Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
|
|
364
364
|
- name: Strict mode — fail check on critical findings
|
|
365
365
|
if: success()
|
|
366
|
-
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.
|
|
366
|
+
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.30
|
|
367
367
|
with:
|
|
368
368
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
369
369
|
# v0.6.22 / 0.0.O: summary now posted by github-actions[bot].
|
|
@@ -363,7 +363,7 @@ jobs:
|
|
|
363
363
|
# Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
|
|
364
364
|
- name: Strict mode — fail check on critical findings
|
|
365
365
|
if: success()
|
|
366
|
-
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.
|
|
366
|
+
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.30
|
|
367
367
|
with:
|
|
368
368
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
369
369
|
# v0.6.22 / 0.0.O: summary now posted by github-actions[bot].
|
|
@@ -627,7 +627,7 @@ jobs:
|
|
|
627
627
|
# Letting the action's own failure fail the check is louder and right.
|
|
628
628
|
- name: Strict mode — fail check on critical findings
|
|
629
629
|
if: success()
|
|
630
|
-
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.
|
|
630
|
+
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.30
|
|
631
631
|
with:
|
|
632
632
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
633
633
|
# v0.6.22 / 0.0.O: the summary is now posted by the workflow
|