Skip to content

Commit 95ecd19

Browse files
authored
docs(workflow): require PRs to mirror closing issue triage labels, closes #237 (#238)
Add project-specific guidance to CLAUDE.md for the post-push CI watcher (mandatory for all commits on CUDly PRs due to 9-15 min pre-commit job runtime) and label-mirroring rule (PRs must carry the same priority/severity/ urgency/impact/effort/type labels as their closing issues). Resolves conflict by including main's "Known Issues" section alongside the new sections added by this PR.
1 parent 0479b99 commit 95ecd19

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

‎CLAUDE.md‎

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,143 @@ Do this in the same PR that closes the issue. A full sweep of the directory
8686
should happen at the start of each sprint. Full convention and entry format
8787
are in `CONTRIBUTING.md` under "Known Issues Sweep".
8888

89+
## Post-push CI watcher (MANDATORY — even for one-line fix commits)
90+
91+
After **every** `git push` that publishes new commits to a PR branch on
92+
this repo (including follow-up CodeRabbit-nitpick fixes), launch a
93+
background watcher per workflow run **before** ending the turn. This is
94+
the project-level reinforcement of the global rule in
95+
`~/.claude/git-workflow.md` §Post-push CI watcher — CUDly's pre-commit
96+
job alone takes 9–15 min and is silently broken by routine changes
97+
(missing CI tools, pre-existing markdownlint debt, git-secrets
98+
allowlist drift), so leaving a push unwatched routinely lets CI
99+
failures sit overnight.
100+
101+
Mechanics:
102+
103+
1. After `git push`, list the runs the push triggered:
104+
105+
```bash
106+
gh run list --repo LeanerCloud/CUDly --commit "$(git rev-parse HEAD)" \
107+
--limit 10 --json databaseId,workflowName,status
108+
```
109+
110+
2. For each run still in `queued`/`in_progress`, launch one background
111+
watcher script via `Bash` with `run_in_background: true` (do NOT use
112+
foreground polling — the main session must stay unblocked). The
113+
minimum viable watcher polls `gh run view <ID> --json status,conclusion`
114+
every 30s and on completion either reports success or dumps the failed
115+
step digest. A reusable template lives at
116+
`.claude/scripts/watch-ci-run.sh` if present, or write a one-shot to
117+
`/tmp/claude/watch-<sha>-<workflow>.sh`.
118+
119+
3. If a watcher reports failure, the same session investigates and
120+
pushes a fix on the same branch (which fires a fresh watcher round).
121+
Decisions that need a human go to PR comments; everything else is
122+
autonomous per the global rule.
123+
124+
Forgetting this rule has been a recurring failure mode in this
125+
project. Before declaring a "pushed and done" turn complete, confirm
126+
at least one `ci-watch-*` background task is armed.
127+
128+
## CodeRabbit loop — iterate to silence (MANDATORY)
129+
130+
CodeRabbit reviews this repo on every push to a PR branch. The full
131+
rules live in `~/.claude/git-workflow.md` §"Post-PR review loop"
132+
(§§3, 3a) — read them. The minimum-viable loop for this project:
133+
134+
1. After every push, ping `@coderabbitai review` on the PR (CR doesn't
135+
always re-review automatically; the explicit ping makes it
136+
deterministic).
137+
2. Wait for the review (60–120s polling, soft-handle 429s).
138+
3. Triage every Actionable / Outside-diff / Nitpick finding into:
139+
actionable-fix-now, dismiss-with-justification-on-thread, or
140+
genuine-nitpick-batch-into-one-fix-commit.
141+
4. Push the fix(es), comment on the PR summarising what was addressed
142+
vs. dismissed (and why), end the comment with a fresh
143+
`@coderabbitai review` ping.
144+
5. **Repeat until CR's most recent review has zero Actionable items
145+
AND every Nitpick is either fixed or has a justification reply.**
146+
"I fixed pass 1" is not loop-exit. CUDly PRs commonly run 3–6 CR
147+
passes before settling.
148+
6. If a fix push triggers conflicts (`mergeStateStatus: DIRTY`), resolve
149+
them per `~/.claude/git-workflow.md` §3a — `git rebase
150+
origin/<base>`, atomic conflict resolution, `git push
151+
--force-with-lease`, post a rebase note on the PR, then continue
152+
the CR loop.
153+
154+
Forgetting this rule leaves CR threads silently unresolved and pushes
155+
the triage burden onto the human reviewer.
156+
157+
**When delegating PR work to a subagent**: the prompt MUST include the
158+
full CR loop, not stop at the first `@coderabbitai review` ping. A fork
159+
that pushes the PR, pings CR, then exits leaves the CR threads
160+
unresolved — same failure mode as forgetting the post-push CI watcher.
161+
The subagent's exit criteria must be: "CR's most recent review has zero
162+
Actionable items AND every Nitpick is either fixed or has a
163+
justification reply on the thread", not "PR opened and CR pinged". When
164+
in doubt, copy the iteration loop above (steps 2–6) into the fork
165+
prompt verbatim.
166+
167+
## PR labeling — mirror closing-issue labels (MANDATORY)
168+
169+
Every PR opened in this repo must carry the **same** triage labels as
170+
the issue it closes — `priority/*`, `severity/*`, `urgency/*`,
171+
`impact/*`, `effort/*`, `type/*`, plus `triaged` if the issue carries
172+
it. Skipping this leaves PRs invisible to the same priority queries
173+
that surface the issues, so an unlabeled PR is effectively
174+
unreviewable in priority order.
175+
176+
Mechanics — fold into the **same `gh pr create` round**, before pinging
177+
CodeRabbit:
178+
179+
```bash
180+
# Right after `gh pr create ...` returns the PR URL:
181+
# Derive PR_NUM from the current branch context (avoids brittle hand-copying).
182+
PR_NUM=$(gh pr view "$(git rev-parse --abbrev-ref HEAD)" --repo LeanerCloud/CUDly --json number --jq '.number')
183+
ISSUE_NUM=<the issue this PR closes>
184+
185+
LABELS=$(gh issue view "$ISSUE_NUM" --repo LeanerCloud/CUDly --json labels \
186+
--jq '[.labels[].name | select(test("^(priority|severity|urgency|impact|effort|type)/")) ]
187+
+ (if [.labels[].name] | any(. == "triaged") then ["triaged"] else [] end)
188+
| join(",")')
189+
190+
# Guard against empty $LABELS — gh pr edit --add-label "" fails, which would
191+
# silently break this MANDATORY flow. If the closing issue has no triage
192+
# labels in the selected classes, surface the gap deterministically instead.
193+
if [ -n "$LABELS" ]; then
194+
gh pr edit "$PR_NUM" --repo LeanerCloud/CUDly --add-label "$LABELS"
195+
else
196+
echo "WARN: issue #$ISSUE_NUM has no priority/severity/urgency/impact/effort/type labels"
197+
echo " Triage the issue first, then re-run the label-mirror step."
198+
echo " Surface this gap in the PR body or as a comment on issue #$ISSUE_NUM."
199+
fi
200+
201+
# Verify
202+
gh pr view "$PR_NUM" --repo LeanerCloud/CUDly --json labels \
203+
--jq '[.labels[].name] | sort | join(",")'
204+
```
205+
206+
If the closing issue lacks the `triaged` label, do NOT apply
207+
`triaged` to the PR — that would lie. Surface the gap in the PR body
208+
(or as a comment on the issue) so the human can triage.
209+
210+
If a label doesn't yet exist in the repo (rare — the label set is
211+
populated from the existing issue queue), `gh label create` it with
212+
the same color/description as a sibling label BEFORE applying.
213+
214+
For PRs that close more than one issue (e.g., a PR that closes
215+
`#A` and `#B`): take the **highest** `priority/*` and `severity/*`
216+
across the issues; `union` the rest (`type/*`, `effort/*`, `impact/*`,
217+
etc.). The PR represents the work for both, so it should be
218+
discoverable under either filter.
219+
220+
Forgetting this rule has the same shape as forgetting the post-push
221+
CI watcher: it silently breaks priority-ordered review and triage.
222+
Before declaring a "PR opened and done" turn complete, confirm the
223+
label set on the PR matches the closing issue's set (or the merged
224+
set for multi-close PRs).
225+
89226
## Security Rules
90227

91228
- NEVER hardcode API keys, secrets, or credentials in source files

0 commit comments

Comments
 (0)