From 406985b2e6ca5bd2c5bdee4995f297b3f147751e Mon Sep 17 00:00:00 2001 From: james-haytko_nwx Date: Fri, 27 Feb 2026 17:30:42 -0600 Subject: [PATCH 1/2] Fix preexisting issues comment and normalize bold issue format Fixer: record last comment ID before detection action, then PATCH the action's auto-posted summary comment with the formatted output instead of adding a second comment. Reviewer: extend normalizer to also handle bold-header + sub-bullet format (collapse **Line N: title** + sub-bullets to a single line). Generated with AI Co-Authored-By: Claude Code --- .../workflows/claude-documentation-fixer.yml | 38 ++++++++++++++++-- .../claude-documentation-reviewer.yml | 39 +++++++++++++++---- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/.github/workflows/claude-documentation-fixer.yml b/.github/workflows/claude-documentation-fixer.yml index f266103316..77be815a6b 100644 --- a/.github/workflows/claude-documentation-fixer.yml +++ b/.github/workflows/claude-documentation-fixer.yml @@ -97,6 +97,16 @@ jobs: --allowedTools "Read,Write,Edit,Bash(vale:*),Bash(gh pr view:*),Bash(gh pr diff:*),Bash(git config:*),Bash(git add:*),Bash(git commit:*),Bash(git push:*),Bash(git status:*),Bash(git diff:*)" --append-system-prompt "${{ steps.read-prompt.outputs.prompt }}" + - name: Record last comment ID + id: pre-claude + if: steps.pr-info.outputs.is_fork == 'false' && steps.cmd-type.outputs.is_preexisting == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + LAST_ID=$(gh api repos/${{ github.repository }}/issues/${{ steps.pr-info.outputs.number }}/comments \ + --jq 'if length > 0 then .[-1].id else 0 end' 2>/dev/null || echo "0") + echo "last_comment_id=$LAST_ID" >> "$GITHUB_OUTPUT" + - name: Detect preexisting issues if: steps.pr-info.outputs.is_fork == 'false' && steps.cmd-type.outputs.is_preexisting == 'true' uses: anthropics/claude-code-action@v1 @@ -115,6 +125,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ steps.pr-info.outputs.number }} REPO: ${{ github.repository }} + LAST_COMMENT_ID: ${{ steps.pre-claude.outputs.last_comment_id }} run: | python3 << 'PYTHON_EOF' import os @@ -123,6 +134,7 @@ jobs: pr_number = os.environ['PR_NUMBER'] repo = os.environ['REPO'] + last_comment_id = int(os.environ.get('LAST_COMMENT_ID', '0')) FOOTER = ( "\n\n---\n\n" @@ -157,8 +169,28 @@ jobs: else: clean_body = '## Preexisting issues\nNone.' + FOOTER - subprocess.run( - ['gh', 'pr', 'comment', pr_number, '--repo', repo, '--body', clean_body], - check=True, + # Find the action's auto-posted comment (ID > last recorded, posted by a bot) + result = subprocess.run( + ['gh', 'api', f'repos/{repo}/issues/{pr_number}/comments'], + capture_output=True, text=True, check=True, ) + comments = json.loads(result.stdout) + new_bot_comments = [c for c in comments + if c['id'] > last_comment_id + and c['user']['login'].endswith('[bot]')] + + if new_bot_comments: + # Replace the action's auto-comment in-place with our formatted output + target_id = new_bot_comments[-1]['id'] + subprocess.run( + ['gh', 'api', f'repos/{repo}/issues/comments/{target_id}', + '-X', 'PATCH', '--input', '-'], + input=json.dumps({'body': clean_body}), + capture_output=True, text=True, check=True, + ) + else: + subprocess.run( + ['gh', 'pr', 'comment', pr_number, '--repo', repo, '--body', clean_body], + check=True, + ) PYTHON_EOF diff --git a/.github/workflows/claude-documentation-reviewer.yml b/.github/workflows/claude-documentation-reviewer.yml index 308fe14e4a..7de88b35ba 100644 --- a/.github/workflows/claude-documentation-reviewer.yml +++ b/.github/workflows/claude-documentation-reviewer.yml @@ -251,16 +251,39 @@ jobs: return valid def normalize_review_body(body): - """Convert any heading-formatted issue lines to bullet points.""" - lines = [] - for line in body.split('\n'): - # Convert "### Line N: ..." or "#### Line N: ..." etc. to "- Line N: ..." + """Normalize issue formatting to single-line bullet points.""" + src = body.split('\n') + result = [] + i = 0 + while i < len(src): + line = src[i] + + # Convert heading format: ### Line N: ... → - Line N: ... m = re.match(r'^#{1,6}\s+(Line \d+:.+)$', line) if m: - lines.append(f'- {m.group(1)}') - else: - lines.append(line) - return '\n'.join(lines) + result.append(f'- {m.group(1)}') + i += 1 + continue + + # Convert bold format: **Line N: title** + sub-bullets → - Line N: single line + m = re.match(r'^\*\*(Line \d+:.*?)\*\*\s*$', line) + if m: + title = m.group(1).rstrip('.') + i += 1 + parts = [] + while i < len(src) and re.match(r'^\s*[-*]\s+', src[i]): + sub = re.sub(r'^\s*[-*]\s+', '', src[i]) + sub = re.sub(r'^(Issue|Fix|Description|Suggested change):\s*', '', sub, flags=re.IGNORECASE) + if sub.strip(): + parts.append(sub.strip().rstrip('.')) + i += 1 + combined = f'- {title}. {". ".join(parts)}.' if parts else f'- {title}.' + result.append(combined) + continue + + result.append(line) + i += 1 + return '\n'.join(result) # Read the review summary Claude wrote summary_path = '/tmp/review-summary.md' From 3d2f570e31d12848a8bf02eee9845c5f523381d0 Mon Sep 17 00:00:00 2001 From: james-haytko_nwx Date: Fri, 27 Feb 2026 17:34:35 -0600 Subject: [PATCH 2/2] Add explicit prompt to preexisting issues detection step Without a prompt: parameter, Claude responds conversationally and ignores the instruction to write to /tmp/preexisting-issues.md. Adding an explicit prompt mirrors how the reviewer works and ensures Claude writes results to the file for post-processing. Generated with AI Co-Authored-By: Claude Code --- .../workflows/claude-documentation-fixer.yml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/claude-documentation-fixer.yml b/.github/workflows/claude-documentation-fixer.yml index 77be815a6b..3bd497fa53 100644 --- a/.github/workflows/claude-documentation-fixer.yml +++ b/.github/workflows/claude-documentation-fixer.yml @@ -114,6 +114,27 @@ jobs: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} show_full_output: false + prompt: | + Detect preexisting issues in PR ${{ steps.pr-info.outputs.number }}. + + Follow these steps in order: + + 1. Use `gh pr diff ${{ steps.pr-info.outputs.number }}` to identify which lines are part of the PR diff. + + 2. Use `gh pr view ${{ steps.pr-info.outputs.number }}` to get the list of changed files, then read the full content of each changed markdown file. + + 3. Run all three review passes on each file per your instructions, but report ONLY issues on lines that are NOT part of the PR diff. + + 4. You MUST write your results ONLY to `/tmp/preexisting-issues.md` — always, even if there are no issues. Do not post a comment. Do not write anything else. Use this exact format: + + ## Preexisting issues + ### path/to/file.md + - Line N: issue. Suggested change: '...' + + Or if there are no issues: + + ## Preexisting issues + None. claude_args: | --model claude-sonnet-4-5-20250929 --allowedTools "Read,Write,Edit,Bash(gh pr view:*),Bash(gh pr diff:*)"