Skip to content
21 changes: 8 additions & 13 deletions .claude/skills/doc-pr-fix/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ You receive:

Parse the writer's comment to determine what they want. Common patterns:

- **Fix all issues** — apply every fix from the doc-pr review comment (Dale + editorial)
- **Fix only Dale issues** — apply only Dale linting fixes
- **Fix all issues** — apply every fix from the doc-pr review comment (editorial)
- **Fix a specific issue** — apply one targeted fix
- **Improve flow/clarity/structure** — editorial rewrite of specific content
- **Explain something** — answer a question about a flagged issue (respond in a PR comment, don't edit files)
Expand All @@ -35,28 +34,26 @@ Parse the writer's comment to determine what they want. Common patterns:
```bash
gh api repos/{owner}/{repo}/issues/$PR_NUMBER/comments --jq '.[] | select(.body | contains("Documentation PR Review")) | .body' | tail -1
```
This tells you what Dale and the editorial review flagged.
This tells you what the editorial review flagged.

## Step 3: Plan your work and post a progress comment

Use Todo to create a task for each discrete piece of work you need to do. Build the task list from what you learned in Steps 1–2. Each task should be concrete and trackable. Mark each task as complete as you finish it.

Example tasks for a "fix all issues" request:
- Fix Dale issues in `path/to/file.md` (N issues)
- Apply editorial suggestions
- Apply editorial suggestions in `path/to/file.md`
- Verify changes
- Commit and push

Only include tasks for what the writer actually asked for. If they said "fix only the Dale issues," your task list should contain Dale fixes, verify, and commit — no editorial tasks. The task list must reflect the writer's request exactly.
Only include tasks for what the writer actually asked for. The task list must reflect the writer's request exactly.

Then post a PR comment mirroring your task list so the writer can see what you're doing:

```bash
PROGRESS_COMMENT_ID=$(gh pr comment "$PR_NUMBER" --body "$(cat <<'EOF'
**Fix in progress:**

- [ ] Fix Dale issues in `path/to/file.md` (N issues)
- [ ] Apply editorial suggestions
- [ ] Apply editorial suggestions in `path/to/file.md`
- [ ] Verify changes
- [ ] Commit and push
EOF
Expand All @@ -76,15 +73,14 @@ Update the PR comment at natural milestones (after finishing each file, after co

Work through the requested fixes methodically:

- For **Dale fixes**: fix each flagged issue in order, file by file
- For **editorial fixes from the review**: apply the suggested changes from the review comment
- For **broader editorial requests** ("improve the flow", "make this clearer", "help with structure"): invoke `/doc-help` with the file path and the writer's request. Doc-help will analyze the document using its structured editing framework (structure, clarity, voice, surface). Since this is running in CI without an interactive writer, apply all of doc-help's suggestions autonomously rather than waiting for feedback
- For **explanations**: post a PR comment explaining the issue and how to fix it, then stop — don't edit files

When editing:
- Use the Edit tool for targeted changes, Write for larger rewrites
- Preserve the author's meaning and intent — fix the style, don't rewrite the content
- Only change what was requested; don't fix other categories of issues even if they're on the same line (e.g., if asked to fix Dale issues, don't also fix editorial issues)
- Only change what was requested; don't fix unrelated issues even if they're on the same line

## Step 5: Verify

Expand Down Expand Up @@ -113,8 +109,7 @@ gh api repos/{owner}/{repo}/issues/comments/$PROGRESS_COMMENT_ID \
-X PATCH -f body="$(cat <<'EOF'
**Fix complete:**

- [x] Fix Dale issues in `path/to/file.md` (N issues)
- [x] Apply editorial suggestions
- [x] Apply editorial suggestions in `path/to/file.md`
- [x] Verify changes
- [x] Commit and push

Expand All @@ -130,7 +125,7 @@ Skip progress tracking only for pure explanations (e.g., "why is this flagged?")
## Behavioral Notes

- **Fix what's clear, ask about what isn't.** If a request has both obvious parts and ambiguous parts, apply the obvious fixes, commit and push those, then post a comment that summarizes what you did AND asks clarifying questions about the rest. The writer can reply with another `@claude` comment to continue.
- **Never fix issues the writer didn't ask about.** If they said "fix the Dale issues," only fix Dale issues — don't also fix editorial issues or rewrite sentences for clarity, even if the fix is on the same line.
- **Never fix issues the writer didn't ask about.** Only fix what was requested — don't rewrite sentences for clarity or fix unrelated issues, even if they're on the same line.
- **If a fix would substantially change the author's meaning**, skip it and explain why in your summary comment. Ask the writer how they'd like to handle it.
- **If the entire request is unclear**, don't edit anything — post a comment asking for clarification. It's better to ask one good question than to guess wrong and push unwanted changes.
- **Each `@claude` comment is a fresh invocation.** You won't remember previous runs, so always re-read the PR diff and review comment for context.
42 changes: 11 additions & 31 deletions .claude/skills/doc-pr/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
name: doc-pr
description: "Orchestrate a documentation review for pull requests targeting dev. Runs Dale linting and editorial review on changed markdown files, then posts a structured comment to the PR. Vale issues are auto-fixed separately by the vale-autofix workflow. Use this skill whenever a PR involves markdown files in docs/ and targets the dev branch — triggered automatically by the doc-pr GitHub Actions workflow on PR open, sync, or when invoked manually via /doc-pr."
description: "Orchestrate an editorial review for pull requests targeting dev. Reviews changed markdown files and posts a structured comment to the PR. Vale and Dale issues are auto-fixed separately by the vale-autofix workflow. Use this skill whenever a PR involves markdown files in docs/ and targets the dev branch — triggered automatically by the doc-pr GitHub Actions workflow on PR open, sync, or when invoked manually via /doc-pr."
argument-hint: "[changed-files-csv] [pr-number]"
---

# Doc PR Review

You orchestrate a two-stage documentation review pipeline for pull requests. Your job is to run each stage, collect the results, and post a single comprehensive review comment to the PR.
You orchestrate an editorial review for pull requests. Your job is to review changed files and post a comprehensive review comment to the PR.

Vale issues are auto-fixed separately by the vale-autofix workflow. Do not run Vale or include Vale results in your review.
Vale and Dale issues are auto-fixed separately by the vale-autofix workflow. Do not run Vale, Dale, or include their results in your review.

Read `docs/CLAUDE.md` before starting — it contains the writing standards and Vale guidance you need for the editorial review stage.

Expand All @@ -26,19 +26,9 @@ If the environment variables are empty, check for positional arguments (`$1` = f

Split the comma-separated file list into individual file paths for processing.

**Exclude KB files:** Skip any file under `docs/kb/`. KB articles have their own style conventions and are not subject to Dale linting or editorial review. If the file list contains only KB files, post a comment noting that no reviewable files were found and exit.
**Exclude KB files:** Skip any file under `docs/kb/`. KB articles have their own style conventions and are not subject to editorial review. If the file list contains only KB files, post a comment noting that no reviewable files were found and exit.

## Stage 1: Dale Linting

For each changed file, invoke the Dale linter skill:

```
/dale <file>
```

Dale returns a table of rule violations or a clean report. Collect all Dale output.

## Stage 2: Editorial Review
## Editorial Review

This stage applies the doc-help editing analysis to the PR changes — but non-interactively. You are producing a written review, not having a conversation.

Expand All @@ -52,7 +42,7 @@ This stage applies the doc-help editing analysis to the PR changes — but non-i

**Voice** — Passive constructions, first person, impersonal phrases, condescending language? Does it follow Netwrix style (active voice, present tense, second person for procedures)?

**Surface** — Word choice, wordiness, redundancy? Anything Vale or Dale might miss?
**Surface** — Word choice, wordiness, redundancy? Anything the auto-fix linters might miss?

For each issue found, note:
- The file path and line number
Expand All @@ -68,7 +58,7 @@ Only report issues on lines that were added or modified in this PR. Do not flag

## Output — MANDATORY: Post as PR Comment

After completing both stages, you MUST write the review to a file and post it as a PR comment. This is the most important step — the review is useless if it is not posted.
After completing the review, you MUST write the results to a file and post it as a PR comment. This is the most important step — the review is useless if it is not posted.

**Step 1: Write the review to a temporary file.**

Expand All @@ -77,16 +67,6 @@ Write the full review body to `/tmp/doc-pr-review.md` using the Write tool. Foll
```markdown
## Documentation PR Review

### Dale Linting

**path/to/file.md**

| Line | Rule | Message | Offending Text |
|------|------|---------|----------------|
| N | `rule-name` | description of the issue | `offending text` |

(Repeat for each file. Write "No issues found." if clean.)

### Editorial Review

**path/to/file.md**
Expand All @@ -99,15 +79,15 @@ Write the full review body to `/tmp/doc-pr-review.md` using the Write tool. Foll

### Summary

N Dale issues, N editorial suggestions across N files. Vale issues are auto-fixed separately.
N editorial suggestions across N files. Vale and Dale issues are auto-fixed separately.

---

**What to do next:**

Comment `@claude` on this PR followed by your instructions to get help:

- `@claude fix all issues` — fix all Dale and editorial issues
- `@claude fix all issues` — fix all editorial issues
- `@claude help improve the flow of this document` — get writing assistance
- `@claude explain the voice issues` — understand why something was flagged

Expand All @@ -127,6 +107,6 @@ If the `gh pr comment` command fails, report the error. Do NOT end your turn wit
## Behavioral Notes

- Be thorough but not pedantic — focus on issues that genuinely affect reader comprehension or violate Netwrix standards
- Do not flag issues that Vale or Dale already catch — focus on what linters miss
- If a file has zero issues across both stages, still list it with "No issues found." so the reviewer knows it was checked
- Do not flag issues that Vale or Dale already catch (both auto-fixed separately) — focus on what linters miss
- If a file has zero editorial issues, still list it with "No issues found." so the reviewer knows it was checked
- Never modify the files — this is a read-only review
120 changes: 5 additions & 115 deletions .github/workflows/claude-doc-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,77 +65,6 @@ jobs:
for ID in $COMMENT_IDS; do
gh api repos/${{ github.repository }}/issues/comments/${ID} -X DELETE 2>/dev/null || true
done
# Dismiss previous bot reviews (Dale) so they don't pile up
REVIEW_IDS=$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER}/reviews \
--jq '[.[] | select(.user.login == "github-actions[bot]" and .state == "COMMENTED" and (.body | contains("Dale found"))) | .id] | .[]' 2>/dev/null || true)
for ID in $REVIEW_IDS; do
gh api repos/${{ github.repository }}/pulls/${PR_NUMBER}/reviews/${ID}/dismissals \
-f message="Superseded by new review" -f event="DISMISS" 2>/dev/null || true
done

