clud-bug 0.6.25 → 0.6.27

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/lib/prompts.js CHANGED
@@ -201,6 +201,36 @@ Rules:
201
201
  every file's verdict so a maintainer can verify nothing was
202
202
  skipped.
203
203
 
204
+ Mid-review self-check-in (v0.6.27 / §5.5 Layer 3):
205
+ After every 5 tool_uses, write a single-line budget heartbeat as a
206
+ free-text "thinking" message (not a tool call — these don't cost a
207
+ turn) of the form:
208
+
209
+ [budget] files_reviewed=X/N, turns_used=Y/M, pace=ok|behind
210
+
211
+ Where:
212
+ - X / N is the count of files you've meaningfully looked at so far
213
+ over the total in this PR's diff.
214
+ - Y / M is your current turn count over max_turns.
215
+ - pace = "ok" when X / N >= Y / (M - 5). The denominator subtracts the
216
+ 5-turn emit reservation: over the (M - 5) turns available for file
217
+ review, your file-coverage rate must match where you actually are
218
+ in the budget. (Don't subtract from Y — that would be saying "I've
219
+ used Y minus 5 turns" which double-counts the reservation.)
220
+ pace = "behind" otherwise.
221
+
222
+ When pace = "behind", immediately pivot strategy:
223
+ 1. Stop deep-dive analysis on the current file.
224
+ 2. Switch to one-sentence verdicts for every remaining file.
225
+ 3. Keep going through the whole diff — silent skipping is
226
+ non-negotiable. Cover everything, even if some files only get
227
+ "no issues found in this file" as their verdict.
228
+
229
+ The heartbeat serves two purposes: (a) forces internal pacing — you
230
+ can't drift past budget without noticing; (b) lands in the action's
231
+ streaming output for post-hoc calibration of the per-line cost
232
+ coefficients used by paths-check's Layer 1 estimator.
233
+
204
234
  Incremental-diff handshake (v0.6.10+) — emit the SHA marker:
205
235
  At the very end of the summary (after the Skills-referenced footer,
206
236
  on its own line), append:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clud-bug",
3
- "version": "0.6.25",
3
+ "version": "0.6.27",
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",
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v11
1
+ # clud-bug-template-version: v12
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -53,17 +53,32 @@ jobs:
53
53
  } >> "$GITHUB_OUTPUT"
54
54
  exit 0
55
55
  fi
56
- IS_WORKFLOW_ONLY=true
56
+ # v0.6.26 / 0.0.W² — see workflow.yml.tmpl for design notes.
57
+ ALL_IN_ALLOWLIST=true
58
+ HAS_WORKFLOW_CHANGE=false
57
59
  while IFS= read -r f; do
58
60
  case "$f" in
