Skip to content

Commit 2f191a4

Browse files
Claude Code Botclaude
andcommitted
feat(ci-workflows): add post-push-loop skill for CI monitoring automation
New plugin providing the /post-push-loop skill that autonomously monitors CI after pushing a PR, fetches bot review findings, applies confident fixes, and escalates ambiguous issues to the human. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 61288d7 commit 2f191a4

File tree

3 files changed

+263
-0
lines changed

3 files changed

+263
-0
lines changed

.claude-plugin/marketplace.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,27 @@
5858
],
5959
"category": "mobile",
6060
"strict": false
61+
},
62+
{
63+
"name": "ci-workflows",
64+
"source": "./plugins/ci-workflows",
65+
"description": "CI/CD workflow automation: post-push CI monitoring, review iteration, and automated fix loops.",
66+
"version": "1.0.0",
67+
"author": {
68+
"name": "Andrew Rich",
69+
"url": "https://github.com/smartwatermelon"
70+
},
71+
"license": "MIT",
72+
"keywords": [
73+
"ci",
74+
"cd",
75+
"post-push",
76+
"github-actions",
77+
"review-loop",
78+
"automation"
79+
],
80+
"category": "workflow",
81+
"strict": false
6182
}
6283
]
6384
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "ci-workflows",
3+
"description": "CI/CD workflow automation skills for Claude Code: post-push CI monitoring, review iteration, and automated fix loops.",
4+
"version": "1.0.0",
5+
"author": {
6+
"name": "Andrew Rich",
7+
"url": "https://github.com/smartwatermelon"
8+
},
9+
"homepage": "https://github.com/smartwatermelon/smartwatermelon-marketplace",
10+
"repository": "https://github.com/smartwatermelon/smartwatermelon-marketplace",
11+
"license": "MIT",
12+
"keywords": [
13+
"ci",
14+
"cd",
15+
"post-push",
16+
"github-actions",
17+
"review-loop",
18+
"automation"
19+
]
20+
}
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
---
2+
name: post-push-loop
3+
description: "Use after pushing a PR branch to autonomously monitor CI, fetch remote review findings, generate fixes, and iterate until CI is clean or a hard decision is needed. Invoke as: /post-push-loop [pr-number]"
4+
---
5+
6+
# Post-Push Loop
7+
8+
Autonomously iterate through the CI/review cycle after a push. Loop exits only when
9+
external signals clear (CI green + no unresolved bot findings), or when a finding
10+
requires a human decision.
11+
12+
## Invoke
13+
14+
```
15+
/post-push-loop [pr-number]
16+
```
17+
18+
If `pr-number` is omitted, auto-detect from `gh pr view --json number -q .number`.
19+
20+
---
21+
22+
## Dependencies
23+
24+
This skill requires `~/.claude/scripts/post-push-status.sh` (from the `claude-config`
25+
repo). Before starting, verify it exists and is executable. If missing, escalate
26+
immediately — do not attempt to create or substitute it.
27+
28+
Additional requirements: `gh` (authenticated), `jq`, `python3`, and a `pre-push` git
29+
hook with `POSTPUSH_LOOP` bypass support.
30+
31+
---
32+
33+
## Protocol
34+
35+
### Before Starting
36+
37+
1. Verify `~/.claude/scripts/post-push-status.sh` exists and is executable
38+
2. Confirm current branch: `git branch --show-current` — must NOT be `main`
39+
3. Confirm PR number: `gh pr view --json number -q .number`
40+
4. Confirm `POSTPUSH_LOOP` will be set for all git push calls in this session
41+
42+
### The Loop
43+
44+
Repeat until **Exit: Success** or **Exit: Escalate**:
45+
46+
---
47+
48+
#### Phase 1 — WAIT for CI to complete
49+
50+
Poll every 30 seconds:
51+
52+
```bash
53+
bash ~/.claude/scripts/post-push-status.sh <PR_NUMBER>
54+
```
55+
56+
Continue polling while `CI_STATE=PENDING`, `CI_STATE=EXPECTED`, or `CI_STATE=UNKNOWN`.
57+
58+
`CI_STATE=UNKNOWN` indicates a transient error (network failure or no CI checks registered yet) — treat it as pending and continue polling. It counts against the 15-minute timeout.
59+
60+
Timeout: if `CI_STATE` has not resolved after 15 minutes, escalate with reason
61+
"CI did not complete within timeout — check for stuck jobs."
62+
63+
---
64+
65+
#### Phase 2 — EVALUATE termination
66+
67+
Parse script output using this exact decision table — evaluate in order:
68+
69+
| CI_STATE | FINDING lines present? | Action |
70+
|---|---|---|
71+
| `SUCCESS` | No | **Exit: Success** |
72+
| `SUCCESS` | Yes | Proceed to Phase 3 |
73+
| `FAILURE` | Either | Proceed to Phase 3 |
74+
| `ERROR` | Either | Proceed to Phase 3 |
75+
| `UNKNOWN` | Either | Return to Phase 1 (transient — treat as pending) |
76+
77+
`CI_STATE=SUCCESS` alone is NOT sufficient to exit — zero `FINDING` lines are also required.
78+
79+
---
80+
81+
#### Phase 3 — CLASSIFY findings
82+
83+
`FINDING` lines have the format:
84+
85+
```
86+
FINDING source=<bot> file="<path>" line=<line> comment=<text>
87+
```
88+
89+
The `file=` value is always double-quoted (strip the outer quotes when reading it). This handles file paths that contain spaces.
90+
91+
**Note on `issues/comments` staleness:** Findings from the PR conversation thread
92+
(`issues/comments`) cannot be filtered by commit — that API provides no
93+
`original_commit_id` field. These findings may include bot comments from previous
94+
commits. Treat an `issues/comments` finding with an empty `file=` and `line=` as a
95+
potential staleness indicator; apply extra scrutiny before classifying as CONFIDENT_FIX.
96+
97+
For each `FINDING` line, classify as **CONFIDENT_FIX** or **ESCALATE**:
98+
99+
**CONFIDENT_FIX** (all of the following must be true):
100+
101+
- Finding has a specific `file=` and `line=` reference
102+
- Fix is local to that file — explicitly verify before classifying: (1) no other files import or call the changed symbol, (2) no tests outside this file reference it, (3) the change does not affect a shared interface or type
103+
- Finding falls into a known pattern:
104+
- Lint or style error
105+
- Type error or missing type annotation
106+
- Missing null/undefined check
107+
- Unused import or variable
108+
- Test assertion mismatch with clear expected value
109+
110+
**ESCALATE** (any of the following):
111+
112+
- Finding is architectural ("this approach should be reconsidered")
113+
- Finding touches a security-critical path:
114+
- Files matching: `**/auth/**`, `**/jwt/**`, `**/password/**`, `**/session/**`,
115+
`**/payment/**`, `**/billing/**`, `**/db/**`, `**/migrations/**`,
116+
`**/crypto/**`, `**/secrets/**`
117+
- Finding references behavior not introduced by this branch
118+
- Root cause is ambiguous (no specific file/line, or conflicting signals)
119+
- Two findings appear to conflict with each other
120+
- `CI_STATE=ERROR` (infrastructure failure, not code failure)
121+
122+
**If ANY finding is ESCALATE**: stop. Do NOT apply any fixes. Go to **Exit: Escalate**.
123+
124+
**If ALL findings are CONFIDENT_FIX**: proceed to Phase 4.
125+
126+
---
127+
128+
#### Phase 4 — FIX
129+
130+
Apply edits for each confident finding. Keep changes minimal — fix exactly what the
131+
finding describes, nothing more.
132+
133+
After all edits:
134+
135+
```bash
136+
git diff HEAD
137+
```
138+
139+
Review the diff briefly. If the diff is larger than expected or touches files not
140+
referenced in findings, treat as ESCALATE.
141+
142+
---
143+
144+
#### Phase 5 — COMMIT
145+
146+
```bash
147+
git add <only the files you changed>
148+
git commit -m "fix: address <summary of findings>
149+
150+
Post-push loop iteration N: <list of findings addressed>"
151+
```
152+
153+
Note: the pre-commit hook will run automatically. If it blocks the commit, treat as
154+
**Exit: Escalate** — surface both the hook finding and the remote finding.
155+
156+
Environment for push: set `POSTPUSH_LOOP=1` to bypass the Protocol 4 interactive prompt.
157+
158+
---
159+
160+
#### Phase 6 — PUSH
161+
162+
```bash
163+
POSTPUSH_LOOP=1 git push
164+
```
165+
166+
Return to Phase 1.
167+
168+
---
169+
170+
### Exit: Success
171+
172+
```
173+
✅ POST-PUSH LOOP COMPLETE
174+
175+
PR #NNN | N iterations
176+
CI state: SUCCESS
177+
Unresolved bot findings: 0
178+
179+
Commits pushed:
180+
<list of commit hashes + messages>
181+
```
182+
183+
### Exit: Escalate
184+
185+
```
186+
⚠️ LOOP PAUSED — Human decision required
187+
188+
PR #NNN | Iteration N | CI: <state>
189+
190+
FINDING(S) requiring your input:
191+
[For each finding]
192+
Source: <bot>
193+
File: <path>:<line>
194+
Comment: <text>
195+
Reason escalated: <reason>
196+
197+
CONTEXT:
198+
Fixes applied this session: <list or "none">
199+
Local review at last commit: passed / blocked
200+
Current CI state: <state>
201+
202+
OPTIONS:
203+
1. Provide direction here → I resume with your guidance
204+
2. Take over manually → type "take over" to exit loop
205+
3. Abandon → type "abandon" to exit, leave branch as-is
206+
```
207+
208+
Await human response. Do not proceed autonomously.
209+
210+
---
211+
212+
## Hard Constraints
213+
214+
- Never exit with "clean" status based on your own assertion — only based on
215+
`CI_STATE=SUCCESS` AND no `FINDING` lines from the status script.
216+
- Never apply fixes when any finding is classified ESCALATE.
217+
- Never use `--no-verify` on any git command.
218+
- Never push to `main`.
219+
- Set `POSTPUSH_LOOP=1` on every `git push` call within this skill.
220+
- Never exceed 5 push iterations in a single loop session. After 5 iterations,
221+
escalate with reason "Iteration limit reached — autonomous loop terminated to
222+
prevent unbounded cost."

0 commit comments

Comments
 (0)