Skip to content

Commit 24b83e8

Browse files
bpamiriclaude
andauthored
ci: add docs-request path — write-docs stage parallel to propose-fix (#2535)
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>
1 parent 109f3f1 commit 24b83e8

6 files changed

Lines changed: 431 additions & 20 deletions

File tree

.claude/commands/triage-issue.md

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,15 @@ below. Highlights for this command:
3939
should Wheels do X," "Rails does it like Y, can Wheels do that," anything
4040
that requires picking among reasonable approaches before code can be
4141
written. The answer is a design decision, not a defect fix.
42-
- **`other`** — docs request, support question ("how do I…"), discussion
43-
thread without an actionable ask, broad product feedback.
42+
- **`docs-request`** — actionable docs work needed. There's a concrete
43+
deliverable (a specific feature, page, or section that should exist
44+
or be updated). Distinguished from `other` by: is there a clear
45+
"what should the docs contain" deliverable in the issue?
46+
"Document the Debug Panel features" → `docs-request`.
47+
"The docs are confusing" → `other`.
48+
- **`other`** — non-actionable docs feedback, support question
49+
("how do I…"), discussion thread without an actionable ask, broad
50+
product feedback.
4451

4552
When the report mixes a bug with a feature request, classify as `bug` if
4653
the bug is reproducible in isolation; otherwise `framework-design`.
@@ -84,6 +91,53 @@ below. Highlights for this command:
8491

8592
Then exit.
8693

94+
### If `docs-request`
95+
96+
Identify the rough docs scope from the issue body — what page(s) or
97+
section(s) need creating or updating, and roughly how much work it
98+
represents. Then self-rate confidence:
99+
100+
- **`high`**: the gap is concrete (specific feature/behavior to
101+
document), the right page/path is clear from the issue or from
102+
existing structure under
103+
`web/sites/guides/src/content/docs/v4-0-0-snapshot/`, the work is
104+
mostly translation-of-code (not requiring deep design decisions).
105+
High-confidence docs-requests trigger the auto-fire write-docs
106+
workflow.
107+
- **`medium`**: the gap is real but the right page/path is ambiguous,
108+
OR a new top-level section is needed (a structural design decision),
109+
OR the docs require significant code investigation to write
110+
accurately.
111+
- **`low`**: the gap is vague, the scope is large (e.g. "rewrite the X
112+
chapter"), or it's not clear what concretely needs to exist.
113+
114+
Post the triage comment:
115+
116+
```
117+
## Wheels Bot — Triage
118+
119+
Classified as **docs-request**.
120+
121+
### Docs scope
122+
123+
<one paragraph: what page(s) need creating/updating and what they should cover>
124+
125+
### Confidence: `<low|medium|high>`
126+
127+
<one sentence: why this confidence level>
128+
129+
<!-- wheels-bot:triage:<issue-number> -->
130+
<!-- wheels-bot:triage-class:docs-request -->
131+
<CONFIDENCE_MARKER>
132+
```
133+
134+
Where `<CONFIDENCE_MARKER>` is:
135+
- `<!-- wheels-bot:docs-confidence:high -->` if confidence is high
136+
(triggers auto-fire of `bot-write-docs.yml`)
137+
- omitted otherwise (medium/low confidence does not auto-trigger)
138+
139+
Then exit.
140+
87141
### If `bug`
88142

89143
Continue to step 5.
@@ -153,6 +207,9 @@ below. Highlights for this command:
153207
and does the fix sketch identify a plausible target file?
154208
- For `bug` path: is the confidence consistent with the auto-downgrade
155209
rules?
210+
- For `docs-request` path: is the docs scope concrete (a named page or
211+
section), and is the confidence rating consistent with how clear the
212+
deliverable is?
156213
- Are all required markers present?
157214

158215
If any check fails, fix and re-post (do not post twice).

.claude/commands/write-docs.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# /write-docs
2+
3+
Create a new documentation PR from a docs-request triaged issue. Writes
4+
MDX guide pages, `.ai/wheels/` references, or `CLAUDE.md` updates as
5+
appropriate, then opens a draft PR against `develop`. This stage is the
6+
docs-path counterpart to propose-fix — it bypasses the TDD invariant
7+
since docs-only PRs have nothing to spec.
8+
9+
## Rails
10+
11+
Read `.claude/commands/_shared-rails.md` first. Highlights for this command:
12+
13+
- Use `gh` for GitHub state, full `git` for **your branch only** (the
14+
caller workflow has created `docs/bot-<issue>-<slug>` for you and you
15+
are checked out on it).
16+
- **Filesystem writes are scoped to doc paths only**:
17+
`web/sites/guides/**`, `.ai/wheels/**`, `CLAUDE.md`, `CHANGELOG.md`.
18+
**Do NOT modify any file under `vendor/wheels/**`, `app/**`,
19+
`tests/**`, `vendor/wheels/tests/**`, `.github/**`, `cli/**`, or
20+
`config/**`** — this is a docs-only stage. The TDD gate skips this
21+
branch class precisely because docs PRs don't need specs.
22+
- Output is **one draft PR** against `develop` plus one comment on the
23+
issue.
24+
25+
## Args
26+
27+
- `<issue-number>` — the docs-request issue to write docs for
28+
29+
## Steps
30+
31+
1. **Idempotency check.** Read existing comments on the issue via
32+
`gh issue view <issue-number> --json comments`. If any comment
33+
contains `<!-- wheels-bot:write-docs:<issue-number> -->` or
34+
`<!-- wheels-bot:docs-held:<issue-number> -->`, exit silently — docs
35+
have already been written or the safety net already held them.
36+
37+
2. **Read the authoritative context.**
38+
- The triage comment (marker `wheels-bot:triage:<issue-number>`) —
39+
gives you the docs scope and confidence assessment.
40+
- The issue body — original wording on what's needed.
41+
- Any human follow-up comments — they may refine the scope.
42+
43+
If no triage comment exists, exit with a comment explaining no triage
44+
was found.
45+
46+
3. **Read the supporting context.**
47+
- `CLAUDE.md` § "Commit Message Conventions" — type `docs`, allowed
48+
scopes (`docs`, `web/guides`, `web/landing`, `web/blog`, etc.).
49+
- `web/sites/guides/src/content/docs/v4-0-0-snapshot/` — browse the
50+
existing structure to find the right place for the new content.
51+
- One or two existing pages in the same area for style/depth
52+
reference. Match existing tone (no emoji unless the surrounding
53+
pages use them).
54+
55+
4. **Auto-downgrade safety net.** Before writing anything, check whether
56+
the work would:
57+
- Touch more than ~5 files (signals over-broad scope)
58+
- Require creating a new top-level section (vs. extending existing)
59+
- Demand significant code-reading to write accurately (signals the
60+
issue is really `framework-design`, not `docs-request`)
61+
- Need real screenshots that the bot cannot produce (these become
62+
placeholders; see step 6)
63+
64+
If the scope feels too big to write cleanly, **stop**. Post a comment
65+
on the issue:
66+
67+
```
68+
## Wheels Bot — Docs work on hold for human review
69+
70+
The proposed docs scope is larger than this stage handles cleanly
71+
(<one-line reason>). A human should plan the structure before the
72+
bot writes content.
73+
74+
<!-- wheels-bot:docs-held:<issue-number> -->
75+
```
76+
77+
Then exit.
78+
79+
5. **Decide what to write.** Based on the triage scope and the issue body,
80+
pick targets:
81+
82+
- **MDX guide page(s)** under
83+
`web/sites/guides/src/content/docs/v4-0-0-snapshot/`. If the issue
84+
is about a feature or subsystem, look for the right `<area>/`
85+
(e.g. `working-with-wheels/`, `digging-deeper/`,
86+
`command-line-tools/`). Add to an existing page where possible;
87+
create a new page only when no existing page covers the area.
88+
- **`.ai/wheels/<layer>/`** — only if the docs change documents a
89+
pattern or convention an AI agent should know about. Most user-
90+
facing docs do NOT need a corresponding `.ai/` update.
91+
- **`CLAUDE.md`** — only if the change is about a top-level convention
92+
or critical anti-pattern. Most docs PRs do NOT touch `CLAUDE.md`.
93+
94+
6. **Write conservatively.**
95+
- Read the existing page (if extending) before editing.
96+
- Match the existing style: heading depth, code-fence language tags,
97+
prose tone.
98+
- **For features that benefit from screenshots:** insert a placeholder
99+
comment in the MDX where the screenshot belongs:
100+
101+
```mdx
102+
{/* screenshot: short description of what to capture, e.g.
103+
"Debug Panel — Packages tab showing both installed and
104+
available-from-registry tables" */}
105+
```
106+
107+
The bot cannot capture screenshots itself (no headless browser
108+
available in the runner). The PR description (step 8) will list
109+
these placeholders so a human can capture and replace them.
110+
- Add a `CHANGELOG.md` `[Unreleased]` entry. One line, present
111+
tense, no PR number.
112+
113+
7. **Stage and commit.**
114+
115+
Conventional commit. Type `docs`. Scope from the allowlist:
116+
- `web/guides` for changes under `web/sites/guides/src/content/docs/`
117+
- `docs` for `.ai/wheels/` changes
118+
- no scope for `CLAUDE.md` or mixed paths
119+
Subject ≤ 100 chars, sentence-case.
120+
121+
Examples:
122+
- `docs(web/guides): add Debug Panel guide outlining each feature`
123+
- `docs(web/guides): document scope on findOne nested includes`
124+
125+
```bash
126+
git add <files>
127+
git commit -m "<message>"
128+
```
129+
130+
The caller workflow handles the actual `git push` — just commit
131+
cleanly. Do **not** use `--amend` or `--force`.
132+
133+
8. **Open the draft PR.** Use `gh pr create --draft --base develop`. The
134+
PR body must:
135+
136+
- Open with one paragraph naming what was added/changed and why.
137+
- Include `Fixes #<issue-number>`.
138+
- **List screenshot placeholders** if any were inserted. Format:
139+
140+
```markdown
141+
## Screenshots needed
142+
143+
This docs PR includes screenshot-placeholder markers that a human
144+
reviewer can replace with captured images:
145+
146+
- `<file>:line` — `<description from the placeholder>`
147+
- ...
148+
149+
The bot cannot run the app or capture images — these are
150+
intentional gaps for a human follow-up.
151+
```
152+
153+
- End with the marker `<!-- wheels-bot:write-docs:<issue-number> -->`.
154+
155+
9. **Self-check before opening.** Do NOT open the PR if any check fails:
156+
- [ ] No files changed outside doc paths (`web/sites/guides/`,
157+
`.ai/wheels/`, `CLAUDE.md`, `CHANGELOG.md`)
158+
- [ ] At least one doc file changed (don't open empty PRs)
159+
- [ ] Commit message is conventional and ≤ 100 chars
160+
- [ ] PR body includes `Fixes #<issue-number>`
161+
- [ ] PR body includes any screenshot-placeholder list
162+
- [ ] PR is created with `--draft`
163+
- [ ] Marker is present in PR body
164+
165+
If any check fails: do not open the PR. Comment "no docs proposed"
166+
on the issue and exit non-zero.
167+
168+
10. **Comment back on the issue** with a link to the PR:
169+
170+
```
171+
## Wheels Bot — Docs proposed
172+
173+
Draft PR: <link>
174+
175+
Pages updated: <list>
176+
Screenshots needed: <count, or "none">
177+
178+
A human review is required before merge. Reviewer A and Reviewer B
179+
will weigh in shortly.
180+
181+
<!-- wheels-bot:write-docs:<issue-number> -->
182+
```
183+
184+
Then exit.

.github/workflows/bot-tdd-gate.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,31 @@ jobs:
2323
run: |
2424
set -euo pipefail
2525
is_bot=false
26+
is_docs=false
2627
if [[ "$PR_AUTHOR" == "wheels-bot[bot]" ]]; then
2728
is_bot=true
28-
elif [[ "$PR_HEAD" =~ ^bot/ || "$PR_HEAD" =~ ^fix/bot- ]]; then
29+
elif [[ "$PR_HEAD" =~ ^bot/ || "$PR_HEAD" =~ ^fix/bot- || "$PR_HEAD" =~ ^docs/bot- ]]; then
2930
is_bot=true
3031
fi
32+
if [[ "$PR_HEAD" =~ ^docs/bot- ]]; then
33+
is_docs=true
34+
fi
3135
echo "is_bot=$is_bot" >> "$GITHUB_OUTPUT"
36+
echo "is_docs=$is_docs" >> "$GITHUB_OUTPUT"
3237
if [[ "$is_bot" == "false" ]]; then
3338
echo "Human-authored PR — gate is a no-op."
39+
elif [[ "$is_docs" == "true" ]]; then
40+
echo "Docs-only bot PR (docs/bot-* branch) — TDD invariant doesn't apply, gate is a no-op."
3441
fi
3542
3643
- name: Checkout
37-
if: steps.classify.outputs.is_bot == 'true'
44+
if: steps.classify.outputs.is_bot == 'true' && steps.classify.outputs.is_docs == 'false'
3845
uses: actions/checkout@v6
3946
with:
4047
fetch-depth: 0
4148

4249
- name: Verify bot PR contains spec + implementation changes
43-
if: steps.classify.outputs.is_bot == 'true'
50+
if: steps.classify.outputs.is_bot == 'true' && steps.classify.outputs.is_docs == 'false'
4451
env:
4552
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4653
PR_NUMBER: ${{ github.event.pull_request.number }}

.github/workflows/bot-update-docs.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ jobs:
2828
# Auto-fire only for the bot's own PRs. The bot-identity check is
2929
# load-bearing: prevents human PRs from triggering this stage and
3030
# adding bot-authored doc commits to in-flight branches.
31+
# Skips docs/bot-* branches — those are write-docs's own PRs, which
32+
# are docs-only by construction. Running update-docs on them would
33+
# be redundant work.
3134
if: |
3235
vars.WHEELS_BOT_ENABLED == 'true'
3336
&& (
3437
github.event_name == 'workflow_dispatch'
3538
|| (github.event_name == 'pull_request'
36-
&& github.event.pull_request.user.login == 'wheels-bot[bot]')
39+
&& github.event.pull_request.user.login == 'wheels-bot[bot]'
40+
&& !startsWith(github.event.pull_request.head.ref, 'docs/bot-'))
3741
)
3842
env:
3943
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr-number }}

0 commit comments

Comments
 (0)