clud-bug 0.6.24 → 0.6.25
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/agents-md.js +29 -3
- package/lib/detect.js +9 -1
- package/lib/prompts.js +24 -0
- package/package.json +1 -1
- package/templates/workflow-py.yml.tmpl +89 -16
- package/templates/workflow-ts.yml.tmpl +89 -16
- package/templates/workflow.yml.tmpl +154 -30
package/lib/agents-md.js
CHANGED
|
@@ -45,11 +45,19 @@ const TOUCH_IF_PRESENT = [
|
|
|
45
45
|
// set) is a documented advisory upgrade path — `clud-bug init` deliberately
|
|
46
46
|
// preserves that state. If the block rendered "on" for that case, other
|
|
47
47
|
// agents reading AGENTS.md would get a wrong model of the gate.
|
|
48
|
-
|
|
48
|
+
//
|
|
49
|
+
// v0.6.25 (gotcha #2 fix): when the consuming repo IS the publisher of
|
|
50
|
+
// the clud-bug-collaboration skill (skill source lives at
|
|
51
|
+
// `skills/clud-bug-collaboration/SKILL.md` instead of the consumer-install
|
|
52
|
+
// path `.claude/skills/...`), render the LOCAL repo path. Otherwise the
|
|
53
|
+
// link is dead in the publisher repo (this used to require a manual fix
|
|
54
|
+
// every v0.6.* propagation cycle on agent-skills).
|
|
55
|
+
export function renderBlock({ version, strictMode, skillRelPath } = {}) {
|
|
49
56
|
const versionLine = version ? `_Installed at clud-bug v${version}._` : '';
|
|
50
57
|
const strictNote = strictMode === true
|
|
51
58
|
? '**on** in this repo (workflow check fails on critical findings)'
|
|
52
59
|
: '**off** in this repo (advisory only)';
|
|
60
|
+
const skillPath = skillRelPath || '.claude/skills/clud-bug-collaboration/SKILL.md';
|
|
53
61
|
return `${START_MARKER}
|
|
54
62
|
<!-- clud-bug-block-version: ${BLOCK_VERSION} -->
|
|
55
63
|
## clud-bug — Claude PR review
|
|
@@ -57,7 +65,7 @@ export function renderBlock({ version, strictMode } = {}) {
|
|
|
57
65
|
This repo uses [clud-bug](https://cludbug.dev) for automatic PR reviews.
|
|
58
66
|
Full collaboration rules — fix-push flow, skill structure, comment format,
|
|
59
67
|
strict-mode mechanics, workflow-edit constraint — live in the bundled
|
|
60
|
-
[\`clud-bug-collaboration\` skill](
|
|
68
|
+
[\`clud-bug-collaboration\` skill](${skillPath}).
|
|
61
69
|
Read that skill before pushing fixes addressing prior review threads.
|
|
62
70
|
|
|
63
71
|
Strict mode is ${strictNote}. Toggle via \`.claude/skills/.clud-bug.json\`
|
|
@@ -71,6 +79,19 @@ ${versionLine}
|
|
|
71
79
|
${END_MARKER}`;
|
|
72
80
|
}
|
|
73
81
|
|
|
82
|
+
// v0.6.25 (gotcha #2): detect repos that PUBLISH the
|
|
83
|
+
// clud-bug-collaboration skill (agent-skills is the canonical case).
|
|
84
|
+
// When the skill source exists at `skills/clud-bug-collaboration/SKILL.md`
|
|
85
|
+
// in the working tree, the AGENTS.md link should point there, not at the
|
|
86
|
+
// consumer-install `.claude/skills/...` path that doesn't exist in the
|
|
87
|
+
// publisher repo.
|
|
88
|
+
export async function detectSkillRelPath(cwd) {
|
|
89
|
+
const publisherPath = 'skills/clud-bug-collaboration/SKILL.md';
|
|
90
|
+
const consumerPath = '.claude/skills/clud-bug-collaboration/SKILL.md';
|
|
91
|
+
if (await fileExists(join(cwd, publisherPath))) return publisherPath;
|
|
92
|
+
return consumerPath;
|
|
93
|
+
}
|
|
94
|
+
|
|
74
95
|
// Replace an existing clud-bug block in `content`, OR append if absent.
|
|
75
96
|
// Idempotent: running multiple times leaves a single block.
|
|
76
97
|
export function upsertBlock(content, block) {
|
|
@@ -126,7 +147,12 @@ function escapeRe(s) {
|
|
|
126
147
|
//
|
|
127
148
|
// Returns { touched: string[], created: string[] } for the caller to log.
|
|
128
149
|
export async function applyToRepo(cwd, blockOpts = {}) {
|
|
129
|
-
|
|
150
|
+
// v0.6.25 / gotcha #2: detect publisher repo + render local skill path.
|
|
151
|
+
// Pre-v0.6.25 always rendered the consumer install path → broke
|
|
152
|
+
// agent-skills' check-links every propagation cycle. Detection runs
|
|
153
|
+
// before block render so the path is correct from the first write.
|
|
154
|
+
const skillRelPath = blockOpts.skillRelPath ?? await detectSkillRelPath(cwd);
|
|
155
|
+
const block = renderBlock({ ...blockOpts, skillRelPath });
|
|
130
156
|
const touched = [];
|
|
131
157
|
const created = [];
|
|
132
158
|
|
package/lib/detect.js
CHANGED
|
@@ -214,7 +214,15 @@ export function buildDescriptionLine(signals) {
|
|
|
214
214
|
const parts = [];
|
|
215
215
|
if (signals.name) parts.push(`This project is "${signals.name}".`);
|
|
216
216
|
if (signals.description) {
|
|
217
|
-
|
|
217
|
+
// v0.6.25 / issue #89: when signals.description comes from a
|
|
218
|
+
// README first paragraph or similar multi-paragraph source, the
|
|
219
|
+
// raw `\n` characters survive into the rendered YAML's
|
|
220
|
+
// APPEND_SYSTEM_PROMPT value. The renderer's indent-aware
|
|
221
|
+
// substitution preserves layout but a literal `\n` inside a YAML
|
|
222
|
+
// double-quoted string is interpreted as a newline, breaking
|
|
223
|
+
// the YAML. Collapse any whitespace run (including newlines + tabs)
|
|
224
|
+
// to a single space before further processing.
|
|
225
|
+
const desc = signals.description.replace(/\s+/g, ' ').trim();
|
|
218
226
|
parts.push(/[.!?]$/.test(desc) ? desc : `${desc}.`);
|
|
219
227
|
}
|
|
220
228
|
if (signals.primaryLanguage) {
|
package/lib/prompts.js
CHANGED
|
@@ -177,6 +177,30 @@ Keep total output under ~600 tokens. Per finding:
|
|
|
177
177
|
Not a hard cap (SDK doesn't expose max_tokens); brevity compounds
|
|
178
178
|
across the org on every review.
|
|
179
179
|
|
|
180
|
+
Turn budget self-rationing (v0.6.25 / §5.5 Layer 2):
|
|
181
|
+
You are run with a finite \`--max-turns\` cap. The exact value, plus
|
|
182
|
+
the pre-flight estimate for this PR, lands in the per-PR prompt
|
|
183
|
+
section below as \`max_turns=N, estimated=M, files=F, +A/-D lines, threads=T\`.
|
|
184
|
+
Treat that as your budget, not a ceiling to test.
|
|
185
|
+
|
|
186
|
+
Rules:
|
|
187
|
+
- Reserve the LAST 5 turns for structured-output emit. It is
|
|
188
|
+
non-skippable; if you run out before emitting, the workflow
|
|
189
|
+
falls back to a synthetic summary scraped from your inline
|
|
190
|
+
findings (v0.6.26+ Layer 6) — strictly worse than a real one.
|
|
191
|
+
- Rough rates: ~1 turn per 50 lines of code, ~1 turn per 150 lines
|
|
192
|
+
of docs, ~1 turn per 100 lines of tests or config. Each prior
|
|
193
|
+
unresolved thread is ~1.5 turns to walk + decide.
|
|
194
|
+
- Every line of the diff MUST be in your scan. Never silently skip
|
|
195
|
+
a file. If you must shorten coverage to fit budget, switch from
|
|
196
|
+
deep-dive analysis to one-sentence per-file verdicts — but every
|
|
197
|
+
file gets a verdict, even if it's "no issues found".
|
|
198
|
+
- If you're falling behind pace (files_reviewed/files_total
|
|
199
|
+
materially less than turns_used/max_turns), broaden+shallow over
|
|
200
|
+
deep+narrow. Audit visibility lives in \`per_skill_scan\` — list
|
|
201
|
+
every file's verdict so a maintainer can verify nothing was
|
|
202
|
+
skipped.
|
|
203
|
+
|
|
180
204
|
Incremental-diff handshake (v0.6.10+) — emit the SHA marker:
|
|
181
205
|
At the very end of the summary (after the Skills-referenced footer,
|
|
182
206
|
on its own line), append:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clud-bug",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.25",
|
|
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,10 +1,15 @@
|
|
|
1
|
-
# clud-bug-template-version:
|
|
1
|
+
# clud-bug-template-version: v11
|
|
2
2
|
name: Clud Bug 🐛 Crawls Your Code
|
|
3
3
|
|
|
4
4
|
on:
|
|
5
5
|
pull_request:
|
|
6
6
|
types: [opened, synchronize]
|
|
7
7
|
|
|
8
|
+
# v0.6.25: workflow-level concurrency — see workflow.yml.tmpl.
|
|
9
|
+
concurrency:
|
|
10
|
+
group: clud-bug-review-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
8
13
|
jobs:
|
|
9
14
|
# Pre-flight (v0.6.14 / 0.0.W + v0.6.15 / 0.0.R) — see workflow.yml.tmpl.
|
|
10
15
|
paths-check:
|
|
@@ -16,6 +21,12 @@ jobs:
|
|
|
16
21
|
is_workflow_only: ${{ steps.classify.outputs.is_workflow_only }}
|
|
17
22
|
model: ${{ steps.classify.outputs.model }}
|
|
18
23
|
max_turns: ${{ steps.classify.outputs.max_turns }}
|
|
24
|
+
# v0.6.25 / §5.5 Layer 1.5 (calibration) — see workflow.yml.tmpl.
|
|
25
|
+
turns_estimated: ${{ steps.classify.outputs.turns_estimated }}
|
|
26
|
+
files_count: ${{ steps.classify.outputs.files_count }}
|
|
27
|
+
lines_added: ${{ steps.classify.outputs.lines_added }}
|
|
28
|
+
lines_deleted: ${{ steps.classify.outputs.lines_deleted }}
|
|
29
|
+
threads_count: ${{ steps.classify.outputs.threads_count }}
|
|
19
30
|
steps:
|
|
20
31
|
- name: Classify PR diff
|
|
21
32
|
id: classify
|
|
@@ -34,6 +45,11 @@ jobs:
|
|
|
34
45
|
echo "is_workflow_only=false"
|
|
35
46
|
echo "model=$MODEL"
|
|
36
47
|
echo "max_turns=15"
|
|
48
|
+
echo "turns_estimated=0"
|
|
49
|
+
echo "files_count=0"
|
|
50
|
+
echo "lines_added=0"
|
|
51
|
+
echo "lines_deleted=0"
|
|
52
|
+
echo "threads_count=0"
|
|
37
53
|
} >> "$GITHUB_OUTPUT"
|
|
38
54
|
exit 0
|
|
39
55
|
fi
|
|
@@ -81,22 +97,57 @@ jobs:
|
|
|
81
97
|
fi
|
|
82
98
|
echo "model=$MODEL" >> "$GITHUB_OUTPUT"
|
|
83
99
|
|
|
84
|
-
#
|
|
85
|
-
|
|
100
|
+
# Smart budget (v0.6.25 / §5.5 Layer 1) — see workflow.yml.tmpl for design notes + formula.
|
|
101
|
+
FILE_COUNT=$(echo "$CHANGED" | wc -l | tr -d ' ')
|
|
102
|
+
THREAD_COUNT=$(gh api graphql -f query='{repository(owner:"'"$(echo "$REPO" | cut -d/ -f1)"'",name:"'"$(echo "$REPO" | cut -d/ -f2)"'"){pullRequest(number:'"$PR_NUMBER"'){reviewThreads(first:50){nodes{isResolved comments(first:1){nodes{author{login}}}}}}}}' --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and (.comments.nodes[0].author.login == "claude" or .comments.nodes[0].author.login == "claude[bot]"))] | length' 2>/dev/null || echo 0)
|
|
103
|
+
THREAD_COUNT=${THREAD_COUNT:-0}
|
|
104
|
+
|
|
86
105
|
if [ "$IS_TRIVIAL" = "true" ]; then
|
|
87
106
|
MAX_TURNS=10
|
|
107
|
+
TURNS_ESTIMATED=10
|
|
108
|
+
LINES_ADDED=0
|
|
109
|
+
LINES_DELETED=0
|
|
110
|
+
echo "::notice title=Clud Bug 🐛::Trivial (Haiku) budget: max_turns=10."
|
|
88
111
|
else
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
FILES_JSON=$(gh pr view "$PR_NUMBER" -R "$REPO" --json files --jq '.files' 2>/dev/null || echo '[]')
|
|
113
|
+
LINES_ADDED=$(echo "$FILES_JSON" | jq '[.[].additions] | add // 0' 2>/dev/null || echo 0)
|
|
114
|
+
LINES_DELETED=$(echo "$FILES_JSON" | jq '[.[].deletions] | add // 0' 2>/dev/null || echo 0)
|
|
115
|
+
LINES_ADDED=${LINES_ADDED:-0}
|
|
116
|
+
LINES_DELETED=${LINES_DELETED:-0}
|
|
117
|
+
|
|
118
|
+
# jq implements the §5.5 Layer 1 formula — see workflow.yml.tmpl.
|
|
119
|
+
TURNS_ESTIMATED=$(echo "$FILES_JSON" | jq --argjson threads "$THREAD_COUNT" '
|
|
120
|
+
def per_line(path):
|
|
121
|
+
if (path | test("\\.(test|spec)\\.|__tests__|^tests?/")) then 0.01
|
|
122
|
+
elif (path | test("\\.(md|txt|rst|adoc|mdx)$")) then 0.00666667
|
|
123
|
+
elif (path | test("\\.(yml|yaml|toml|json|cfg|ini|conf|tmpl)$")) then 0.01
|
|
124
|
+
elif (path | test("\\.(ts|tsx|py|js|jsx|mjs|cjs|go|rs|java|kt|rb|php|cs|c|cpp|h|hpp|swift|scala)$")) then 0.02
|
|
125
|
+
else 0.0125 end;
|
|
126
|
+
map(
|
|
127
|
+
select(.path != "docs/timeline.md"
|
|
128
|
+
and .path != "docs/file-structure.md"
|
|
129
|
+
and .path != "docs/decisions.md")
|
|
130
|
+
| (.additions) as $add | (.deletions) as $del
|
|
131
|
+
| (if $add < $del then $add else $del end) as $mod
|
|
132
|
+
| ($add - $mod) as $pa | ($del - $mod) as $pd
|
|
133
|
+
| per_line(.path) as $tw
|
|
134
|
+
| 0.3 + ($pa * $tw) + ($mod * 1.5 * $tw) + ($pd * 0.1 * $tw)
|
|
135
|
+
) | (add // 0) + 10 + ($threads * 1.5) | (. + 0.9999999) | floor
|
|
136
|
+
' 2>/dev/null || echo 15)
|
|
137
|
+
TURNS_ESTIMATED=${TURNS_ESTIMATED:-15}
|
|
138
|
+
MAX_TURNS=$(( (TURNS_ESTIMATED * 12 + 9) / 10 ))
|
|
139
|
+
[ "$MAX_TURNS" -lt 15 ] && MAX_TURNS=15
|
|
140
|
+
[ "$MAX_TURNS" -gt 60 ] && MAX_TURNS=60
|
|
141
|
+
echo "::notice title=Clud Bug 🐛::Smart budget: estimated $TURNS_ESTIMATED turns → max_turns=$MAX_TURNS ($FILE_COUNT files, +$LINES_ADDED/-$LINES_DELETED lines, $THREAD_COUNT prior threads)."
|
|
98
142
|
fi
|
|
99
|
-
|
|
143
|
+
{
|
|
144
|
+
echo "max_turns=$MAX_TURNS"
|
|
145
|
+
echo "turns_estimated=$TURNS_ESTIMATED"
|
|
146
|
+
echo "files_count=$FILE_COUNT"
|
|
147
|
+
echo "lines_added=$LINES_ADDED"
|
|
148
|
+
echo "lines_deleted=$LINES_DELETED"
|
|
149
|
+
echo "threads_count=$THREAD_COUNT"
|
|
150
|
+
} >> "$GITHUB_OUTPUT"
|
|
100
151
|
|
|
101
152
|
clud-bug-review:
|
|
102
153
|
needs: paths-check
|
|
@@ -180,20 +231,42 @@ jobs:
|
|
|
180
231
|
Review this pull request following the discipline in your
|
|
181
232
|
system prompt — every rule about skill routing, comment
|
|
182
233
|
format, the strict-mode header, the two-surface review
|
|
183
|
-
shape,
|
|
234
|
+
shape, the FIX-PUSH FLOW, and (v0.6.25+) turn-budget
|
|
235
|
+
self-rationing applies.
|
|
236
|
+
|
|
237
|
+
## Your turn budget for this PR (v0.6.25 / §5.5 Layer 2)
|
|
238
|
+
|
|
239
|
+
max_turns=${{ needs.paths-check.outputs.max_turns }},
|
|
240
|
+
estimated=${{ needs.paths-check.outputs.turns_estimated }},
|
|
241
|
+
files=${{ needs.paths-check.outputs.files_count }},
|
|
242
|
+
+${{ needs.paths-check.outputs.lines_added }}/-${{ needs.paths-check.outputs.lines_deleted }} lines,
|
|
243
|
+
prior_threads=${{ needs.paths-check.outputs.threads_count }}.
|
|
244
|
+
|
|
245
|
+
Plan accordingly per the "Turn budget self-rationing"
|
|
246
|
+
section in your system prompt. Reserve 5 turns for emit.
|
|
184
247
|
id: clud-bug-review
|
|
185
248
|
|
|
186
249
|
# v0.6.22 / 0.0.O: render structured output → post via gh pr comment.
|
|
250
|
+
# v0.6.25 / §5.5 Layer 1.5: append calibration marker.
|
|
187
251
|
- name: Render + post structured review
|
|
188
252
|
if: success() && steps.clud-bug-review.outputs.structured_output != ''
|
|
189
253
|
env:
|
|
190
254
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
191
255
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
192
256
|
STRUCTURED: ${{ steps.clud-bug-review.outputs.structured_output }}
|
|
257
|
+
TURNS_ESTIMATED: ${{ needs.paths-check.outputs.turns_estimated }}
|
|
258
|
+
MAX_TURNS: ${{ needs.paths-check.outputs.max_turns }}
|
|
259
|
+
FILES_COUNT: ${{ needs.paths-check.outputs.files_count }}
|
|
260
|
+
LINES_ADDED: ${{ needs.paths-check.outputs.lines_added }}
|
|
261
|
+
LINES_DELETED: ${{ needs.paths-check.outputs.lines_deleted }}
|
|
262
|
+
THREADS_COUNT: ${{ needs.paths-check.outputs.threads_count }}
|
|
193
263
|
run: |
|
|
194
264
|
set -euo pipefail
|
|
195
265
|
BODY=$(printf '%s\n' "$STRUCTURED" | npx --yes clud-bug@{{CLUD_BUG_VERSION}} render --stdin)
|
|
196
|
-
|
|
266
|
+
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 -->"
|
|
267
|
+
gh pr comment "$PR_NUMBER" --body "$BODY
|
|
268
|
+
|
|
269
|
+
$CALIBRATION"
|
|
197
270
|
|
|
198
271
|
# Fallback when structured_output is empty (max-retries hit).
|
|
199
272
|
- name: Fallback summary (structured_output empty)
|
|
@@ -216,7 +289,7 @@ jobs:
|
|
|
216
289
|
# Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
|
|
217
290
|
- name: Strict mode — fail check on critical findings
|
|
218
291
|
if: success()
|
|
219
|
-
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.
|
|
292
|
+
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.25
|
|
220
293
|
with:
|
|
221
294
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
222
295
|
# v0.6.22 / 0.0.O: summary now posted by github-actions[bot].
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
# clud-bug-template-version:
|
|
1
|
+
# clud-bug-template-version: v11
|
|
2
2
|
name: Clud Bug 🐛 Crawls Your Code
|
|
3
3
|
|
|
4
4
|
on:
|
|
5
5
|
pull_request:
|
|
6
6
|
types: [opened, synchronize]
|
|
7
7
|
|
|
8
|
+
# v0.6.25: workflow-level concurrency — see workflow.yml.tmpl.
|
|
9
|
+
concurrency:
|
|
10
|
+
group: clud-bug-review-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
8
13
|
jobs:
|
|
9
14
|
# Pre-flight (v0.6.14 / 0.0.W + v0.6.15 / 0.0.R) — see workflow.yml.tmpl.
|
|
10
15
|
paths-check:
|
|
@@ -16,6 +21,12 @@ jobs:
|
|
|
16
21
|
is_workflow_only: ${{ steps.classify.outputs.is_workflow_only }}
|
|
17
22
|
model: ${{ steps.classify.outputs.model }}
|
|
18
23
|
max_turns: ${{ steps.classify.outputs.max_turns }}
|
|
24
|
+
# v0.6.25 / §5.5 Layer 1.5 (calibration) — see workflow.yml.tmpl.
|
|
25
|
+
turns_estimated: ${{ steps.classify.outputs.turns_estimated }}
|
|
26
|
+
files_count: ${{ steps.classify.outputs.files_count }}
|
|
27
|
+
lines_added: ${{ steps.classify.outputs.lines_added }}
|
|
28
|
+
lines_deleted: ${{ steps.classify.outputs.lines_deleted }}
|
|
29
|
+
threads_count: ${{ steps.classify.outputs.threads_count }}
|
|
19
30
|
steps:
|
|
20
31
|
- name: Classify PR diff
|
|
21
32
|
id: classify
|
|
@@ -34,6 +45,11 @@ jobs:
|
|
|
34
45
|
echo "is_workflow_only=false"
|
|
35
46
|
echo "model=$MODEL"
|
|
36
47
|
echo "max_turns=15"
|
|
48
|
+
echo "turns_estimated=0"
|
|
49
|
+
echo "files_count=0"
|
|
50
|
+
echo "lines_added=0"
|
|
51
|
+
echo "lines_deleted=0"
|
|
52
|
+
echo "threads_count=0"
|
|
37
53
|
} >> "$GITHUB_OUTPUT"
|
|
38
54
|
exit 0
|
|
39
55
|
fi
|
|
@@ -81,22 +97,57 @@ jobs:
|
|
|
81
97
|
fi
|
|
82
98
|
echo "model=$MODEL" >> "$GITHUB_OUTPUT"
|
|
83
99
|
|
|
84
|
-
#
|
|
85
|
-
|
|
100
|
+
# Smart budget (v0.6.25 / §5.5 Layer 1) — see workflow.yml.tmpl for design notes + formula.
|
|
101
|
+
FILE_COUNT=$(echo "$CHANGED" | wc -l | tr -d ' ')
|
|
102
|
+
THREAD_COUNT=$(gh api graphql -f query='{repository(owner:"'"$(echo "$REPO" | cut -d/ -f1)"'",name:"'"$(echo "$REPO" | cut -d/ -f2)"'"){pullRequest(number:'"$PR_NUMBER"'){reviewThreads(first:50){nodes{isResolved comments(first:1){nodes{author{login}}}}}}}}' --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and (.comments.nodes[0].author.login == "claude" or .comments.nodes[0].author.login == "claude[bot]"))] | length' 2>/dev/null || echo 0)
|
|
103
|
+
THREAD_COUNT=${THREAD_COUNT:-0}
|
|
104
|
+
|
|
86
105
|
if [ "$IS_TRIVIAL" = "true" ]; then
|
|
87
106
|
MAX_TURNS=10
|
|
107
|
+
TURNS_ESTIMATED=10
|
|
108
|
+
LINES_ADDED=0
|
|
109
|
+
LINES_DELETED=0
|
|
110
|
+
echo "::notice title=Clud Bug 🐛::Trivial (Haiku) budget: max_turns=10."
|
|
88
111
|
else
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
FILES_JSON=$(gh pr view "$PR_NUMBER" -R "$REPO" --json files --jq '.files' 2>/dev/null || echo '[]')
|
|
113
|
+
LINES_ADDED=$(echo "$FILES_JSON" | jq '[.[].additions] | add // 0' 2>/dev/null || echo 0)
|
|
114
|
+
LINES_DELETED=$(echo "$FILES_JSON" | jq '[.[].deletions] | add // 0' 2>/dev/null || echo 0)
|
|
115
|
+
LINES_ADDED=${LINES_ADDED:-0}
|
|
116
|
+
LINES_DELETED=${LINES_DELETED:-0}
|
|
117
|
+
|
|
118
|
+
# jq implements the §5.5 Layer 1 formula — see workflow.yml.tmpl.
|
|
119
|
+
TURNS_ESTIMATED=$(echo "$FILES_JSON" | jq --argjson threads "$THREAD_COUNT" '
|
|
120
|
+
def per_line(path):
|
|
121
|
+
if (path | test("\\.(test|spec)\\.|__tests__|^tests?/")) then 0.01
|
|
122
|
+
elif (path | test("\\.(md|txt|rst|adoc|mdx)$")) then 0.00666667
|
|
123
|
+
elif (path | test("\\.(yml|yaml|toml|json|cfg|ini|conf|tmpl)$")) then 0.01
|
|
124
|
+
elif (path | test("\\.(ts|tsx|py|js|jsx|mjs|cjs|go|rs|java|kt|rb|php|cs|c|cpp|h|hpp|swift|scala)$")) then 0.02
|
|
125
|
+
else 0.0125 end;
|
|
126
|
+
map(
|
|
127
|
+
select(.path != "docs/timeline.md"
|
|
128
|
+
and .path != "docs/file-structure.md"
|
|
129
|
+
and .path != "docs/decisions.md")
|
|
130
|
+
| (.additions) as $add | (.deletions) as $del
|
|
131
|
+
| (if $add < $del then $add else $del end) as $mod
|
|
132
|
+
| ($add - $mod) as $pa | ($del - $mod) as $pd
|
|
133
|
+
| per_line(.path) as $tw
|
|
134
|
+
| 0.3 + ($pa * $tw) + ($mod * 1.5 * $tw) + ($pd * 0.1 * $tw)
|
|
135
|
+
) | (add // 0) + 10 + ($threads * 1.5) | (. + 0.9999999) | floor
|
|
136
|
+
' 2>/dev/null || echo 15)
|
|
137
|
+
TURNS_ESTIMATED=${TURNS_ESTIMATED:-15}
|
|
138
|
+
MAX_TURNS=$(( (TURNS_ESTIMATED * 12 + 9) / 10 ))
|
|
139
|
+
[ "$MAX_TURNS" -lt 15 ] && MAX_TURNS=15
|
|
140
|
+
[ "$MAX_TURNS" -gt 60 ] && MAX_TURNS=60
|
|
141
|
+
echo "::notice title=Clud Bug 🐛::Smart budget: estimated $TURNS_ESTIMATED turns → max_turns=$MAX_TURNS ($FILE_COUNT files, +$LINES_ADDED/-$LINES_DELETED lines, $THREAD_COUNT prior threads)."
|
|
98
142
|
fi
|
|
99
|
-
|
|
143
|
+
{
|
|
144
|
+
echo "max_turns=$MAX_TURNS"
|
|
145
|
+
echo "turns_estimated=$TURNS_ESTIMATED"
|
|
146
|
+
echo "files_count=$FILE_COUNT"
|
|
147
|
+
echo "lines_added=$LINES_ADDED"
|
|
148
|
+
echo "lines_deleted=$LINES_DELETED"
|
|
149
|
+
echo "threads_count=$THREAD_COUNT"
|
|
150
|
+
} >> "$GITHUB_OUTPUT"
|
|
100
151
|
|
|
101
152
|
clud-bug-review:
|
|
102
153
|
needs: paths-check
|
|
@@ -180,20 +231,42 @@ jobs:
|
|
|
180
231
|
Review this pull request following the discipline in your
|
|
181
232
|
system prompt — every rule about skill routing, comment
|
|
182
233
|
format, the strict-mode header, the two-surface review
|
|
183
|
-
shape,
|
|
234
|
+
shape, the FIX-PUSH FLOW, and (v0.6.25+) turn-budget
|
|
235
|
+
self-rationing applies.
|
|
236
|
+
|
|
237
|
+
## Your turn budget for this PR (v0.6.25 / §5.5 Layer 2)
|
|
238
|
+
|
|
239
|
+
max_turns=${{ needs.paths-check.outputs.max_turns }},
|
|
240
|
+
estimated=${{ needs.paths-check.outputs.turns_estimated }},
|
|
241
|
+
files=${{ needs.paths-check.outputs.files_count }},
|
|
242
|
+
+${{ needs.paths-check.outputs.lines_added }}/-${{ needs.paths-check.outputs.lines_deleted }} lines,
|
|
243
|
+
prior_threads=${{ needs.paths-check.outputs.threads_count }}.
|
|
244
|
+
|
|
245
|
+
Plan accordingly per the "Turn budget self-rationing"
|
|
246
|
+
section in your system prompt. Reserve 5 turns for emit.
|
|
184
247
|
id: clud-bug-review
|
|
185
248
|
|
|
186
249
|
# v0.6.22 / 0.0.O: render structured output → post via gh pr comment.
|
|
250
|
+
# v0.6.25 / §5.5 Layer 1.5: append calibration marker.
|
|
187
251
|
- name: Render + post structured review
|
|
188
252
|
if: success() && steps.clud-bug-review.outputs.structured_output != ''
|
|
189
253
|
env:
|
|
190
254
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
191
255
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
192
256
|
STRUCTURED: ${{ steps.clud-bug-review.outputs.structured_output }}
|
|
257
|
+
TURNS_ESTIMATED: ${{ needs.paths-check.outputs.turns_estimated }}
|
|
258
|
+
MAX_TURNS: ${{ needs.paths-check.outputs.max_turns }}
|
|
259
|
+
FILES_COUNT: ${{ needs.paths-check.outputs.files_count }}
|
|
260
|
+
LINES_ADDED: ${{ needs.paths-check.outputs.lines_added }}
|
|
261
|
+
LINES_DELETED: ${{ needs.paths-check.outputs.lines_deleted }}
|
|
262
|
+
THREADS_COUNT: ${{ needs.paths-check.outputs.threads_count }}
|
|
193
263
|
run: |
|
|
194
264
|
set -euo pipefail
|
|
195
265
|
BODY=$(printf '%s\n' "$STRUCTURED" | npx --yes clud-bug@{{CLUD_BUG_VERSION}} render --stdin)
|
|
196
|
-
|
|
266
|
+
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 -->"
|
|
267
|
+
gh pr comment "$PR_NUMBER" --body "$BODY
|
|
268
|
+
|
|
269
|
+
$CALIBRATION"
|
|
197
270
|
|
|
198
271
|
# Fallback when structured_output is empty (max-retries hit).
|
|
199
272
|
- name: Fallback summary (structured_output empty)
|
|
@@ -216,7 +289,7 @@ jobs:
|
|
|
216
289
|
# Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
|
|
217
290
|
- name: Strict mode — fail check on critical findings
|
|
218
291
|
if: success()
|
|
219
|
-
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.
|
|
292
|
+
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.25
|
|
220
293
|
with:
|
|
221
294
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
222
295
|
# v0.6.22 / 0.0.O: summary now posted by github-actions[bot].
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
# clud-bug-template-version:
|
|
1
|
+
# clud-bug-template-version: v11
|
|
2
2
|
name: Clud Bug 🐛 Crawls Your Code
|
|
3
3
|
|
|
4
4
|
on:
|
|
5
5
|
pull_request:
|
|
6
6
|
types: [opened, synchronize]
|
|
7
7
|
|
|
8
|
+
# v0.6.25: workflow-level concurrency — newer pushes cancel older
|
|
9
|
+
# in-flight runs on the same PR. Avoids the duplicate-review failure
|
|
10
|
+
# mode hit on tokenomics #21 where my merge-of-main and logmind's
|
|
11
|
+
# auto-regen-derived-docs follow-up fired two concurrent reviews
|
|
12
|
+
# (each posted its own "in-progress" todo comment).
|
|
13
|
+
concurrency:
|
|
14
|
+
group: clud-bug-review-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
8
17
|
jobs:
|
|
9
18
|
# Pre-flight: classify the PR diff to decide (a) whether to skip the
|
|
10
19
|
# LLM review entirely, and (b) which model to route to.
|
|
@@ -30,6 +39,17 @@ jobs:
|
|
|
30
39
|
is_workflow_only: ${{ steps.classify.outputs.is_workflow_only }}
|
|
31
40
|
model: ${{ steps.classify.outputs.model }}
|
|
32
41
|
max_turns: ${{ steps.classify.outputs.max_turns }}
|
|
42
|
+
# v0.6.25 / §5.5 Layer 1.5 (calibration measurement): emit the
|
|
43
|
+
# raw estimated-turns alongside the actual max_turns the workflow
|
|
44
|
+
# passes to claude-code-action. The post-step records both into
|
|
45
|
+
# the summary comment as a hidden HTML marker so we can tune the
|
|
46
|
+
# per-file coefficients (Layer 1) from real consumer-review data
|
|
47
|
+
# over the Step 4 30-day window. Re-tune in v0.6.26+.
|
|
48
|
+
turns_estimated: ${{ steps.classify.outputs.turns_estimated }}
|
|
49
|
+
files_count: ${{ steps.classify.outputs.files_count }}
|
|
50
|
+
lines_added: ${{ steps.classify.outputs.lines_added }}
|
|
51
|
+
lines_deleted: ${{ steps.classify.outputs.lines_deleted }}
|
|
52
|
+
threads_count: ${{ steps.classify.outputs.threads_count }}
|
|
33
53
|
steps:
|
|
34
54
|
- name: Classify PR diff
|
|
35
55
|
id: classify
|
|
@@ -53,6 +73,11 @@ jobs:
|
|
|
53
73
|
echo "is_workflow_only=false"
|
|
54
74
|
echo "model=$MODEL"
|
|
55
75
|
echo "max_turns=15"
|
|
76
|
+
echo "turns_estimated=0"
|
|
77
|
+
echo "files_count=0"
|
|
78
|
+
echo "lines_added=0"
|
|
79
|
+
echo "lines_deleted=0"
|
|
80
|
+
echo "threads_count=0"
|
|
56
81
|
} >> "$GITHUB_OUTPUT"
|
|
57
82
|
exit 0
|
|
58
83
|
fi
|
|
@@ -113,37 +138,106 @@ jobs:
|
|
|
113
138
|
fi
|
|
114
139
|
echo "model=$MODEL" >> "$GITHUB_OUTPUT"
|
|
115
140
|
|
|
116
|
-
# --- (c)
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
141
|
+
# --- (c) smart budget estimation (v0.6.25 / §5.5 Layer 1) ---
|
|
142
|
+
# Replaces v0.6.23's 4-bucket if-elif with a line-based
|
|
143
|
+
# formula. File count was a crude proxy; lines + edit-type +
|
|
144
|
+
# file-class is much better. Per-file cost in turns:
|
|
145
|
+
#
|
|
146
|
+
# per_file_cost = 0.3 + added × tw × 1.0
|
|
147
|
+
# + modified × tw × 1.5 # context-heavy
|
|
148
|
+
# + deleted × tw × 0.1 # trivial
|
|
149
|
+
#
|
|
150
|
+
# type_weight tw (turns per line):
|
|
151
|
+
# code (.ts/.py/.js/.go/.rs/.java/...) : 1/50
|
|
152
|
+
# docs (.md/.txt/.rst/.adoc/...) : 1/150
|
|
153
|
+
# tests (.test.*/.spec.*/__tests__/*) : 1/100
|
|
154
|
+
# config (.yml/.toml/.json/.cfg) : 1/100
|
|
155
|
+
# derived (timeline.md/file-structure.md/decisions.md) : 0
|
|
156
|
+
#
|
|
157
|
+
# estimated_turns = 10 + sum(per_file_cost) + 1.5 × prior_threads
|
|
158
|
+
# max_turns = max(estimated × 1.2, 15) # 20% safety margin
|
|
159
|
+
# max_turns = min(max_turns, 60) # ceiling; L5 retry above
|
|
123
160
|
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
|
|
161
|
+
# Emit overhead 10 (raised from initial 5 design): tokenomics
|
|
162
|
+
# #21 (26 docs files, +310/-235) used ~25 turns vs old-formula
|
|
163
|
+
# 16 — structured-output emit + JSON-schema retries + initial
|
|
164
|
+
# context loading cost ~5-10 turns themselves. Will retune from
|
|
165
|
+
# Layer 1.5 calibration data in v0.6.26+.
|
|
166
|
+
#
|
|
167
|
+
# `gh` is the source for additions/deletions + threads.
|
|
168
|
+
# `jq` is the inline estimator (preinstalled on ubuntu-latest
|
|
169
|
+
# runners; an earlier design used python3 but jq sidesteps the
|
|
170
|
+
# YAML-block-indent vs heredoc-content-indent dance).
|
|
171
|
+
# Calibration via the turns_estimated + turns_actually_used data
|
|
172
|
+
# points the post-step records (Layer 1.5).
|
|
173
|
+
FILE_COUNT=$(echo "$CHANGED" | wc -l | tr -d ' ')
|
|
174
|
+
# Count unresolved claude-bot threads. Best-effort: rate-limit
|
|
175
|
+
# or auth failures default to 0 (no escalation, fall back to
|
|
176
|
+
# the line-count-only estimate).
|
|
177
|
+
THREAD_COUNT=$(gh api graphql -f query='{repository(owner:"'"$(echo "$REPO" | cut -d/ -f1)"'",name:"'"$(echo "$REPO" | cut -d/ -f2)"'"){pullRequest(number:'"$PR_NUMBER"'){reviewThreads(first:50){nodes{isResolved comments(first:1){nodes{author{login}}}}}}}}' --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and (.comments.nodes[0].author.login == "claude" or .comments.nodes[0].author.login == "claude[bot]"))] | length' 2>/dev/null || echo 0)
|
|
178
|
+
THREAD_COUNT=${THREAD_COUNT:-0}
|
|
179
|
+
|
|
130
180
|
if [ "$IS_TRIVIAL" = "true" ]; then
|
|
181
|
+
# Haiku route stays on a small flat budget — trivial PRs
|
|
182
|
+
# need ~5 turns; 10 leaves margin.
|
|
131
183
|
MAX_TURNS=10
|
|
184
|
+
TURNS_ESTIMATED=10
|
|
185
|
+
LINES_ADDED=0
|
|
186
|
+
LINES_DELETED=0
|
|
187
|
+
echo "::notice title=Clud Bug 🐛::Trivial (Haiku) budget: max_turns=10."
|
|
132
188
|
else
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
189
|
+
FILES_JSON=$(gh pr view "$PR_NUMBER" -R "$REPO" --json files --jq '.files' 2>/dev/null || echo '[]')
|
|
190
|
+
LINES_ADDED=$(echo "$FILES_JSON" | jq '[.[].additions] | add // 0' 2>/dev/null || echo 0)
|
|
191
|
+
LINES_DELETED=$(echo "$FILES_JSON" | jq '[.[].deletions] | add // 0' 2>/dev/null || echo 0)
|
|
192
|
+
LINES_ADDED=${LINES_ADDED:-0}
|
|
193
|
+
LINES_DELETED=${LINES_DELETED:-0}
|
|
194
|
+
|
|
195
|
+
# jq implements the formula from §5.5 Layer 1:
|
|
196
|
+
# per_file_cost = 0.3 + pa×tw + mod×1.5×tw + pd×0.1×tw
|
|
197
|
+
# estimated = 10 + sum(per_file_cost) + 1.5×threads
|
|
198
|
+
# type_weight tw (turns per line; reciprocal of lines-per-turn):
|
|
199
|
+
# code 1/50 = 0.02, docs 1/150 ≈ 0.00667,
|
|
200
|
+
# tests/config 1/100 = 0.01, unknown 1/80 = 0.0125
|
|
201
|
+
# Emit overhead 10 (raised from 5 in initial design): empirical
|
|
202
|
+
# tokenomics #21 (26 docs files, +310/-235) used ~25 turns vs
|
|
203
|
+
# formula-predicted 16 — structured-output emit + JSON-schema
|
|
204
|
+
# retries + initial context loading cost ~5-10 turns themselves.
|
|
205
|
+
# 10 matches better; will retune from Layer 1.5 calibration data.
|
|
206
|
+
# ceil via (+ 0.9999999 | floor) since jq has no ceil.
|
|
207
|
+
TURNS_ESTIMATED=$(echo "$FILES_JSON" | jq --argjson threads "$THREAD_COUNT" '
|
|
208
|
+
def per_line(path):
|
|
209
|
+
if (path | test("\\.(test|spec)\\.|__tests__|^tests?/")) then 0.01
|
|
210
|
+
elif (path | test("\\.(md|txt|rst|adoc|mdx)$")) then 0.00666667
|
|
211
|
+
elif (path | test("\\.(yml|yaml|toml|json|cfg|ini|conf|tmpl)$")) then 0.01
|
|
212
|
+
elif (path | test("\\.(ts|tsx|py|js|jsx|mjs|cjs|go|rs|java|kt|rb|php|cs|c|cpp|h|hpp|swift|scala)$")) then 0.02
|
|
213
|
+
else 0.0125 end;
|
|
214
|
+
map(
|
|
215
|
+
select(.path != "docs/timeline.md"
|
|
216
|
+
and .path != "docs/file-structure.md"
|
|
217
|
+
and .path != "docs/decisions.md")
|
|
218
|
+
| (.additions) as $add | (.deletions) as $del
|
|
219
|
+
| (if $add < $del then $add else $del end) as $mod
|
|
220
|
+
| ($add - $mod) as $pa | ($del - $mod) as $pd
|
|
221
|
+
| per_line(.path) as $tw
|
|
222
|
+
| 0.3 + ($pa * $tw) + ($mod * 1.5 * $tw) + ($pd * 0.1 * $tw)
|
|
223
|
+
) | (add // 0) + 10 + ($threads * 1.5) | (. + 0.9999999) | floor
|
|
224
|
+
' 2>/dev/null || echo 15)
|
|
225
|
+
TURNS_ESTIMATED=${TURNS_ESTIMATED:-15}
|
|
226
|
+
|
|
227
|
+
# max_turns = ceil(estimated × 1.2), floor 15, ceiling 60
|
|
228
|
+
MAX_TURNS=$(( (TURNS_ESTIMATED * 12 + 9) / 10 ))
|
|
229
|
+
[ "$MAX_TURNS" -lt 15 ] && MAX_TURNS=15
|
|
230
|
+
[ "$MAX_TURNS" -gt 60 ] && MAX_TURNS=60
|
|
231
|
+
echo "::notice title=Clud Bug 🐛::Smart budget: estimated $TURNS_ESTIMATED turns → max_turns=$MAX_TURNS ($FILE_COUNT files, +$LINES_ADDED/-$LINES_DELETED lines, $THREAD_COUNT prior threads). v0.6.25 Phase 1; coefficients TBD from calibration data."
|
|
145
232
|
fi
|
|
146
|
-
|
|
233
|
+
{
|
|
234
|
+
echo "max_turns=$MAX_TURNS"
|
|
235
|
+
echo "turns_estimated=$TURNS_ESTIMATED"
|
|
236
|
+
echo "files_count=$FILE_COUNT"
|
|
237
|
+
echo "lines_added=$LINES_ADDED"
|
|
238
|
+
echo "lines_deleted=$LINES_DELETED"
|
|
239
|
+
echo "threads_count=$THREAD_COUNT"
|
|
240
|
+
} >> "$GITHUB_OUTPUT"
|
|
147
241
|
|
|
148
242
|
clud-bug-review:
|
|
149
243
|
needs: paths-check
|
|
@@ -288,23 +382,53 @@ jobs:
|
|
|
288
382
|
Review this pull request following the discipline in your
|
|
289
383
|
system prompt — every rule about skill routing, comment
|
|
290
384
|
format, the strict-mode header, the two-surface review
|
|
291
|
-
shape,
|
|
385
|
+
shape, the FIX-PUSH FLOW, and (v0.6.25+) turn-budget
|
|
386
|
+
self-rationing applies.
|
|
387
|
+
|
|
388
|
+
## Your turn budget for this PR (v0.6.25 / §5.5 Layer 2)
|
|
389
|
+
|
|
390
|
+
max_turns=${{ needs.paths-check.outputs.max_turns }},
|
|
391
|
+
estimated=${{ needs.paths-check.outputs.turns_estimated }},
|
|
392
|
+
files=${{ needs.paths-check.outputs.files_count }},
|
|
393
|
+
+${{ needs.paths-check.outputs.lines_added }}/-${{ needs.paths-check.outputs.lines_deleted }} lines,
|
|
394
|
+
prior_threads=${{ needs.paths-check.outputs.threads_count }}.
|
|
395
|
+
|
|
396
|
+
Plan accordingly per the "Turn budget self-rationing"
|
|
397
|
+
section in your system prompt. The estimate is the
|
|
398
|
+
pre-flight's best guess; the cap is the workflow's hard
|
|
399
|
+
stop. Reserve 5 turns for emit.
|
|
292
400
|
id: clud-bug-review
|
|
293
401
|
|
|
294
402
|
# v0.6.22 / 0.0.O: render the structured output to the summary
|
|
295
403
|
# comment shape, post via gh pr comment. Guarded so we only run
|
|
296
404
|
# when the action returned a non-empty structured payload
|
|
297
405
|
# (max-retries hit → empty → fall through to the next step).
|
|
406
|
+
#
|
|
407
|
+
# v0.6.25 / §5.5 Layer 1.5 (calibration measurement): append a
|
|
408
|
+
# hidden HTML marker carrying the pre-flight estimate + the
|
|
409
|
+
# actual max_turns we passed to claude-code-action. Aggregation
|
|
410
|
+
# script lives in clud-bug usage --calibration (TBD v0.6.26+);
|
|
411
|
+
# for now, the marker is just an audit trail visible via
|
|
412
|
+
# `gh api ...issues/N/comments`.
|
|
298
413
|
- name: Render + post structured review
|
|
299
414
|
if: success() && steps.clud-bug-review.outputs.structured_output != ''
|
|
300
415
|
env:
|
|
301
416
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
302
417
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
303
418
|
STRUCTURED: ${{ steps.clud-bug-review.outputs.structured_output }}
|
|
419
|
+
TURNS_ESTIMATED: ${{ needs.paths-check.outputs.turns_estimated }}
|
|
420
|
+
MAX_TURNS: ${{ needs.paths-check.outputs.max_turns }}
|
|
421
|
+
FILES_COUNT: ${{ needs.paths-check.outputs.files_count }}
|
|
422
|
+
LINES_ADDED: ${{ needs.paths-check.outputs.lines_added }}
|
|
423
|
+
LINES_DELETED: ${{ needs.paths-check.outputs.lines_deleted }}
|
|
424
|
+
THREADS_COUNT: ${{ needs.paths-check.outputs.threads_count }}
|
|
304
425
|
run: |
|
|
305
426
|
set -euo pipefail
|
|
306
427
|
BODY=$(printf '%s\n' "$STRUCTURED" | npx --yes clud-bug@{{CLUD_BUG_VERSION}} render --stdin)
|
|
307
|
-
|
|
428
|
+
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 -->"
|
|
429
|
+
gh pr comment "$PR_NUMBER" --body "$BODY
|
|
430
|
+
|
|
431
|
+
$CALIBRATION"
|
|
308
432
|
|
|
309
433
|
# Fallback comment when the model couldn't produce schema-valid
|
|
310
434
|
# output after max retries (structured_output is empty). Keeps a
|
|
@@ -342,7 +466,7 @@ jobs:
|
|
|
342
466
|
# Letting the action's own failure fail the check is louder and right.
|
|
343
467
|
- name: Strict mode — fail check on critical findings
|
|
344
468
|
if: success()
|
|
345
|
-
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.
|
|
469
|
+
uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.25
|
|
346
470
|
with:
|
|
347
471
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
348
472
|
# v0.6.22 / 0.0.O: the summary is now posted by the workflow
|