59
- .github/workflows/clud-bug-*.yml) ;;
60
- .github/actions/strict-mode-gate/*) ;;
61
- *) IS_WORKFLOW_ONLY=false; break ;;
61
+ .github/workflows/clud-bug-*.yml) HAS_WORKFLOW_CHANGE=true ;;
62
+ .github/actions/strict-mode-gate/*) HAS_WORKFLOW_CHANGE=true ;;
63
+ AGENTS.md) ;;
64
+ .cursorrules|.clinerules|.windsurfrules|.continuerules) ;;
65
+ .github/copilot-instructions.md) ;;
66
+ .claude/skills/.clud-bug.json) ;;
67
+ .claude/skills/critical-issues-only/SKILL.md) ;;
68
+ .claude/skills/evidence-based-review/SKILL.md) ;;
69
+ .claude/skills/respect-existing-conventions/SKILL.md) ;;
70
+ docs/timeline.md|docs/file-structure.md|docs/decisions.md) ;;
71
+ docs/decisions-branches/*.md) ;;
72
+ *) ALL_IN_ALLOWLIST=false; break ;;
62
73
  esac
63
74
  done <<< "$CHANGED"
75
+ IS_WORKFLOW_ONLY=false
76
+ if [ "$ALL_IN_ALLOWLIST" = "true" ] && [ "$HAS_WORKFLOW_CHANGE" = "true" ]; then
77
+ IS_WORKFLOW_ONLY=true
78
+ fi
64
79
  echo "is_workflow_only=$IS_WORKFLOW_ONLY" >> "$GITHUB_OUTPUT"
65
80
  if [ "$IS_WORKFLOW_ONLY" = "true" ]; then
66
- echo "::notice title=Clud Bug 🐛::Skipping LLM review — workflow-only PR."
81
+ echo "::notice title=Clud Bug 🐛::Skipping LLM review — clud-bug update output."
67
82
  echo "model=$MODEL" >> "$GITHUB_OUTPUT"
68
83
  exit 0
69
84
  fi
@@ -268,28 +283,63 @@ jobs:
268
283
 
269
284
  $CALIBRATION"
270
285
 
271
- # Fallback when structured_output is empty (max-retries hit).
286
+ # v0.6.26 / §5.5 Layer 6 fallback render-from-inlines — see workflow.yml.tmpl for design notes.
272
287
  - name: Fallback summary (structured_output empty)
273
288
  if: success() && steps.clud-bug-review.outputs.structured_output == ''
274
289
  env:
275
290
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
276
291
  PR_NUMBER: ${{ github.event.pull_request.number }}
292
+ REPO: ${{ github.repository }}
293
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
294
+ TURNS_ESTIMATED: ${{ needs.paths-check.outputs.turns_estimated }}
295
+ MAX_TURNS: ${{ needs.paths-check.outputs.max_turns }}
296
+ FILES_COUNT: ${{ needs.paths-check.outputs.files_count }}
297
+ LINES_ADDED: ${{ needs.paths-check.outputs.lines_added }}
298
+ LINES_DELETED: ${{ needs.paths-check.outputs.lines_deleted }}
299
+ THREADS_COUNT: ${{ needs.paths-check.outputs.threads_count }}
277
300
  run: |
278
301
  set -euo pipefail
279
- gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review
302
+ INLINES=$(gh api "repos/$REPO/pulls/$PR_NUMBER/comments?per_page=100" \
303
+ --jq "[.[] | select(.user.login == \"claude[bot]\" and .commit_id == \"$HEAD_SHA\")]")
304
+ INLINE_COUNT=$(echo "$INLINES" | jq 'length')
305
+ CRITICAL=$(echo "$INLINES" | jq '[.[] | select(.body | test("🔴"))] | length')
306
+ MINOR=$(echo "$INLINES" | jq '[.[] | select(.body | test("🟡"))] | length')
307
+ PREEXISTING=$(echo "$INLINES" | jq '[.[] | select(.body | test("🟣"))] | length')
308
+ CALIBRATION="<!-- clud-bug-calibration: turns_estimated=$TURNS_ESTIMATED, max_turns=$MAX_TURNS, files=$FILES_COUNT, lines_added=$LINES_ADDED, lines_deleted=$LINES_DELETED, threads=$THREADS_COUNT, structured_output=empty, inline_findings=$INLINE_COUNT -->"
309
+ if [ "$INLINE_COUNT" -gt 0 ]; then
310
+ STATUS=""
311
+ [ "$CRITICAL" -gt 0 ] && STATUS=" — critical findings"
312
+ gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review${STATUS}
313
+
314
+ **This round:** $CRITICAL critical · $MINOR minor · 0 resolved from prior · 0 still open
315
+
316
+ Found: $CRITICAL 🔴 / $MINOR 🟡 / $PREEXISTING 🟣
317
+
318
+ ⚠️ **Synthetic summary** (v0.6.26 §5.5 Layer 6 fallback). Inline findings above ARE the substantive review.
319
+
320
+ <!-- last-reviewed-sha: $HEAD_SHA -->
321
+
322
+ $CALIBRATION"
323
+ else
324
+ gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review
280
325
 
281
326
  **This round:** 0 critical · 0 minor · 0 resolved from prior · 0 still open
282
327
 
283
328
  Found: 0 🔴 / 0 🟡 / 0 🟣
284
329
 
285
- ⚠️ Structured output (\`--json-schema\`) returned empty likely max-retries hit on schema validation. Investigate the action logs.
330
+ ⚠️ Structured output (\`--json-schema\`) returned empty AND no inline findings posted. Investigate run logs.
331
+
332
+ Skills referenced: [none]
333
+
334
+ <!-- last-reviewed-sha: $HEAD_SHA -->
286
335
 
287
- Skills referenced: [none]"
336
+ $CALIBRATION"
337
+ fi
288
338
 
289
339
  # Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
290
340
  - name: Strict mode — fail check on critical findings
291
341
  if: success()
292
- uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.25
342
+ uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.27
293
343
  with:
294
344
  github-token: ${{ secrets.GITHUB_TOKEN }}
295
345
  # v0.6.22 / 0.0.O: summary now posted by github-actions[bot].
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v11
1
+ # clud-bug-template-version: v12
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -53,17 +53,32 @@ jobs:
53
53
  } >> "$GITHUB_OUTPUT"
54
54
  exit 0
55
55
  fi
56
- IS_WORKFLOW_ONLY=true
56
+ # v0.6.26 / 0.0.W² — see workflow.yml.tmpl for design notes.
57
+ ALL_IN_ALLOWLIST=true
58
+ HAS_WORKFLOW_CHANGE=false
57
59
  while IFS= read -r f; do
58
60
  case "$f" in
59
- .github/workflows/clud-bug-*.yml) ;;
60
- .github/actions/strict-mode-gate/*) ;;
61
- *) IS_WORKFLOW_ONLY=false; break ;;
61
+ .github/workflows/clud-bug-*.yml) HAS_WORKFLOW_CHANGE=true ;;
62
+ .github/actions/strict-mode-gate/*) HAS_WORKFLOW_CHANGE=true ;;
63
+ AGENTS.md) ;;
64
+ .cursorrules|.clinerules|.windsurfrules|.continuerules) ;;
65
+ .github/copilot-instructions.md) ;;
66
+ .claude/skills/.clud-bug.json) ;;
67
+ .claude/skills/critical-issues-only/SKILL.md) ;;
68
+ .claude/skills/evidence-based-review/SKILL.md) ;;
69
+ .claude/skills/respect-existing-conventions/SKILL.md) ;;
70
+ docs/timeline.md|docs/file-structure.md|docs/decisions.md) ;;
71
+ docs/decisions-branches/*.md) ;;
72
+ *) ALL_IN_ALLOWLIST=false; break ;;
62
73
  esac
63
74
  done <<< "$CHANGED"
75
+ IS_WORKFLOW_ONLY=false
76
+ if [ "$ALL_IN_ALLOWLIST" = "true" ] && [ "$HAS_WORKFLOW_CHANGE" = "true" ]; then
77
+ IS_WORKFLOW_ONLY=true
78
+ fi
64
79
  echo "is_workflow_only=$IS_WORKFLOW_ONLY" >> "$GITHUB_OUTPUT"
65
80
  if [ "$IS_WORKFLOW_ONLY" = "true" ]; then
66
- echo "::notice title=Clud Bug 🐛::Skipping LLM review — workflow-only PR."
81
+ echo "::notice title=Clud Bug 🐛::Skipping LLM review — clud-bug update output."
67
82
  echo "model=$MODEL" >> "$GITHUB_OUTPUT"
68
83
  exit 0
69
84
  fi
@@ -268,28 +283,63 @@ jobs:
268
283
 
269
284
  $CALIBRATION"
270
285
 
271
- # Fallback when structured_output is empty (max-retries hit).
286
+ # v0.6.26 / §5.5 Layer 6 fallback render-from-inlines — see workflow.yml.tmpl for design notes.
272
287
  - name: Fallback summary (structured_output empty)
273
288
  if: success() && steps.clud-bug-review.outputs.structured_output == ''
274
289
  env:
275
290
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
276
291
  PR_NUMBER: ${{ github.event.pull_request.number }}
292
+ REPO: ${{ github.repository }}
293
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
294
+ TURNS_ESTIMATED: ${{ needs.paths-check.outputs.turns_estimated }}
295
+ MAX_TURNS: ${{ needs.paths-check.outputs.max_turns }}
296
+ FILES_COUNT: ${{ needs.paths-check.outputs.files_count }}
297
+ LINES_ADDED: ${{ needs.paths-check.outputs.lines_added }}
298
+ LINES_DELETED: ${{ needs.paths-check.outputs.lines_deleted }}
299
+ THREADS_COUNT: ${{ needs.paths-check.outputs.threads_count }}
277
300
  run: |
278
301
  set -euo pipefail
279
- gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review
302
+ INLINES=$(gh api "repos/$REPO/pulls/$PR_NUMBER/comments?per_page=100" \
303
+ --jq "[.[] | select(.user.login == \"claude[bot]\" and .commit_id == \"$HEAD_SHA\")]")
304
+ INLINE_COUNT=$(echo "$INLINES" | jq 'length')
305
+ CRITICAL=$(echo "$INLINES" | jq '[.[] | select(.body | test("🔴"))] | length')
306
+ MINOR=$(echo "$INLINES" | jq '[.[] | select(.body | test("🟡"))] | length')
307
+ PREEXISTING=$(echo "$INLINES" | jq '[.[] | select(.body | test("🟣"))] | length')
308
+ CALIBRATION="<!-- clud-bug-calibration: turns_estimated=$TURNS_ESTIMATED, max_turns=$MAX_TURNS, files=$FILES_COUNT, lines_added=$LINES_ADDED, lines_deleted=$LINES_DELETED, threads=$THREADS_COUNT, structured_output=empty, inline_findings=$INLINE_COUNT -->"
309
+ if [ "$INLINE_COUNT" -gt 0 ]; then
310
+ STATUS=""
311
+ [ "$CRITICAL" -gt 0 ] && STATUS=" — critical findings"
312
+ gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review${STATUS}
313
+
314
+ **This round:** $CRITICAL critical · $MINOR minor · 0 resolved from prior · 0 still open
315
+
316
+ Found: $CRITICAL 🔴 / $MINOR 🟡 / $PREEXISTING 🟣
317
+
318
+ ⚠️ **Synthetic summary** (v0.6.26 §5.5 Layer 6 fallback). Inline findings above ARE the substantive review.
319
+
320
+ <!-- last-reviewed-sha: $HEAD_SHA -->
321
+
322
+ $CALIBRATION"
323
+ else
324
+ gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review
280
325
 
281
326
  **This round:** 0 critical · 0 minor · 0 resolved from prior · 0 still open
282
327
 
283
328
  Found: 0 🔴 / 0 🟡 / 0 🟣
284
329
 
285
- ⚠️ Structured output (\`--json-schema\`) returned empty likely max-retries hit on schema validation. Investigate the action logs.
330
+ ⚠️ Structured output (\`--json-schema\`) returned empty AND no inline findings posted. Investigate run logs.
331
+
332
+ Skills referenced: [none]
333
+
334
+ <!-- last-reviewed-sha: $HEAD_SHA -->
286
335
 
287
- Skills referenced: [none]"
336
+ $CALIBRATION"
337
+ fi
288
338
 
289
339
  # Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
290
340
  - name: Strict mode — fail check on critical findings
291
341
  if: success()
292
- uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.25
342
+ uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.27
293
343
  with:
294
344
  github-token: ${{ secrets.GITHUB_TOKEN }}
295
345
  # v0.6.22 / 0.0.O: summary now posted by github-actions[bot].
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v11
1
+ # clud-bug-template-version: v12
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -18,12 +18,45 @@ jobs:
18
18
  # Pre-flight: classify the PR diff to decide (a) whether to skip the
19
19
  # LLM review entirely, and (b) which model to route to.
20
20
  #
21
- # (a) Workflow-only PRs (v0.6.14 / 0.0.W): if every changed file
22
- # matches `.github/workflows/clud-bug-*.yml` or
23
- # `.github/actions/strict-mode-gate/**`, the LLM call is skipped
24
- # entirely claude-code-action would refuse anyway (self-modification
25
- # guard), and template re-renders have no useful review surface.
26
- # Skipping converts what were admin-bypass merges into normal ones.
21
+ # (a) Workflow-only / clud-bug-update PRs (v0.6.14 / 0.0.W;
22
+ # v0.6.26 / 0.0.W²): the LLM call is skipped when EITHER condition:
23
+ #
24
+ # - Every changed file matches `.github/workflows/clud-bug-*.yml`
25
+ # or `.github/actions/strict-mode-gate/**` (original 0.0.W
26
+ # pure workflow PRs that claude-code-action would refuse anyway
27
+ # via its self-modification guard).
28
+ #
29
+ # - The PR is a recognized `clud-bug update` propagation: every
30
+ # changed file is in the clud-bug-update output allowlist AND a
31
+ # workflow file (or strict-mode-gate composite) is part of the
32
+ # change. v0.6.26 / 0.0.W² added this branch so the propagation
33
+ # dance doesn't require admin-bypass every cycle.
34
+ #
35
+ # Allowlist (files `clud-bug update` legitimately touches):
36
+ # .github/workflows/clud-bug-*.yml (workflow re-render)
37
+ # .github/actions/strict-mode-gate/* (composite re-render)
38
+ # AGENTS.md (logmind block + clud-bug stanza)
39
+ # .cursorrules / .clinerules / .windsurfrules / .continuerules
40
+ # .github/copilot-instructions.md
41
+ # .claude/skills/.clud-bug.json (manifest)
42
+ # .claude/skills/{critical-issues-only,evidence-based-review,
43
+ # respect-existing-conventions}/SKILL.md (baselines)
44
+ # docs/timeline.md / docs/file-structure.md / docs/decisions.md
45
+ # docs/decisions-branches/*.md (logmind side-effects)
46
+ #
47
+ # The workflow-change requirement is the SIGNATURE that distinguishes
48
+ # "agent ran clud-bug update" from "user is editing AGENTS.md by hand."
49
+ # An AGENTS.md-only PR (no workflow change) still goes through normal
50
+ # review — this catches the prompt-injection-via-AGENTS.md attack
51
+ # surface that a naive allowlist would open.
52
+ #
53
+ # Safety envelope:
54
+ # - Workflow file is still protected by the App-side guard (it just
55
+ # never runs in this branch).
56
+ # - Files in the allowlist are non-executable; modifying them can't
57
+ # grant code execution.
58
+ # - strictMode toggle is read from BASE ref so a PR can't disable
59
+ # strict-mode on itself.
27
60
  #
28
61
  # (b) Trivial PRs (v0.6.15 / 0.0.R): if the PR author is a dep-bumping
29
62
  # bot (dependabot, renovate) OR the diff is small (<2KB) AND only
@@ -82,18 +115,49 @@ jobs:
82
115
  exit 0
83
116
  fi
84
117
 
85
- # --- (a) workflow-only classifier ---
86
- IS_WORKFLOW_ONLY=true
118
+ # --- (a) workflow-only / clud-bug-update classifier
119
+ # (v0.6.14 / 0.0.W; widened in v0.6.26 / 0.0.W²) ---
120
+ # Two-track check:
121
+ # ALL_IN_ALLOWLIST = every changed file is in the
122
+ # clud-bug-update output allowlist (see header design notes
123
+ # for the full list + rationale).
124
+ # HAS_WORKFLOW_CHANGE = at least one workflow-file or
125
+ # strict-mode-gate change is present. This is the signature
126
+ # that distinguishes "clud-bug update output" from "user
127
+ # edited AGENTS.md by hand" — naked AGENTS.md edits go
128
+ # through normal review.
129
+ #
130
+ # Skip when both are true. Backward-compat with v0.6.14's
131
+ # `is_workflow_only` output name preserved (semantic widened to
132
+ # "skip-review-eligible"; the dependent clud-bug-review job
133
+ # gates on this output without needing a rename).
134
+ ALL_IN_ALLOWLIST=true
135
+ HAS_WORKFLOW_CHANGE=false
87
136
  while IFS= read -r f; do
88
137
  case "$f" in
89
- .github/workflows/clud-bug-*.yml) ;;
90
- .github/actions/strict-mode-gate/*) ;;
91
- *) IS_WORKFLOW_ONLY=false; break ;;
138
+ .github/workflows/clud-bug-*.yml) HAS_WORKFLOW_CHANGE=true ;;
139
+ .github/actions/strict-mode-gate/*) HAS_WORKFLOW_CHANGE=true ;;
140
+ # v0.6.26 / 0.0.W² additions — files clud-bug update produces.
141
+ AGENTS.md) ;;
142
+ .cursorrules|.clinerules|.windsurfrules|.continuerules) ;;
143
+ .github/copilot-instructions.md) ;;
144
+ .claude/skills/.clud-bug.json) ;;
145
+ .claude/skills/critical-issues-only/SKILL.md) ;;
146
+ .claude/skills/evidence-based-review/SKILL.md) ;;
147
+ .claude/skills/respect-existing-conventions/SKILL.md) ;;
148
+ # logmind side-effects of `logmind log` on the same commit.
149
+ docs/timeline.md|docs/file-structure.md|docs/decisions.md) ;;
150
+ docs/decisions-branches/*.md) ;;
151
+ *) ALL_IN_ALLOWLIST=false; break ;;
92
152
  esac
93
153
  done <<< "$CHANGED"
154
+ IS_WORKFLOW_ONLY=false
155
+ if [ "$ALL_IN_ALLOWLIST" = "true" ] && [ "$HAS_WORKFLOW_CHANGE" = "true" ]; then
156
+ IS_WORKFLOW_ONLY=true
157
+ fi
94
158
  echo "is_workflow_only=$IS_WORKFLOW_ONLY" >> "$GITHUB_OUTPUT"
95
159
  if [ "$IS_WORKFLOW_ONLY" = "true" ]; then
96
- echo "::notice title=Clud Bug 🐛::Skipping LLM review — PR only touches workflow files."
160
+ echo "::notice title=Clud Bug 🐛::Skipping LLM review — clud-bug update output (workflow + allowlist files, no review surface)."
97
161
  echo "model=$MODEL" >> "$GITHUB_OUTPUT"
98
162
  exit 0
99
163
  fi
@@ -434,22 +498,81 @@ jobs:
434
498
  # output after max retries (structured_output is empty). Keeps a
435
499
  # bare H2 header so the strict-mode gate sees a comment and falls
436
500
  # open (advisory) rather than panicking on a missing summary.
501
+ # v0.6.26 / §5.5 Layer 6: when structured_output is empty BUT inline
502
+ # findings were posted before the budget exhausted (tokenomics #21
503
+ # pattern), scrape the inline findings via gh api and render a
504
+ # synthetic summary that cites the real findings. Avoids the
505
+ # "0-findings-shown / 5-inline-posted" failure mode where the
506
+ # maintainer reading the PR sees a meaningless empty summary even
507
+ # though the review actually surfaced issues.
508
+ #
509
+ # If NO inline findings were posted either, falls through to the
510
+ # original advisory bare-H2 (legacy v0.6.22 behaviour).
437
511
  - name: Fallback summary (structured_output empty)
438
512
  if: success() && steps.clud-bug-review.outputs.structured_output == ''
439
513
  env:
440
514
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
441
515
  PR_NUMBER: ${{ github.event.pull_request.number }}
516
+ REPO: ${{ github.repository }}
517
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
518
+ TURNS_ESTIMATED: ${{ needs.paths-check.outputs.turns_estimated }}
519
+ MAX_TURNS: ${{ needs.paths-check.outputs.max_turns }}
520
+ FILES_COUNT: ${{ needs.paths-check.outputs.files_count }}
521
+ LINES_ADDED: ${{ needs.paths-check.outputs.lines_added }}
522
+ LINES_DELETED: ${{ needs.paths-check.outputs.lines_deleted }}
523
+ THREADS_COUNT: ${{ needs.paths-check.outputs.threads_count }}
442
524
  run: |
443
525
  set -euo pipefail
444
- gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review
526
+
527
+ # Layer 6: count inline findings claude[bot] posted on THIS
528
+ # head SHA via the inline-review-thread endpoint. Filter to
529
+ # the current SHA so older review-pass findings don't inflate
530
+ # the count.
531
+ INLINES=$(gh api "repos/$REPO/pulls/$PR_NUMBER/comments?per_page=100" \
532
+ --jq "[.[] | select(.user.login == \"claude[bot]\" and .commit_id == \"$HEAD_SHA\")]")
533
+ INLINE_COUNT=$(echo "$INLINES" | jq 'length')
534
+ CRITICAL=$(echo "$INLINES" | jq '[.[] | select(.body | test("🔴"))] | length')
535
+ MINOR=$(echo "$INLINES" | jq '[.[] | select(.body | test("🟡"))] | length')
536
+ PREEXISTING=$(echo "$INLINES" | jq '[.[] | select(.body | test("🟣"))] | length')
537
+ # Findings with no emoji prefix (cross-cutting / un-prefixed) — count once.
538
+ UNPREFIXED=$(( INLINE_COUNT - CRITICAL - MINOR - PREEXISTING ))
539
+ [ "$UNPREFIXED" -lt 0 ] && UNPREFIXED=0
540
+
541
+ CALIBRATION="<!-- clud-bug-calibration: turns_estimated=$TURNS_ESTIMATED, max_turns=$MAX_TURNS, files=$FILES_COUNT, lines_added=$LINES_ADDED, lines_deleted=$LINES_DELETED, threads=$THREADS_COUNT, structured_output=empty, inline_findings=$INLINE_COUNT -->"
542
+
543
+ if [ "$INLINE_COUNT" -gt 0 ]; then
544
+ # L6 synthetic summary — cites the real inline findings the
545
+ # action managed to post before structured-output emit failed.
546
+ STATUS=""
547
+ [ "$CRITICAL" -gt 0 ] && STATUS=" — critical findings"
548
+ gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review${STATUS}
549
+
550
+ **This round:** $CRITICAL critical · $MINOR minor · 0 resolved from prior · 0 still open
551
+
552
+ Found: $CRITICAL 🔴 / $MINOR 🟡 / $PREEXISTING 🟣
553
+
554
+ ⚠️ **Synthetic summary** (v0.6.26 §5.5 Layer 6 fallback) — the action posted $INLINE_COUNT inline finding(s) before the structured-output emit step exhausted its turn budget (or hit schema-validation retries). The inline findings above ARE the substantive review; this summary is reconstructed from them. v0.6.26's Layer 5 (auto-retry on cap-hit) is the more permanent fix; this is the safety net.
555
+
556
+ <!-- last-reviewed-sha: $HEAD_SHA -->
557
+
558
+ $CALIBRATION"
559
+ else
560
+ # No inline findings either — legacy bare-H2 advisory. The
561
+ # action errored before it could post anything substantive.
562
+ gh pr comment "$PR_NUMBER" --body "## 🐛 Clud Bug review
445
563
 
446
564
  **This round:** 0 critical · 0 minor · 0 resolved from prior · 0 still open
447
565
 
448
566
  Found: 0 🔴 / 0 🟡 / 0 🟣
449
567
 
450
- ⚠️ Structured output (\`--json-schema\`) returned empty likely max-retries hit on schema validation. Investigate the action logs.
568
+ ⚠️ Structured output (\`--json-schema\`) returned empty AND no inline findings were posted. The action likely errored before producing review output investigate the run logs.
569
+
570
+ Skills referenced: [none]
451
571
 
452
- Skills referenced: [none]"
572
+ <!-- last-reviewed-sha: $HEAD_SHA -->
573
+
574
+ $CALIBRATION"
575
+ fi
453
576
 
454
577
  # Strict-mode gate. Fails the check when the BASE ref's manifest
455
578
  # has { "strictMode": true } AND the latest clud-bug review's first
@@ -466,7 +589,7 @@ jobs:
466
589
  # Letting the action's own failure fail the check is louder and right.
467
590
  - name: Strict mode — fail check on critical findings
468
591
  if: success()
469
- uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.25
592
+ uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.27
470
593
  with:
471
594
  github-token: ${{ secrets.GITHUB_TOKEN }}
472
595
  # v0.6.22 / 0.0.O: the summary is now posted by the workflow