- name: Run Dale linting
id: dale
if: steps.changed-files.outputs.count > 0
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
show_full_output: true
prompt: |
You are Dale, a documentation linter. Your ONLY job is to check files against Dale rules and write results to a JSON file.

CHANGED FILES: ${{ steps.changed-files.outputs.files }}

INSTRUCTIONS:

Step 1: Read every .yml rule file in .claude/skills/dale/rules/

Step 2: Read each changed file listed above (split on commas).

Step 3: For each file, check every line against each rule's "reason" field. When a line triggers a rule, record it.

Step 4: Write results to /tmp/dale-results.json as a JSON array. Each entry must have:
- "path": the file path exactly as given above
- "line": the line number (integer)
- "rule": the rule filename without extension (e.g. "minimizing-difficulty")
- "message": the rule's "message" field value

If no issues found, write an empty array: []

Example output:
[{"path":"docs/foo/bar.md","line":7,"rule":"minimizing-difficulty","message":"Do not minimize the difficulty of tasks users are performing."}]

IMPORTANT: Write ONLY the JSON file. Do not post comments, do not run any other tools. Your task is done when /tmp/dale-results.json exists.
claude_args: '--allowedTools "Read,Write,Glob"'

- name: Post Dale inline comments
id: dale-post
if: steps.changed-files.outputs.count > 0
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
DALE_COUNT=0
if [ -f /tmp/dale-results.json ]; then
DALE_COUNT=$(jq 'length' /tmp/dale-results.json 2>/dev/null || echo "0")
fi

