Password Secure: Release 26.3.100 #119
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Doc PR Review | |
| on: | |
| pull_request: | |
| types: [opened, synchronize] | |
| branches: | |
| - dev | |
| paths: | |
| - 'docs/**/*.md' | |
| - '!docs/**/CLAUDE.md' | |
| - '!docs/**/SKILL.md' | |
| - '!docs/kb/**' | |
| issue_comment: | |
| types: [created] | |
| jobs: | |
| doc-review: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| fetch-depth: 1 | |
| - name: Get changed markdown files | |
| id: changed-files | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| PR_NUMBER=${{ github.event.pull_request.number }} | |
| CHANGED_MD_FILES=$(gh pr diff "$PR_NUMBER" --name-only | grep -E '^docs/.*\.md$' | grep -v '/CLAUDE\.md$' | grep -v '/SKILL\.md$' | grep -v '^docs/kb/' || true) | |
| if [ -z "$CHANGED_MD_FILES" ]; then | |
| echo "No docs markdown files changed" | |
| echo "files=" >> "$GITHUB_OUTPUT" | |
| echo "count=0" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Changed markdown files:" | |
| echo "$CHANGED_MD_FILES" | |
| FILES_LIST=$(echo "$CHANGED_MD_FILES" | tr '\n' ',' | sed 's/,$//') | |
| echo "files=$FILES_LIST" >> "$GITHUB_OUTPUT" | |
| echo "count=$(echo "$CHANGED_MD_FILES" | wc -l | tr -d ' ')" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Delete previous bot comments | |
| if: steps.changed-files.outputs.count > 0 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| PR_NUMBER=${{ github.event.pull_request.number }} | |
| # Delete previous review comments | |
| COMMENT_IDS=$(gh api repos/${{ github.repository }}/issues/${PR_NUMBER}/comments \ | |
| --jq '[.[] | select(.user.login == "github-actions[bot]" and (.body | contains("Documentation PR Review"))) | .id] | .[]' 2>/dev/null || true) | |
| 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 each Dale rule file: | |
| - .claude/skills/dale/rules/minimizing-difficulty.yml | |
| - .claude/skills/dale/rules/negative-assumptions.yml | |
| - .claude/skills/dale/rules/xy-slop.yml | |
| 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"' | |
| - 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 | |
| if: steps.changed-files.outputs.count > 0 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh pr diff ${{ github.event.pull_request.number }} > /tmp/pr-diff.txt 2>&1 || true | |
| echo "Diff saved to /tmp/pr-diff.txt" | |
| wc -l /tmp/pr-diff.txt | |
| - name: Run editorial review | |
| 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 a documentation reviewer. Your ONLY job is to: | |
| 1. Read the PR diff and changed files | |
| 2. Do a brief editorial review | |
| 3. Post a formatted review comment to the PR | |
| CONTEXT: | |
| - 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 linting runs separately (vale-linter workflow) and posts inline review comments plus a summary PR comment. Do not run Vale or include Vale issues in this review. | |
| INSTRUCTIONS: | |
| Step 1: Read /tmp/pr-diff.txt to see what changed. Then read each changed file to understand context. | |
| Step 2: Do a brief editorial review of ONLY added/modified lines, focusing on: | |
| - 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. | |
| Step 3: Write the review to /tmp/doc-pr-review.md with this EXACT structure: | |
| ## Documentation PR Review | |
| ### Editorial Review | |
| (list editorial issues as bullet points: **Category** — Line N: description. Suggested fix: "...") | |
| (if no issues found, write "No editorial issues found.") | |
| ### Summary | |
| N Dale issues (see inline comments), N editorial suggestions across N files. | |
| --- | |
| **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 reorganize the prerequisites section` | |
| - `@claude help improve the flow of this document` | |
| > Automated fixes are only available for branches in this repository, not forks. | |
| Step 4: Post the comment by running this exact command: | |
| gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --body-file /tmp/doc-pr-review.md | |
| IMPORTANT: You MUST complete all 4 steps. Your task is NOT done until step 4 succeeds. | |
| claude_args: '--allowedTools "Bash,Read,Write"' | |
| - name: Verify review was posted | |
| if: steps.changed-files.outputs.count > 0 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| COMMENTS=$(gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments \ | |
| --jq '[.[] | select(.body | contains("Documentation PR Review"))] | length' 2>/dev/null || echo "0") | |
| echo "Review comments found: $COMMENTS" | |
| if [ "$COMMENTS" = "0" ]; then | |
| echo "::warning::No review comment was posted by Claude" | |
| fi | |
| doc-followup: | |
| if: >- | |
| github.event_name == 'issue_comment' && | |
| github.event.issue.pull_request && | |
| contains(github.event.comment.body, '@claude') && | |
| !startsWith(github.event.comment.user.login, 'github-actions') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| id-token: write | |
| steps: | |
| - name: Get PR info | |
| id: pr-info | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| PR_NUMBER="${{ github.event.issue.number }}" | |
| PR_DATA=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json headRefName,baseRefName,isCrossRepository) | |
| BASE_BRANCH=$(echo "$PR_DATA" | jq -r '.baseRefName') | |
| echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT" | |
| echo "branch=$(echo "$PR_DATA" | jq -r '.headRefName')" >> "$GITHUB_OUTPUT" | |
| echo "is_fork=$(echo "$PR_DATA" | jq -r '.isCrossRepository')" >> "$GITHUB_OUTPUT" | |
| # Check target branch using the shell variable to avoid | |
| # re-interpolating the output via expressions (code injection risk). | |
| if [ "$BASE_BRANCH" = "dev" ]; then | |
| echo "targets_dev=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "targets_dev=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Post fork notice | |
| if: steps.pr-info.outputs.is_fork == 'true' && steps.pr-info.outputs.targets_dev == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh pr comment ${{ steps.pr-info.outputs.number }} --repo ${{ github.repository }} \ | |
| --body "This PR is from a fork. Automated fixes cannot be pushed directly. I can still review and suggest changes — apply them manually from the comments." | |
| - name: Checkout repository | |
| if: steps.pr-info.outputs.is_fork == 'false' && steps.pr-info.outputs.targets_dev == 'true' | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ steps.pr-info.outputs.branch }} | |
| fetch-depth: 0 | |
| - name: Handle @claude request | |
| if: steps.pr-info.outputs.is_fork == 'false' && steps.pr-info.outputs.targets_dev == 'true' | |
| uses: anthropics/claude-code-action@v1 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| with: | |
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| show_full_output: true | |
| prompt: | | |
| /doc-pr-fix ${{ steps.pr-info.outputs.number }} $COMMENT_BODY | |
| claude_args: '--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 and Vale 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**")) or (.comments.nodes[0].body | contains("**Vale**")))) | .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 and Vale reviews | |
| REVIEW_IDS=$(gh api repos/${REPO}/pulls/${PR_NUMBER}/reviews \ | |
| --jq '[.[] | select(.user.login == "github-actions[bot]" and ((.body | contains("Dale found")) or (.body | contains("Vale 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 |