Skip to content

Commit c25b390

Browse files
committed
feat(ci): auto-commit docs fixes from the docs check
Set `auto_fix: true` on the `_claude-docs-check.yml` call so a FAIL verdict is repaired and pushed instead of only reported. The toolkit runs a second Claude pass scoped to Read/Edit/Write/Grep/Glob/Bash(git:*), commits as `docs(claude): fix documentation issues (auto-fix)`, and pushes directly to the PR head branch using `WORKFLOW_PAT` so the push re-triggers the check. `auto_commit` is deliberately left dispatch-only. It is a different mechanism — it overwrites whole files with the model's `suggested_content` and carries no loop guard — and its commit subject does not match the anchored regex `auto_fix`'s loop guard keys on, so enabling both would make the two push over each other indefinitely. Fork PRs are unaffected: the job-level `if:` already requires `head.repo.full_name == github.repository`, and every auto-fix step inside the reusable workflow re-checks the PR's live head repo independently. Auto-fix is dormant on bot-authored PRs until ai-toolkit plumbs `allowed_bots` through, because the job still fails at claude-code-action's actor check before any verdict is produced.
1 parent 693892c commit c25b390

2 files changed

Lines changed: 88 additions & 1 deletion

File tree

.github/workflows/CLAUDE.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,61 @@ needed to merge here, but the durable fix is to drop
300300
`docs-check / docs-check` from the ruleset in favour of a context that
301301
always reports.
302302

303+
#### Auto-committing doc fixes (`auto_fix: true`)
304+
305+
**This workflow now pushes commits to PR head branches.** `auto_fix: true`
306+
is set on the reusable-workflow call. When the check reaches a verdict and
307+
that verdict is `FAIL` — or it produced any suggestions or missing updates —
308+
the toolkit runs a **second** Claude pass, scoped to
309+
`Read,Edit,Write,Grep,Glob,Bash(git:*)`, commits whatever it changed as
310+
`docs(claude): fix documentation issues (auto-fix)` authored by
311+
`github-actions[bot]`, and pushes **straight to the PR's head branch**. It
312+
does not open a fixup branch and it does not open a follow-up PR.
313+
314+
Blast radius, stated plainly:
315+
316+
- The job holds `contents: write` and will rewrite **contributors' branches**,
317+
not just its own. It applies to **human-authored PRs as much as bot ones** —
318+
nothing in the gate distinguishes them for this purpose.
319+
- **Fork PRs are unaffected.** Two independent guards: this repo's job-level
320+
`if:` still requires
321+
`github.event.pull_request.head.repo.full_name == github.repository`, and
322+
every auto-fix step inside the reusable workflow separately re-checks
323+
`steps.pr-branch.outputs.repo_full_name == github.repository` against the
324+
PR's live head repo. Either one alone would be sufficient.
325+
- **A loop guard exists.** Auto-fix is skipped when the PR head commit is
326+
authored by `github-actions[bot]` **and** its subject matches
327+
`^\w+\(claude\):.*\(auto-fix\)$`. The author check uses `.author.login`
328+
from the API (GitHub-verified) rather than `.commit.author.name`
329+
(user-controlled git metadata), and fails open — an unlinked email means
330+
auto-fix runs.
331+
- **It flips the check green when it pushes.** With `WORKFLOW_PAT` set, a
332+
`FAIL` verdict that produced a push exits `0` on the theory that the push
333+
re-triggers a fresh check. If `WORKFLOW_PAT` is _unset_, the push falls
334+
back to `GITHUB_TOKEN`, which does not trigger workflows, and the toolkit
335+
fails the job with an explicit message rather than reporting green against
336+
unvalidated code. That is the failure mode to look for if auto-fix commits
337+
land but no follow-up check appears.
338+
339+
**Not yet effective on bot-authored PRs.** Auto-fix runs off
340+
`steps.process-results.outputs.verdict`, and on a `claude[bot]` PR the job
341+
still dies earlier at `claude-code-action`'s `allowed_bots` actor check
342+
(above), so `Process Results` is skipped and no verdict is ever produced.
343+
Auto-fix is therefore live for human-authored PRs today and dormant on bot
344+
PRs until `allowed_bots` is plumbed through `_claude-docs-check.yml` in
345+
`Uniswap/ai-toolkit`.
346+
347+
**Do not enable `auto_commit` as well.** It is a different mechanism —
348+
`post-docs-check.ts`'s `autoCommitChanges`, which applies the model's
349+
structured `suggestions[]` by overwriting whole files with
350+
`suggested_content`, has no loop guard of its own, and commits as
351+
`docs: auto-update documentation`. That subject does not match the anchored
352+
regex `auto_fix`'s loop guard keys on, so with both enabled the two
353+
mechanisms push over each other indefinitely. `auto_commit` remains wired to
354+
`github.event.inputs.auto_commit`, which is empty on `pull_request` events
355+
and so always `false` there — that expression is why the check has never
356+
auto-committed on a PR, and it is intentionally left alone.
357+
303358
### Generate PR Title & Description
304359