echo "dale_count=$DALE_COUNT" >> "$GITHUB_OUTPUT"

if [ "$DALE_COUNT" -gt 0 ]; then
echo "Posting $DALE_COUNT Dale inline comments"
# Transform Dale results into PR review comment format
COMMENTS_JSON=$(jq '[.[] | {"path": .path, "line": .line, "body": ("**Dale** (`" + .rule + "`): " + .message)}]' /tmp/dale-results.json)
jq -n \
--arg body "**Dale found ${DALE_COUNT} issue(s).** See inline comments below." \
--argjson comments "$COMMENTS_JSON" \
'{"body": $body, "event": "COMMENT", "comments": $comments}' \
| gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews \
--input - 2>&1
else
echo "No Dale issues found"
fi

- name: Get PR diff
id: diff
Expand Down Expand Up @@ -166,10 +95,9 @@ jobs:
- Repository: ${{ github.repository }}
- PR number: ${{ github.event.pull_request.number }}
- Changed files: ${{ steps.changed-files.outputs.files }}
- Dale issues: ${{ steps.dale-post.outputs.dale_count }} (already posted as inline comments)
- PR diff is at: /tmp/pr-diff.txt

NOTE: Vale issues are auto-fixed separately by the vale-autofix workflow. Do not run Vale or include Vale issues in this review.
NOTE: Vale and Dale issues are auto-fixed separately by the vale-autofix workflow. Do not run Vale, Dale, or include their issues in this review.

