Skip to content

feat(workflows): raise Claude max-turns to 50 and split models by job difficulty - #17

Merged
laurigates merged 1 commit into
mainfrom
feat/claude-workflow-turn-model-config
Jul 2, 2026
Merged

feat(workflows): raise Claude max-turns to 50 and split models by job difficulty#17
laurigates merged 1 commit into
mainfrom
feat/claude-workflow-turn-model-config

Conversation

@laurigates

Copy link
Copy Markdown
Owner

What

Bumps the default max-turns on every Claude-powered workflow to 50 (was 5–6 on the analysis workflows, 20–30 elsewhere) and replaces the flat --model haiku with a difficulty-based opus/sonnet split.

Every analysis workflow gains a model input so callers can override the model the same way they already can override max-turns (and the review/auto-fix workflows keep their claude_args override).

Model split

Model Workflows
opus (deep reasoning) reusable-security-owasp, reusable-quality-async, reusable-a11y-wcag, reusable-claude-review, reusable-claude (@claude mentions), reusable-auto-fix
sonnet (more mechanical) reusable-security-secrets, reusable-security-deps, reusable-quality-typescript, reusable-quality-code-smell, reusable-a11y-aria, reusable-auto-resolve-conflicts

Why

The 5–6 turn haiku budgets flake on large PRs — jobs die with error_max_turns in a rotating subset across re-runs, producing red ❌s that are budget exhaustion rather than findings (see ForumViriumHelsinki/.github#79 for the same failure class). 50 turns gives the analyses room to actually finish, and stronger models reduce wasted turns per finding.

Notes

  • Callers pin @main, so this takes effect on the next fresh pull_request event after merge (re-runs replay the old resolution).
  • actionlint findings on these files are pre-existing (undefined action-output properties, shellcheck style notes in untouched script blocks); none touch the changed lines.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vuuw6bCHCoYyhT7Q6jXhRj

… difficulty

Raise the default turn budget on every Claude-powered workflow from
5-30 to 50 so large PRs no longer flake with error_max_turns, and
replace the flat haiku model with a difficulty-based split, overridable
per caller via a new 'model' input on each analysis workflow:

- opus: security-owasp, quality-async, a11y-wcag, claude-review,
  claude (@claude mentions), auto-fix
- sonnet: security-secrets, security-deps, quality-typescript,
  quality-code-smell, a11y-aria, auto-resolve-conflicts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vuuw6bCHCoYyhT7Q6jXhRj
@laurigates laurigates added the enhancement New feature or improvement label Jul 2, 2026
@laurigates laurigates self-assigned this Jul 2, 2026
@laurigates
laurigates merged commit 573d6df into main Jul 2, 2026
@laurigates
laurigates deleted the feat/claude-workflow-turn-model-config branch July 2, 2026 17:55
laurigates added a commit that referenced this pull request Jul 2, 2026
…deprecated direct_prompt (#18)

> **Stacked on #17** — do not merge before #17. When #17 merges,
retarget this PR to `main` first (it will auto-close otherwise).

## What

Two latent bugs found while auditing against the [claude-code-action
docs](https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md):

### 1. Analysis workflow outputs were always empty

All eight analysis workflows declared job outputs like `issues-found:
${{ steps.scan.outputs.total }}`, but the action only exposes
`execution_file`, `branch_name`, `github_token`, `structured_output`,
and `session_id`. Asking Claude to print `TOTAL_ISSUES: n` in a PR
comment never produced a step output, so:

- every declared workflow output was empty
- **`fail-on-critical` (OWASP) and `fail-on-high` (deps) could never
fire** — the gate compared against an empty string

Fix: pass `--json-schema` in `claude_args` (the action's supported
structured-output mechanism) and read counts via
`fromJSON(steps.<id>.outputs.structured_output || '{}').<field>`. The
`|| '{}'` guard keeps output evaluation safe when the analysis step is
skipped (no matching changed files). Prompts now instruct Claude to fill
the structured fields instead of printing count markers.

### 2. `reusable-auto-fix` used the removed-soon `direct_prompt` input

`direct_prompt` is deprecated upstream (migration table says use
`prompt`). Renamed the input in the action call — content unchanged.

## Verification

`actionlint` on all nine files: the previous `property "total" is not
defined` / `input "direct_prompt" is not defined` findings are gone;
remaining findings are pre-existing shellcheck style notes in untouched
script blocks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Vuuw6bCHCoYyhT7Q6jXhRj

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
laurigates added a commit that referenced this pull request Jul 2, 2026
…timeouts (#19)

> **Stacked on #18** (which is stacked on #17) — merge order: #17#18
→ this. Retarget this PR to `main` before merging #18, or it will
auto-close.

## What

Adopts
[claude-code-action](https://github.com/anthropics/claude-code-action)
options we weren't using, all overridable per caller:

| Input | Where | Default | Why |
|---|---|---|---|
| `use-sticky-comment` | 8 analysis workflows | `true` | One maintained
PR comment per analysis instead of a new comment on every push — 8
analyses × N pushes was a lot of noise |
| `allowed-bots` | 8 analysis workflows | `''` (`renovate[bot]` on
security-deps) | The action allows **no** bots by default, so
bot-authored PRs fail with "non-human actor". Deps audit triggers on
lockfile changes — mostly Renovate PRs, so it allowlists renovate by
default |
| `max-budget-usd` | 8 analysis workflows | `0` (unlimited) | Per-run
spend backstop via `--max-budget-usd`, relevant now that defaults are
opus + 50 turns |
| `timeout-minutes` / `timeout_minutes` | all Claude jobs | 30 (45 for
`@claude`) | A wedged run currently holds a runner for GitHub's 6-hour
default |
| `allowed_bots` | claude-review | `claude[bot],claude` | Was hardcoded;
now an input |

## Verification

`actionlint` clean on all eleven files (only pre-existing shellcheck
style notes remain).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Vuuw6bCHCoYyhT7Q6jXhRj

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
laurigates added a commit that referenced this pull request Jul 2, 2026
`just lint` only validated YAML syntax; the workflow edits in #17#19
needed actionlint run by hand each time. This adds actionlint to the
recipe via mise's aqua backend, with shellcheck integration disabled
(`-shellcheck=`) because those style findings on `run:` blocks are
accepted pre-existing noise — actionlint still checks expressions,
action inputs, and workflow structure (it's what caught the broken
outputs fixed in #18).

Verified: `just lint` → "All workflow files are valid YAML." +
"actionlint: clean".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Vuuw6bCHCoYyhT7Q6jXhRj

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
laurigates added a commit that referenced this pull request Jul 3, 2026
… fix (#30)

## Summary

Surgical reconciliation of `reusable-security-deps.yml` against the FVH
upstream — ports exactly two deltas while preserving every local
improvement from PRs #17/#18/#19 (model + max-turns inputs, allowed-bots
input, sticky comments, max-budget-usd, timeout-minutes,
structured_output JSON schema).

- **Native bun audit**: the `bun)` audit case fell back to `npm audit`,
which ENOLOCKs on bun repos (no `package-lock.json`), silently producing
no audit results. `bun audit` reads `bun.lock` natively since Bun
>=1.2.15 with no install needed.
- **allowed-bots re-run posture**: dependency PRs are bot-authored
(Renovate, release-please), and a GitHub re-run replays the original
`pull_request` event whose sender is the bot — so the action's
human-actor guard blocked re-runs under the old `renovate[bot]` default.
The default is now `'*'`; the job is audit-and-comment only with a fixed
prompt, so widening is safe.

## Changes

- `.github/workflows/reusable-security-deps.yml`
- `bun)` case: `npm audit --json` fallback → `bun audit --json`, with
FVH's rationale comment.
- `allowed-bots` input: default `'renovate[bot]'` → `'*'`; description
updated to explain the `'*'` default and why.
- FVH's re-run rationale ported as a comment next to the action's
`allowed_bots:` line.
- The `allowed-bots` **input is kept** (FVH hardcodes a literal `"*"`);
callers can still narrow it.

## Verification

- `just lint`: "All workflow files are valid YAML." and "actionlint:
clean".
- Diff manually verified to be confined to the two named spots; no local
improvements regressed.

## Notes

- FVH's cheaper-model choice (`haiku`, `max-turns 5`) and its
machine-readable-counts prompt/output parsing were **deliberately not
ported** — local PR #17 split models by job difficulty and local PRs
#18/#19 moved output parsing to structured_output JSON schemas.
- Branch `feat/analysis-diff-size-gate` (issue #25) touches this same
file on a separate branch; whichever merges second may need a trivial
rebase.

Closes #23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_017RvFaUeBffGUoT3ioujtXw

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
laurigates added a commit to laurigates/claude-plugins that referenced this pull request Jul 3, 2026
…code-github-workflows (#1910)

## What

Adds a **claude-code-action v1 Gotchas** section to
`claude-code-github-workflows` covering four facts that each produced a
silently-broken workflow in production (laurigates/.github#17#19):

1. **Outputs are fixed** — the action exposes only
`execution_file`/`branch_name`/`github_token`/`structured_output`/`session_id`;
count-style outputs must flow through `claude_args --json-schema` and be
read with `fromJSON(steps.<id>.outputs.structured_output ||
'{}').<field>` (the guard is required because job outputs evaluate on
skipped steps). Includes a worked snippet.
2. **`allowed_bots` defaults to empty** — Renovate/release-please PRs
fail with "non-human actor", and re-runs replay the original bot sender.
3. **Deprecated inputs** (`direct_prompt`, `max_turns`, `model`, …) may
be silently ignored — a `direct_prompt` workflow can run with no prompt
at all.
4. **Budget levers** — `--max-turns` / `--max-budget-usd` only (no token
budget); both fail mid-run, and turn exhaustion shows as
`error_max_turns` with a rotating failing-job set across re-runs.

## Why here

Session evidence: auditing laurigates/.github's reusable workflows
against the action docs found all eight analysis workflows with
permanently-empty outputs and dead `fail-on-critical` gates (fixed in
laurigates/.github#18). The gotchas are action-level, not repo-level, so
they belong in the marketplace skill every repo loads.

README not updated: the skill's catalog entry/description is unchanged —
this is an additive section within the existing skill.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Vuuw6bCHCoYyhT7Q6jXhRj

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant