Skip to content

Commit 8dce81c

Browse files
GiggleLiuclaude
andcommitted
Harden meta-power skill against fragile patterns
- Replace fixed sleep with CI poll loop (30s intervals, 15min max) - Use gh pr merge --auto to avoid CI/merge race condition - Detect existing PRs from previous runs and resume instead of re-plan - Clean stale branches before starting a new issue - Add batch-mode note: auto-approve sub-skill confirmation prompts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d420532 commit 8dce81c

1 file changed

Lines changed: 68 additions & 46 deletions

File tree

.claude/skills/meta-power/SKILL.md

Lines changed: 68 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Batch-process open `[Model]` and `[Rule]` issues end-to-end: plan, implement, re
1111

1212
You are the **outer orchestrator**. For each issue you invoke existing skills and shell out to subprocesses. You never implement code directly — `make run-plan` does the heavy lifting in a separate Claude session.
1313

14+
**Batch context:** When invoking sub-skills (like `issue-to-pr`), you are running in batch mode. Auto-approve any confirmation prompts from sub-skills — do not wait for user input mid-batch.
15+
1416
## Step 0: Discover and Order Issues
1517

1618
```bash
@@ -20,13 +22,19 @@ gh issue list --state open --limit 50 --json number,title
2022

2123
Filter to issues whose title contains `[Model]` or `[Rule]`. Partition into two buckets, sort each by issue number ascending. Final order: **all Models first, then all Rules**.
2224

25+
**Check for existing PRs:** For each issue, check if a PR already exists:
26+
```bash
27+
gh pr list --search "Fixes #<number>" --state open --json number,headRefName
28+
```
29+
If a PR exists, mark the issue as `resume` — skip Step 1 (plan) and jump to Step 2 (execute) or Step 4 (fix loop) depending on whether the PR already has implementation commits.
30+
2331
Present the ordered list to the user for confirmation before starting:
2432

2533
```
2634
Batch plan:
2735
Models:
2836
#108 [Model] LongestCommonSubsequence
29-
#103 [Model] SubsetSum
37+
#103 [Model] SubsetSum (has open PR #115 — will resume)
3038
Rules:
3139
#109 [Rule] LCS → MIS
3240
#110 [Rule] LCS → ILP
@@ -46,6 +54,15 @@ For the current issue:
4654
git checkout main && git pull origin main
4755
```
4856

57+
**Check for stale branches:** If a branch `issue-<number>-*` exists with no open PR, delete it to start fresh:
58+
```bash
59+
STALE=$(git branch --list "issue-<number>-*" | head -1 | xargs)
60+
if [ -n "$STALE" ]; then
61+
git branch -D "$STALE"
62+
git push origin --delete "$STALE" 2>/dev/null || true
63+
fi
64+
```
65+
4966
Invoke the `issue-to-pr` skill with the issue number. This creates a branch, writes a plan to `docs/plans/`, and opens a PR.
5067

5168
**If `issue-to-pr` fails** (e.g., incomplete issue template): record status as `skipped (plan failed)`, move to next issue.
@@ -69,96 +86,97 @@ This spawns a new Claude session (up to 500 turns) that reads the plan and imple
6986

7087
## Step 3: Review
7188

72-
After execution completes, ensure changes are committed and pushed:
89+
After execution completes, push and request Copilot review:
7390

7491
```bash
75-
# Check for uncommitted changes
76-
git add -A && git diff --cached --quiet || git commit -m "Implement #<number>: <title>
77-
78-
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
7992
git push
80-
```
81-
82-
Request Copilot review:
83-
```bash
8493
make copilot-review
8594
```
8695

8796
## Step 4: Fix Loop (max 3 retries)
8897

8998
```dot
9099
digraph fix_loop {
91-
"Wait 5 min" [shape=box];
100+
"Poll CI until done" [shape=box];
92101
"Run fix-pr" [shape=box];
93102
"Push changes" [shape=box];
94-
"Wait 5 min for CI" [shape=box];
103+
"Poll CI until done (2)" [shape=box];
95104
"CI green?" [shape=diamond];
96105
"Retries < 3?" [shape=diamond];
97106
"Proceed to merge" [shape=doublecircle];
98107
"Give up" [shape=doublecircle];
99108
100-
"Wait 5 min" -> "Run fix-pr";
109+
"Poll CI until done" -> "Run fix-pr";
101110
"Run fix-pr" -> "Push changes";
102-
"Push changes" -> "Wait 5 min for CI";
103-
"Wait 5 min for CI" -> "CI green?";
111+
"Push changes" -> "Poll CI until done (2)";
112+
"Poll CI until done (2)" -> "CI green?";
104113
"CI green?" -> "Proceed to merge" [label="yes"];
105114
"CI green?" -> "Retries < 3?" [label="no"];
106-
"Retries < 3?" -> "Wait 5 min" [label="yes"];
115+
"Retries < 3?" -> "Poll CI until done" [label="yes, re-run fix-pr"];
107116
"Retries < 3?" -> "Give up" [label="no"];
108117
}
109118
```
110119

111120
For each retry:
112121

113-
1. **Wait 5 minutes** for Copilot review and CI to arrive:
114-
```bash
115-
sleep 300
116-
```
117-
118-
2. **Invoke `/fix-pr`** to address review comments, CI failures, and coverage gaps.
119-
120-
3. **Push fixes:**
121-
```bash
122-
git push
123-
```
124-
125-
4. **Wait 5 minutes** for CI to re-run:
126-
```bash
127-
sleep 300
128-
```
129-
130-
5. **Check CI status:**
122+
1. **Wait for CI to complete** (poll every 30s, up to 15 minutes):
131123
```bash
132124
REPO=$(gh repo view --json nameWithOwner --jq .nameWithOwner)
133-
HEAD_SHA=$(gh api repos/$REPO/pulls/$PR | python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
134-
gh api repos/$REPO/commits/$HEAD_SHA/check-runs | python3 -c "
125+
for i in $(seq 1 30); do
126+
sleep 30
127+
HEAD_SHA=$(gh api repos/$REPO/pulls/$PR | python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
128+
STATUS=$(gh api repos/$REPO/commits/$HEAD_SHA/check-runs | python3 -c "
135129
import sys,json
136130
runs = json.load(sys.stdin)['check_runs']
137131
failed = [r['name'] for r in runs if r.get('conclusion') not in ('success', 'skipped', None)]
138132
pending = [r['name'] for r in runs if r.get('conclusion') is None and r['status'] != 'completed']
139133
if pending:
140-
print('PENDING: ' + ', '.join(pending))
134+
print('PENDING')
141135
elif failed:
142-
print('FAILED: ' + ', '.join(failed))
136+
print('FAILED')
143137
else:
144138
print('GREEN')
145-
"
139+
")
140+
if [ "$STATUS" != "PENDING" ]; then break; fi
141+
done
142+
```
143+
144+
- If `GREEN` on the **first** iteration (before any fix-pr): skip the fix loop entirely, proceed to merge.
145+
- If `GREEN` after a fix-pr pass: break out of loop, proceed to merge.
146+
- If `FAILED`: continue to step 2.
147+
- If still `PENDING` after 15 min: treat as `FAILED`.
148+
149+
2. **Invoke `/fix-pr`** to address review comments, CI failures, and coverage gaps.
150+
151+
3. **Push fixes:**
152+
```bash
153+
git push
146154
```
147155

148-
- If `GREEN`: break out of loop, proceed to merge.
149-
- If `PENDING`: wait another 2 minutes, re-check once.
150-
- If `FAILED`: increment retry counter, continue loop.
156+
4. Increment retry counter. If `< 3`, go back to step 1 (poll CI). If `= 3`, give up.
151157

152158
**After 3 failed retries:** record status as `fix-pr failed (3 retries)`, leave PR open, move to next issue.
153159

154160
## Step 5: Merge
155161

156162
```bash
157-
gh pr merge $PR --squash --delete-branch
163+
gh pr merge $PR --squash --delete-branch --auto
158164
```
159165

166+
The `--auto` flag tells GitHub to merge once all required checks pass, avoiding a race between CI completion and the merge command.
167+
160168
**If merge fails** (e.g., conflict): record status as `merge failed`, leave PR open, move to next issue.
161169

170+
Wait for the auto-merge to complete before proceeding:
171+
```bash
172+
for i in $(seq 1 20); do
173+
sleep 15
174+
STATE=$(gh pr view $PR --json state --jq .state)
175+
if [ "$STATE" = "MERGED" ]; then break; fi
176+
if [ "$STATE" = "CLOSED" ]; then break; fi # merge conflict closed it
177+
done
178+
```
179+
162180
## Step 6: Sync
163181

164182
Return to main for the next issue:
@@ -179,7 +197,7 @@ After all issues are processed, print the summary table:
179197
| Issue | Title | Status |
180198
|-------|------------------------------------|---------------------------|
181199
| #108 | [Model] LCS | merged |
182-
| #103 | [Model] SubsetSum | merged |
200+
| #103 | [Model] SubsetSum | merged (resumed PR #115) |
183201
| #109 | [Rule] LCS → MIS | merged |
184202
| #110 | [Rule] LCS → ILP | fix-pr failed (3 retries) |
185203
| #97 | [Rule] BinPacking → ILP | merged |
@@ -193,8 +211,10 @@ Completed: 4/6 | Skipped: 1 | Failed: 1
193211
| Name | Value | Rationale |
194212
|------|-------|-----------|
195213
| `MAX_RETRIES` | 3 | Most issues fix in 1-2 rounds |
196-
| `CI_WAIT` | 5 min | GitHub Actions typical completion time |
197-
| `PENDING_EXTRA_WAIT` | 2 min | One grace period for slow CI |
214+
| `CI_POLL_INTERVAL` | 30s | Frequent enough to react quickly |
215+
| `CI_POLL_MAX` | 15 min | Upper bound for CI completion |
216+
| `MERGE_POLL_INTERVAL` | 15s | Wait for auto-merge to land |
217+
| `MERGE_POLL_MAX` | 5 min | Upper bound for merge completion |
198218

199219
## Common Failure Modes
200220

@@ -205,3 +225,5 @@ Completed: 4/6 | Skipped: 1 | Failed: 1
205225
| CI red after 3 retries | Deep bug or flaky test | Leave PR open for human review |
206226
| Merge conflict | Concurrent push to main | Leave PR open; manual rebase needed |
207227
| Rule fails because model missing | Model issue was skipped earlier | Expected; skip rule too |
228+
| Stale branch from previous run | Previous meta-power run failed mid-issue | Auto-cleaned in Step 1 |
229+
| PR already exists for issue | Previous partial attempt | Resumed from existing PR |

0 commit comments

Comments
 (0)