INSTRUCTIONS:

Expand All @@ -179,7 +107,7 @@ jobs:
- Voice: passive voice, first person, impersonal phrases
- Clarity: hard-to-parse sentences, ambiguous references
- Surface: wordiness, redundancy
Do NOT duplicate issues already caught by Dale — focus on what linters miss.
Do NOT duplicate issues already caught by Vale or Dale (both auto-fixed separately) — focus on what linters miss.

Step 3: Write the review to /tmp/doc-pr-review.md with this EXACT structure:

Expand All @@ -190,13 +118,12 @@ jobs:
(if no issues found, write "No editorial issues found.")

### Summary
N Dale issues (see inline comments), N editorial suggestions across N files.
N editorial suggestions across N files. Vale and Dale issues are auto-fixed separately.

---
**What to do next:**
Comment `@claude` on this PR followed by your instructions. For example:
- `@claude fix all issues`
- `@claude fix only the Dale issues`
- `@claude fix all issues` — fix all editorial issues
- `@claude reorganize the prerequisites section`
- `@claude help improve the flow of this document`
> Automated fixes are only available for branches in this repository, not forks.
Expand Down Expand Up @@ -278,41 +205,4 @@ jobs:
show_full_output: true
prompt: |
/doc-pr-fix ${{ steps.pr-info.outputs.number }} $COMMENT_BODY
claude_args: '--max-turns 50 --allowedTools "Bash(gh:*),Bash(git:*),Read,Write,Edit,Glob,Grep,Skill(doc-pr-fix),Skill(dale),Skill(doc-help)"'

- name: Resolve inline comments and dismiss old reviews
if: steps.pr-info.outputs.is_fork == 'false' && steps.pr-info.outputs.targets_dev == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER=${{ steps.pr-info.outputs.number }}
REPO=${{ github.repository }}
OWNER="${REPO%%/*}"
NAME="${REPO##*/}"

# Resolve all Dale inline comment threads
THREAD_IDS=$(gh api graphql -f query='
query($owner:String!,$name:String!,$pr:Int!) {
repository(owner:$owner,name:$name) {
pullRequest(number:$pr) {
reviewThreads(first:100) {
nodes { id isResolved comments(first:1) { nodes { body } } }
}
}
}
}' -f owner="$OWNER" -f name="$NAME" -F pr="$PR_NUMBER" \
--jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and (.comments.nodes[0].body | contains("**Dale**"))) | .id' 2>/dev/null || true)
for TID in $THREAD_IDS; do
gh api graphql -f query='
mutation($tid:ID!) {
resolveReviewThread(input:{threadId:$tid}) { thread { isResolved } }
}' -f tid="$TID" 2>/dev/null || true
done

# Dismiss all previous Dale reviews
REVIEW_IDS=$(gh api repos/${REPO}/pulls/${PR_NUMBER}/reviews \
--jq '[.[] | select(.user.login == "github-actions[bot]" and (.body | contains("Dale found"))) | .id] | .[]' 2>/dev/null || true)
for ID in $REVIEW_IDS; do
gh api repos/${REPO}/pulls/${PR_NUMBER}/reviews/${ID}/dismissals \
-f message="Superseded after fixes applied" -f event="DISMISS" 2>/dev/null || true
done
claude_args: '--max-turns 50 --allowedTools "Bash(gh:*),Bash(git:*),Read,Write,Edit,Glob,Grep,Skill(doc-pr-fix),Skill(doc-help)"'
Loading
Loading