305360
**File:** `generate-pr-title-description.yml`

.github/workflows/claude-docs-check.yml

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,34 @@ jobs:
9494
with:
9595
pr_number: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
9696
suggestion_mode: ${{ github.event.inputs.suggestion_mode || 'suggest' }}
97+
# `auto_commit` stays manual-dispatch-only. On a `pull_request` event
98+
# `github.event.inputs` is empty, so this expression is always `false`
99+
# there — which is why the docs check has never auto-committed anything
100+
# on a PR. Auto-committing is enabled via `auto_fix` below instead, not
101+
# by flipping this. The two are different mechanisms and enabling both
102+
# is a mistake: `auto_commit` applies the model's structured
103+
# `suggestions[]` array by *overwriting whole files* with
104+
# `suggested_content` (`post-docs-check.ts`, `autoCommitChanges`), it
105+
# has no loop guard, and its commit subject
106+
# (`docs: auto-update documentation`) does not match the anchored
107+
# `^\w+\(claude\):.*\(auto-fix\)$` regex that `auto_fix`'s loop guard
108+
# keys on — so the two would push over each other indefinitely.
97109
auto_commit: ${{ github.event.inputs.auto_commit == 'true' }}
110+
# AUTO-COMMIT DOC FIXES. When the check returns a FAIL verdict (or finds
111+
# any suggestions / missing updates), the toolkit runs a second Claude
112+
# pass scoped to `Read,Edit,Write,Grep,Glob,Bash(git:*)`, commits the
113+
# result as `docs(claude): fix documentation issues (auto-fix)` authored
114+
# by `github-actions[bot]`, and pushes it **directly to the PR's head
115+
# branch** — not to a fixup branch and not as a follow-up PR. The push
116+
# uses `WORKFLOW_PAT` (passed below), which is what lets the resulting
117+
# commit re-trigger this workflow so the fixed code gets a fresh verdict;
118+
# a `GITHUB_TOKEN` push would not re-trigger, and the toolkit
119+
# deliberately fails the job in that case rather than leaving a stale
120+
# green. Blast radius: this rewrites contributors' branches, human- and
121+
# bot-authored alike. It cannot touch a fork — every auto-fix step is
122+
# additionally gated on `head.repo.full_name == github.repository`
123+
# inside the reusable workflow, on top of this job's own `if:`.
124+
auto_fix: true
98125
# Use sonnet for faster, cheaper checks
99126
model: 'claude-sonnet-5'
100127
# Fail if plugin versions aren't bumped
@@ -106,7 +133,12 @@ jobs:
106133
secrets:
107134
# Use OAuth token (Pro/Max) instead of API key
108135
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
109-
# PAT for branch creation
136+
# PAT for branch creation and for the `auto_fix` push. Required for
137+
# auto-fix to be useful: the toolkit falls back to `GITHUB_TOKEN` when
138+
# this is unset, and a `GITHUB_TOKEN` push does not trigger workflows,
139+
# so the fix would never be re-validated. The toolkit's `Set Exit Code`
140+
# step detects that case and fails the job with an explicit message
141+
# rather than reporting a passing check against unverified code.
110142
WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }}
111143
permissions:
112144
contents: write

0 commit comments

Comments
 (0)