refactor: replace /plan workflow with /triage for issue analysis and planning#816
refactor: replace /plan workflow with /triage for issue analysis and planning#816
Conversation
There was a problem hiding this comment.
Adversarial Review
Critical / High
None found.
Medium
-
Missing pagination in
gh apicalls — silent data loss on issues with 30+ comments (issue-triage.yml:41andissue-triage.yml:60)Both
gh apicalls to fetch issue comments lack the--paginateflag. GitHub's REST API returns 30 items per page by default. On an issue with more than 30 comments:find-triage(line 41): If the existing triage comment is beyond page 1, it won't be found. The step will outputfound=false, and Claude will create a duplicate triage comment instead of updating the existing one. Each subsequent/triagewill create yet another duplicate, since the search only ever sees the first 30 comments.Collect issue comment thread(line 60): Maintainer feedback left after the 30th comment will be silently dropped from the context passed to Claude. The whole point of the re-triage feature is to incorporate feedback — this defeats it on active issues.
Fix: Add
--paginateto bothgh apicalls:gh api --paginate "/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments" \ --jq '...'
-
Heredoc delimiter collision in
steps.comments.outputs.thread(issue-triage.yml:63-67)The output is written to
$GITHUB_OUTPUTusing anEOFheredoc delimiter:echo "thread<<EOF" echo "$COMMENTS" echo "EOF"
While
$COMMENTSis JSON from jq (so newlines inside string values are\n-escaped), jq's formatted output itself contains literal newlines between JSON elements. If any comment body contains a JSON string value that, when formatted by jq, produces a line consisting of exactlyEOF, the heredoc terminates early and$GITHUB_OUTPUTgets corrupted — subsequent steps would read a truncated or garbled thread.This is unlikely in practice (the string would need to be literally
EOFas its own JSON value on its own line), but GitHub Actions recommends using a random delimiter for untrusted content.Fix: Use a unique delimiter:
DELIMITER=$(openssl rand -hex 16) { echo "thread<<$DELIMITER" echo "$COMMENTS" echo "$DELIMITER" } >> "$GITHUB_OUTPUT"
Low
-
Comment filter doesn't catch all
/triagevariants (issue-triage.yml:61)The jq filter excludes comments where
.body == "/triage"(exact match). If someone posts a comment with trailing whitespace like"/triage "or"/triage\n", it won't be excluded and will appear in the thread passed to Claude. This is cosmetic — Claude will just see an extra/triagecommand in context — but it adds noise. Not worth blocking over since the workflow trigger itself also uses exact match. -
The marker
<!-- IMPLEMENTATION-PLAN -->is shared with the now-deleted planner workflow (issue-triage.yml:42)Existing issues that had plan comments from the old
issue-planner.ymlworkflow use the same<!-- IMPLEMENTATION-PLAN -->marker. Running/triageon those issues will find and overwrite the old plan comment. This is likely intentional migration behavior, but worth being aware of — any old plans will be replaced with triage output on first/triagerun.
Verdict
PASS — The code is structurally sound and the consolidation from two workflows to one is a clear improvement. The missing --paginate flag (Medium #1) is the most actionable finding and would be worth fixing before or shortly after merge, but it only manifests on issues with 30+ comments, which is uncommon enough that it doesn't block.
There was a problem hiding this comment.
Blocking Issues
- README.md references removed
/planand/plan-updatecommands — Lines 165-167 ofREADME.mdstill describe the/plan+/plan-updateworkflow that this PR removes. After merge, contributors reading the README would try commands that no longer exist. Please update the README to reference/triageinstead.
Suggestions (non-blocking)
-
Heredoc EOF delimiter safety (
.github/workflows/issue-triage.ymlline 64) — TheEOFdelimiter used in the "Collect issue comment thread" step could break if any issue comment body contains a literalEOFon its own line. Consider using a more unique delimiter likeSWAMP_COMMENTS_EOF. -
Loss of auto-triage for external issues — The old
issue-triage.ymlworkflow automatically labeled non-maintainer issues withneeds-triageand posted a welcome comment on issue open. This behavior is now completely removed (replaced with the manual/triagecommand). If this is intentional, no action needed — just flagging in case it was overlooked. -
Model version — The workflow uses
claude-opus-4-5-20251101. You may want to update to a newer model ID in a follow-up.
Overall
The refactoring from split /plan + /plan-update into a single idempotent /triage command is a solid improvement — cleaner, less duplication, and the bug triage phase is a nice addition. The prompt is well-structured with clear phases. The only blocking issue is the stale README documentation.
…planning ## Summary - Replaces the `issue-planner.yml` workflow (`/plan` + `/plan-update` commands) with a single `issue-triage.yml` workflow driven by `/triage` - Adds a bug triage phase: Claude now traces through the codebase to confirm/deny bugs and identify root cause before producing an implementation plan - Makes `/triage` idempotent — re-running it reads the full comment thread for operator feedback and updates the existing triage in-place, eliminating the need for a separate `/plan-update` command - Removes duplicated code style rules from the prompt (CLAUDE.md is the source of truth) and adds missing conventions: license headers, libswamp import rule, dual output modes, design docs, integration tests ## Test plan - [ ] Comment `/triage` on a bug report issue and verify it produces a triage analysis with root cause identification before the implementation plan - [ ] Comment `/triage` on a feature request issue and verify it classifies correctly and produces a plan - [ ] Leave feedback as a regular comment, then comment `/triage` again — verify it picks up the feedback and updates the existing triage comment with a revision history entry - [ ] Verify the triage comment starts with `<!-- IMPLEMENTATION-PLAN -->` marker
There was a problem hiding this comment.
Review: Approve
This PR cleanly consolidates two workflows (/plan + /plan-update) into a single idempotent /triage command and adds a bug-verification phase. The workflow is well-structured with clear phases, proper author_association gating, and good idempotency via the <!-- IMPLEMENTATION-PLAN --> marker.
No blocking issues found.
Suggestions (non-blocking)
-
Lost auto-triage for external contributors: The old
issue-triage.ymlautomatically added aneeds-triagelabel and posted a welcome comment when non-maintainers opened issues. This PR removes that behavior entirely. If that was intentional, great — just worth confirming. If not, consider adding it back as a separate job or workflow. -
Comment thread filtering edge case: The
Collect issue comment threadstep filters out comments matching. == "/triage"(exact match). If someone posts/triagewith trailing whitespace or extra text, those comments would leak into the thread context. This is low-risk since the trigger itself requires an exact match, so non-exact/triagecomments are unlikely to appear. -
GitHub API pagination: The
gh apicalls to fetch comments don't handle pagination. For issues with 100+ comments, this could silently drop older comments. Low-risk for now but worth noting if triage is used on long-lived issues.
🤖 Generated with Claude Code
There was a problem hiding this comment.
Adversarial Review
Medium
-
Missing
--paginateongh apicomment fetches —.github/workflows/issue-triage.yml:41-42and:60-61Both
gh apicalls that fetch issue comments lack--paginate. GitHub's REST API returns 30 items per page by default. On issues with 30+ comments:find-triage(line 41-42): If the triage comment is beyond the first page,TRIAGE_COMMENT_IDwill be empty,foundwill befalse, and Claude will create a duplicate triage comment instead of updating the existing one.comments(line 60-61): Maintainer feedback beyond page 1 is silently dropped, so Claude re-triages without the full context.
Breaking example: Issue has 40 comments. Maintainer left detailed feedback at comment #35. The first
/triagecomment is at position #32. Re-running/triagefinds neither — creates a duplicate plan and ignores the feedback.Fix: Add
--paginateto bothgh apicalls. For the jq infind-triage,--paginatewith--jqworks correctly withgh api. For thecommentsstep, add--paginate --slurpand adjust the jq to flatten:[.[][] | select(...)]. -
No concurrency control —
.github/workflows/issue-triage.yml(top-level)If a maintainer comments
/triagetwice in quick succession (e.g., accidental double-post, or two maintainers independently), two workflow runs execute concurrently. Both find no existing triage comment → both create new ones → issue gets duplicate triage comments.Fix: Add a
concurrencygroup keyed on the issue number:concurrency: group: triage-${{ github.event.issue.number }} cancel-in-progress: true
Low
-
Heredoc delimiter collision in
commentsstep —.github/workflows/issue-triage.yml:64-67The multiline output uses
EOFas the heredoc delimiter. In theory, ifjqoutput contained a bareEOFon its own line,GITHUB_OUTPUTwould be corrupted. In practice this is safe becausejqJSON-encodes string values (newlines become\nliterals), so a comment body containing "EOF" won't produce a bareEOFline in the output. Noting for awareness only. -
Removal of auto-welcome for external contributors — The old workflow auto-labeled
needs-triageand posted a welcome comment for non-maintainer issues on open. The new workflow does nothing until a maintainer manually runs/triage. External contributors will see no automated acknowledgment. This appears intentional given the PR description, but worth confirming it's the desired UX.
Verdict
PASS — No critical or high severity issues. The pagination gap (Medium #1) is the most actionable finding and would cause real problems on active issues, but it's in a CI workflow with limited blast radius, not production code. The concurrency issue (Medium #2) is a standard GitHub Actions hardening concern. Both are worth fixing but do not block merge.
Summary
issue-planner.ymlworkflow (/plan+/plan-updatecommands) with a singleissue-triage.ymlworkflow driven by/triage/triageidempotent — re-running it reads the full comment thread for operator feedback and updates the existing triage in-place, eliminating the need for a separate/plan-updatecommandTest plan
/triageon a bug report issue and verify it produces a triage analysis with root cause identification before the implementation plan/triageon a feature request issue and verify it classifies correctly and produces a plan/triageagain — verify it picks up the feedback and updates the existing triage comment with a revision history entry<!-- IMPLEMENTATION-PLAN -->marker