ci: add docs-request path — write-docs stage parallel to propose-fix#2535
Conversation
Tonight's pipeline test ([#2530] → [#2533]) demonstrated that propose-fix produces solid bug-path PRs end-to-end, but the existing classification taxonomy left a gap: pure docs-request issues fell through into `other` (no automation) or, if reframed as `framework-design`, would attempt the TDD-shaped propose-fix prompt that doesn't fit doc-only work. This PR adds a fourth classification path: triage → docs-request + docs-confidence:high → bot-write-docs.yml → docs/bot-<issue>-<slug> branch → draft PR (TDD gate skips, since the branch prefix says docs) → Reviewer A/B run as normal so the human merge has context ## What changed ### New files - `.claude/commands/write-docs.md` — the prompt. Mirrors propose-fix's shape but is doc-paths-only (no specs, no test runs, no `vendor/`, `app/`, or `tests/` writes). Uses screenshot-placeholder MDX comments for features that benefit from images, since the bot can't run a headless browser. - `.github/workflows/bot-write-docs.yml` — the workflow. Sonnet not Opus (doc work is pattern-recognition, not reasoning-heavy); 30-turn budget; narrow allowlist; same `wheels-bot[bot]` author check as the other Phase 4 auto-fire stages. ### Modified files - `.claude/commands/triage-issue.md` — adds `docs-request` to the classification taxonomy with explicit boundary against `other` ("clear deliverable in the issue?"); adds the docs-request branch in step 4 with confidence rating + comment template emitting the `docs-confidence:high` marker. - `.github/workflows/bot-tdd-gate.yml` — gains `is_docs` output that detects `docs/bot-*` head refs; the verify step now guards on `is_bot && !is_docs`. Docs-only PRs by definition have no spec/impl pair to validate. - `.github/workflows/bot-update-docs.yml` — skips `docs/bot-*` head refs to avoid the redundant case where update-docs runs on a PR whose entire purpose was docs. - `docs/contributing/wheels-bot.md` — six stages → seven; new "### 4. Write Docs" section; Update Docs / Reviewer A / Reviewer B renumbered to 5/6/7; markers table gains the three new markers (`docs-confidence:high`, `write-docs:<issue>`, `docs-held:<issue>`) and adds `docs-request` to the triage-class enumeration. ## Why a separate workflow rather than a mode of bot-update-docs The two workflows look superficially similar but their inputs are different shapes — bot-update-docs adds a commit to an *existing* PR (needs PR number), bot-write-docs creates a *new* PR from an issue (needs issue number). Combining them would require dual-mode logic in one workflow with different env, different concurrency keys, and a non-trivial branch on event_name. Two narrow workflows keep each one clear about its job. ## Coordinated repo-level change The `wheels-bot push scope` ruleset (16174270) needs `docs/bot-*/**` added to the allowed push patterns alongside `bot/**` and `fix/bot-*/**`. Done out-of-band via the GitHub UI before this PR landed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: Solid addition of the docs-request → write-docs pipeline path. The workflow structure, idempotency guards, TDD-gate bypass, and marker naming are all consistent with the existing Phase 4 conventions. One meaningful omission: CLAUDE.md's ## Wheels Bot section was not updated and is now stale in three distinct ways — which matters because that section is loaded as primary context on every bot run. Two minor nits follow.
Docs
CLAUDE.md §Wheels Bot not updated — four separate staleness issues
CLAUDE.md is loaded into every agent's context via the system reminder on every run. The PR correctly updated docs/contributing/wheels-bot.md, but CLAUDE.md still reflects a five-stage world:
1. Stage count (line 1091)
Five stages, all opt-out via the `[skip-claude]` label ...With this PR the count is seven (Triage, Research, Propose Fix, Write Docs, Update Docs, Reviewer A, Reviewer B). A future triage agent reading line 1091 will have a wrong mental model of the pipeline.
2. Triage output description (line 1095)
| Triage | issue opened/reopened | Sonnet | Comment classifying as `bug` / `framework-design` / `other` (+ confidence on `bug` path). |docs-request is missing from the classification list and the note about confidence only mentions the bug path. A triage agent using this as its authoritative reference may classify actionable docs issues as other rather than docs-request.
Suggested replacement:
| Triage | issue opened/reopened | Sonnet | Comment classifying as `bug` / `framework-design` / `docs-request` / `other` (+ confidence on `bug` and `docs-request` paths). |3. Stage table (lines 1093–1099)
Write Docs and Update Docs are absent from the table. Suggested additions after Propose Fix:
| Write Docs | bot triage emits `docs-confidence:high` (or `workflow_dispatch`) | Sonnet | Doc-paths-only draft PR on branch `docs/bot-<issue>-<slug>`. TDD gate skips; Reviewer A/B still run. |
| Update Docs | bot PR opened / synchronized | Sonnet | Doc commits added to a freshly-opened bot fix PR (skips `docs/bot-*` branches). |4. Marker conventions (line 1102) and auto-fire safety net (line 1113)
- `<!-- wheels-bot:triage-class:<bug|framework-design|other> -->`Missing docs-request from the class enum. The new markers (docs-confidence:high, write-docs:<issue>, docs-held:<issue>, update-docs:<pr>) are also absent. And line 1113's auto-fire safety net only mentions the triage → research → propose-fix chain, leaving out triage → write-docs.
These are all in CLAUDE.md at lines 1091–1113, which are not in the diff. A follow-up commit touching that section would close the gap.
Conventions
Push detection uses working-tree diff rather than commit count
In .github/workflows/bot-write-docs.yml, the push step detects whether Claude produced any work:
if git diff --quiet origin/develop -- && git diff --cached --quiet; then
echo "No changes produced — nothing to push."
exit 0
fi
git push -u origin "${{ steps.branch.outputs.name }}"This works correctly in practice — after a commit the working tree matches HEAD, which differs from origin/develop, so the diff is non-empty and the push fires. But the intent is "did Claude create any new commits?", and the direct check for that is:
if git log origin/develop..HEAD --oneline | grep -q .; then
git push -u origin "${{ steps.branch.outputs.name }}"
else
echo "No commits produced — nothing to push."
fiThe current form works but will confuse anyone debugging a future partial-failure scenario (e.g. Claude left uncommitted edits in the working tree — the diff fires but nothing is actually committed). Low priority, noting for clarity.
Prompt step 7 vs. gh pr create auto-push
write-docs.md step 7 says "The caller workflow handles the actual git push — just commit cleanly." Step 8 then instructs Claude to call gh pr create, which auto-pushes the branch to remote if it has no upstream. The separate push step in the workflow becomes a no-op in the happy path. The comment in step 7 is technically inaccurate (gh pr create is the actual pusher). Not a bug, but the explanation may mislead a future prompt editor. Adjusting step 7 to say "Do not call git push directly — the gh pr create in step 8 handles the upstream push, and the workflow has a safety-net push step for the uncommon case where gh does not push" would be clearer.
Commits
Single commit: ci: add docs-request path — write-docs stage parallel to propose-fix. Type ci, no scope, sentence-case subject, well under 100 chars. ✓ Commit message describes the why (parallel path for docs work, mirrors propose-fix's shape) rather than the what. ✓
Security
The issue_comment auto-fire condition on bot-write-docs.yml is correctly gated on github.event.comment.user.login == 'wheels-bot[bot]'. The bot-identity check is load-bearing as the comment notes, and it matches the pattern from the other Phase 4 workflows. The allowed_bots parameter on the Claude action is scoped to bot identities and does not widen the trigger surface. No issues found.
The filesystem write scope (doc paths only) is enforced at the prompt level rather than the tool allowlist — Write is unrestricted in allowedTools. This is the same accepted pattern as bot-propose-fix.yml. Worth noting that the allowlist is the only enforcement; a sufficiently confused agent could write to non-doc paths. The TDD gate skip is keyed on branch prefix, not on file paths actually changed — so a docs bot PR that snuck in a vendor/ file would still pass the gate. Acceptable risk for now but a follow-up git diff --name-only check in the gate could tighten this later.
Wheels Bot — Reviewer B (round 1)A's review is substantive and well-verified. The primary finding -- CLAUDE.md's Wheels Bot section not updated -- is accurate and operationally significant: CLAUDE.md is loaded as primary agent context on every bot run, and the stale stage count, missing SycophancyNone detected. A used COMMENTED rather than approve, consistent with a real finding that has live operational impact. False positivesNone detected. Every claim A makes is accurate against the diff:
Missed issuesOne framing point worth correcting:
No cross-engine issues exist -- no CFML changed. No commit convention violations. No Verdict alignmentA used COMMENTED rather than request-changes. The CLAUDE.md staleness is not purely cosmetic -- the missing |
Summary
Adds a fourth classification path to the wheels-bot pipeline. Previously, docs-only issues fell through to
other(no automation) or, when reframed asframework-design, would hit propose-fix's TDD-shaped prompt that doesn't fit doc-only work. This PR adds a parallel docs-request → write-docs path that mirrors propose-fix's shape but is doc-paths-only.The new path
What changed
New files
.claude/commands/write-docs.mdvendor/,app/,tests/). Uses MDX screenshot-placeholder comments for features that benefit from images (the bot has no headless browser)..github/workflows/bot-write-docs.ymlwheels-bot[bot]author check pattern as the other Phase 4 auto-fire stages.Modified files
.claude/commands/triage-issue.mddocs-requestclassification with explicit boundary againstother("clear deliverable in the issue?"). New branch in step 4 with confidence rating + comment template emittingdocs-confidence:high..github/workflows/bot-tdd-gate.ymlis_docsoutput that detectsdocs/bot-*head refs; verify step guards onis_bot && !is_docs. Docs-only PRs by definition have no spec/impl pair..github/workflows/bot-update-docs.ymldocs/bot-*head refs to avoid the redundant case where update-docs runs on a PR whose entire purpose was docs.docs/contributing/wheels-bot.mdDesign decisions worth flagging
bot-update-docs.ymldocs/bot-*(though no human would, which is fine).bot-update-docs.yml's choice.docs-confidence:highbot PR even draft→ review) already covers this. No new work needed.Coordinated repo-level change
The
wheels-bot push scoperuleset (16174270) neededdocs/bot-*/**added to the allowed push patterns alongsidebot/**andfix/bot-*/**. Done out-of-band via the GitHub UI before this PR landed.Test plan
After merge, file an issue that should classify as
docs-request(e.g., the Debug Panel docs gap discussed earlier) and observe:docs-request(nototherorframework-design).wheels-bot:docs-confidence:highon a clear, well-scoped docs gap.bot-write-docs.ymlauto-fires from the marker (event =issue_comment, author =wheels-bot[bot]).docs/bot-<issue>-<slug>branch and opens a draft PR.vendor/,app/,tests/).bot-tdd-gate.ymlruns and exits cleanly with "Docs-only bot PR — gate is a no-op" message (no spec required).bot-update-docs.ymldoes NOT fire on the docs PR (head ref starts withdocs/bot-).--draftuntil a human marks ready and approves.🤖 Generated with Claude Code