-
Notifications
You must be signed in to change notification settings - Fork 27
438 lines (397 loc) Β· 19.6 KB
/
Copy pathactionadon.yml
File metadata and controls
438 lines (397 loc) Β· 19.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
name: actionadon
# actionadon β Dakota's community next-action bot.
# Posts one short comment each time an issue moves to a new lifecycle stage.
# Handles /claim, /unclaim, and /ready. Sweeps stale claimed issues daily.
#
# Lifecycle:
# opened β status/discussing
# status/approved (maintainer) β needs spec comment
# status/queued (maintainer) β queue announcement
# /claim comment β status/claimed + assigned
# /ready comment (wrangler) β queued for agents
# /unclaim comment β back in queue
# daily sweep β unclaim issues idle > 7 days
on:
issues:
types: [opened, labeled]
issue_comment:
types: [created]
schedule:
- cron: '0 9 * * *'
permissions:
issues: write
pull-requests: write
contents: read
env:
WORKFLOW_NAME: "Raptor Current"
# Maintainers are implicit wranglers via their repo permissions.
# This list is for additional low-barrier operators who can steer the queue.
# Keep this list in sync with AGENTS.md and files/hive/hive-project.yaml.example.
WRANGLERS: "castrojo,ahmedadan,alatiera,hanthor,coxde,renner0e"
jobs:
# ββ Ensure lifecycle labels exist βββββββββββββββββββββββββββββββββββββββββ
# Idempotent. Runs before any job that writes labels.
ensure-labels:
if: >-
github.event_name == 'issues' ||
github.event_name == 'schedule' ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request == null &&
(startsWith(github.event.comment.body, '/claim') ||
startsWith(github.event.comment.body, '/unclaim') ||
startsWith(github.event.comment.body, '/ready')))
runs-on: ubuntu-24.04
steps:
- name: Create lifecycle labels
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
label() {
gh label create "$1" --color "$2" --description "$3" --repo "$REPO" 2>/dev/null \
|| gh label edit "$1" --color "$2" --description "$3" --repo "$REPO" 2>/dev/null \
|| true
}
label "status/discussing" "7c83fd" "Structured issue flow in progress; not ready for the agent queue yet."
label "status/approved" "2ea44f" "Approved for queue preparation; add acceptance criteria before /ready."
label "status/claimed" "e8b86d" "Actively being worked by a human or agent."
label "agent/blocked" "e74c3c" "Blocked and needs human input before work can continue."
label "status/queued" "a467dc" "Structured issue is ready for a human or agent to claim."
label "needs-human/agent-oops" "f795fe" "Agent mistake or bad automation outcome; Hive must not touch."
label "flow/agent-donation" "1b7f83" "Donate agent time for a repo, issue, or PR review/report request."
label "flow/project-report" "0e8a16" "Agent flow: produce a sourced project report."
label "flow/issue-review" "5319e7" "Agent flow: review a linked issue and produce a sourced report."
label "flow/pr-review" "fbca04" "Agent flow: review a linked pull request and produce a sourced report."
label "status/triage" "e4e669" "Issue needs maintainer triage."
label "pr/needs-review" "e4e669" "PR needs a maintainer review before it can move forward."
label "hold" "8b949e" "Do not touch; intentionally held by humans."
label "do-not-merge" "b60205" "Do not merge or automate this item."
# ββ New issue opened βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Templates stamp labels directly (e.g. kind/bug, flow/agent-donation).
# Route by those labels rather than scanning free-text body markers.
on-issue-opened:
if: github.event_name == 'issues' && github.event.action == 'opened'
needs: ensure-labels
runs-on: ubuntu-24.04
steps:
- name: Label as discussing if not already labelled
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.issue.html_url }}
run: |
LABELS=$(gh issue view "$ISSUE_URL" --json labels --jq '[.labels[].name] | join(",")' 2>/dev/null || echo "")
# Skip if already has a lifecycle label (template stamped it)
if echo "$LABELS" | grep -qE "status/|flow/agent-donation"; then
exit 0
fi
gh issue edit "$ISSUE_URL" --add-label "status/discussing"
- name: Welcome comment (bug/feature reports)
if: >-
!contains(github.event.issue.labels.*.name, 'flow/agent-donation')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.issue.html_url }}
WORKFLOW_NAME: ${{ env.WORKFLOW_NAME }}
run: |
ISSUE_NUMBER=$(echo "$ISSUE_URL" | grep -oP '\d+$')
cat > /tmp/comment.md << EOF
**${WORKFLOW_NAME}** β thanks for the report.
A maintainer will triage this and assign it to the right area. Once a fix ships in nightly, confirm it works on your machine:
\`\`\`bash
ujust verify ${ISSUE_NUMBER}
\`\`\`
Your confirmation is what closes the loop.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
- name: Fast-track donated agent work
if: contains(github.event.issue.labels.*.name, 'flow/agent-donation')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.issue.html_url }}
run: |
# Detect flow from the dropdown value in the body
BODY=$(gh issue view "$ISSUE_URL" --json body --jq '.body // ""')
FLOW_LABEL="flow/project-report"
if printf '%s\n' "$BODY" | grep -Eq 'https://[^[:space:])>]+/pull/[0-9]+'; then
FLOW_LABEL="flow/pr-review"
elif printf '%s\n' "$BODY" | grep -Eq 'https://[^[:space:])>]+/issues/[0-9]+'; then
FLOW_LABEL="flow/issue-review"
fi
gh issue edit "$ISSUE_URL" \
--add-label "status/queued" \
--add-label "$FLOW_LABEL"
# ββ Label transitions ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
on-labeled:
if: github.event_name == 'issues' && github.event.action == 'labeled'
needs: ensure-labels
runs-on: ubuntu-24.04
steps:
- name: Comment on status/approved
if: github.event.label.name == 'status/approved'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.issue.html_url }}
run: |
cat << 'EOF' > /tmp/comment.md
β
Approved. The community has found its way here.
Before this goes to the build queue it needs **acceptance criteria** β the spec a contributor or agent will implement against. Add it to the issue body, then apply `status/queued` to open it up.
**Wranglers:** write acceptance criteria, then comment `/ready`.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
- name: Comment on status/queued
if: github.event.label.name == 'status/queued'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.issue.html_url }}
run: |
LABELS=$(gh issue view "$ISSUE_URL" --json labels --jq '[.labels[].name] | join(",")')
if echo "$LABELS" | grep -q "flow/agent-donation"; then
cat << 'EOF' > /tmp/comment.md
π In the queue! Hive now has the instructions it needs for this donated agent request.
**Agent workflow:**
```bash
just validate # check element graph
just build default # build the image
just lint # bootc container lint
just boot-test # confirm desktop boots
```
The issue body defines the flow: project report, issue review, or PR review. Post a sourced report comment when done, then close this issue.
EOF
else
cat << 'EOF' > /tmp/comment.md
π In the queue! This issue has a spec and is ready to build.
**Contributors:** comment `/claim` to take it, then:
```bash
git checkout upstream/main -b fix/this-issue
# make your changes
just validate # check element graph
just build default # build the image
just boot-test # automated smoke test (exits 0/1)
just lint # bootc container lint
```
Open a PR with `Closes #NNN` when ready.
**Reviewers:** after the PR merges and ships in nightly:
```bash
ujust verify <issue-number> # confirm the fix on your machine
```
Read [AGENTS.md](../../blob/main/AGENTS.md) for the full workflow.
EOF
fi
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
# ββ /claim, /unclaim, and /ready ββββββββββββββββββββββββββββββββββββββββββ
claim-handler:
if: >-
github.event_name == 'issue_comment' &&
github.event.issue.pull_request == null &&
(startsWith(github.event.comment.body, '/claim') ||
startsWith(github.event.comment.body, '/unclaim') ||
startsWith(github.event.comment.body, '/ready'))
needs: ensure-labels
runs-on: ubuntu-24.04
steps:
- name: Resolve wrangler status
id: wrangler
env:
COMMENTER: ${{ github.event.comment.user.login }}
WRANGLERS: ${{ env.WRANGLERS }}
run: |
IS_WRANGLER=false
for user in $(echo "$WRANGLERS" | tr ',' ' '); do
if [ "$COMMENTER" = "$user" ]; then
IS_WRANGLER=true
break
fi
done
echo "is_wrangler=$IS_WRANGLER" >> "$GITHUB_OUTPUT"
- name: Handle /claim
if: startsWith(github.event.comment.body, '/claim')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.issue.html_url }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
COMMENTER: ${{ github.event.comment.user.login }}
run: |
LABELS=$(gh issue view "$ISSUE_URL" --json labels --jq '[.labels[].name] | join(",")')
# Already claimed
if echo "$LABELS" | grep -q "status/claimed"; then
ASSIGNEE=$(gh issue view "$ISSUE_URL" --json assignees --jq '.assignees[0].login // "someone"')
cat << EOF > /tmp/comment.md
Already claimed by @${ASSIGNEE}. If they've gone quiet, a maintainer can \`/unclaim\` to return it to the queue.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
exit 0
fi
# Not in queue
if ! echo "$LABELS" | grep -q "status/queued"; then
cat << 'EOF' > /tmp/comment.md
This issue isn't in the agent queue yet β it needs `status/queued` before it can be claimed.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
exit 0
fi
gh issue edit "$ISSUE_URL" --add-label "status/claimed" --remove-label "status/queued" --add-assignee "$COMMENTER"
if echo "$LABELS" | grep -q "flow/agent-donation"; then
cat << EOF > /tmp/comment.md
π£ @${COMMENTER} has this one! Follow the workflow embedded in the issue, post a sourced report comment when you're done, and close this issue so Hive can move on.
EOF
else
cat << EOF > /tmp/comment.md
π£ @${COMMENTER} has this one!
**Your workflow:**
\`\`\`bash
git checkout upstream/main -b fix/issue-${ISSUE_NUMBER}
# implement the acceptance criteria
just validate # fast graph check
just build default # build the image (~1hr first time)
just boot-test # confirm desktop boots (exits 0/1)
just lint # bootc container lint
\`\`\`
Open a PR with \`Closes #${ISSUE_NUMBER}\` in the description when ready. Hit a blocker? Add \`agent/blocked\` and describe what you need.
EOF
fi
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
- name: Handle /ready
if: startsWith(github.event.comment.body, '/ready')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.issue.html_url }}
COMMENTER: ${{ github.event.comment.user.login }}
IS_WRANGLER: ${{ steps.wrangler.outputs.is_wrangler }}
REPO: ${{ github.repository }}
run: |
PERMISSION=$(gh api "repos/${REPO}/collaborators/${COMMENTER}/permission" \
--jq '.permission' 2>/dev/null || echo "none")
HAS_WRITE=false
case "$PERMISSION" in admin|maintain|write) HAS_WRITE=true ;; esac
if [ "$IS_WRANGLER" != "true" ] && [ "$HAS_WRITE" = "false" ]; then
cat << 'EOF' > /tmp/comment.md
Only a wrangler or maintainer can move an issue into the queue with `/ready`.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
exit 0
fi
LABELS=$(gh issue view "$ISSUE_URL" --json labels --jq '[.labels[].name] | join(",")')
BODY=$(gh issue view "$ISSUE_URL" --json body --jq '.body // ""')
if ! echo "$LABELS" | grep -q "status/approved"; then
cat << 'EOF' > /tmp/comment.md
This issue needs `status/approved` before it can enter the queue.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
exit 0
fi
ACCEPTANCE_SECTION=$(
printf '%s\n' "$BODY" | awk '
/^###[[:space:]]/ {
if (in_section) exit
if ($0 ~ /^###[[:space:]]+.*Acceptance criteria[[:space:]]*$/) {
in_section=1
print
next
}
}
in_section { print }
'
)
if [ -z "$ACCEPTANCE_SECTION" ]; then
cat << 'EOF' > /tmp/comment.md
This issue needs a dedicated `### Acceptance criteria` section in the body before it can enter the queue.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
exit 0
fi
if ! printf '%s\n' "$ACCEPTANCE_SECTION" | grep -Eq '^[[:space:]]*-[[:space:]]\[( |x|X)\][[:space:]]+'; then
cat << 'EOF' > /tmp/comment.md
This issue needs at least one real checklist item under `### Acceptance criteria` before it can enter the queue.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
exit 0
fi
if echo "$LABELS" | grep -q "status/queued"; then
cat << 'EOF' > /tmp/comment.md
This issue is already in the queue and ready for a human or agent to `/claim`.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
exit 0
fi
gh issue edit "$ISSUE_URL" --add-label "status/queued"
cat << 'EOF' > /tmp/comment.md
In the queue. Comment `/claim` to take it.
**Build and test loop:**
```bash
git checkout upstream/main -b fix/this-issue
just validate # element graph check
just build default # build the image
just boot-test # automated smoke test (exits 0 = pass)
just lint # bootc container lint
```
Open a PR with `Closes #NNN` in the description. Read [AGENTS.md](../../blob/main/AGENTS.md) for the full workflow.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
- name: Handle /unclaim
if: startsWith(github.event.comment.body, '/unclaim')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.issue.html_url }}
COMMENTER: ${{ github.event.comment.user.login }}
REPO: ${{ github.repository }}
IS_WRANGLER: ${{ steps.wrangler.outputs.is_wrangler }}
run: |
ASSIGNEE=$(gh issue view "$ISSUE_URL" --json assignees --jq '.assignees[0].login // ""')
# Allow assignee, wrangler, or repo collaborators with write+ to unclaim
PERMISSION=$(gh api "repos/${REPO}/collaborators/${COMMENTER}/permission" \
--jq '.permission' 2>/dev/null || echo "none")
HAS_WRITE=false
case "$PERMISSION" in admin|maintain|write) HAS_WRITE=true ;; esac
if [ "$COMMENTER" != "$ASSIGNEE" ] && [ "$HAS_WRITE" = "false" ] && [ "$IS_WRANGLER" != "true" ]; then
cat << EOF > /tmp/comment.md
Only @${ASSIGNEE}, a wrangler, or a maintainer can unclaim this issue.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
exit 0
fi
gh issue edit "$ISSUE_URL" --remove-label "status/claimed" --add-label "status/queued"
if [ -n "$ASSIGNEE" ]; then
gh issue edit "$ISSUE_URL" --remove-assignee "$ASSIGNEE"
fi
cat << 'EOF' > /tmp/comment.md
Back in the queue. Comment `/claim` to pick it up. Start with `just validate` to confirm the graph is sound.
EOF
gh issue comment "$ISSUE_URL" --body-file /tmp/comment.md
# ββ Stale claim sweep ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Daily: find claimed issues with no update in 7 days, return to queue.
stale-claim-sweep:
if: github.event_name == 'schedule'
needs: ensure-labels
runs-on: ubuntu-24.04
steps:
- name: Unclaim stale issues
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
CUTOFF=$(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ')
gh issue list --repo "$REPO" \
--label "status/claimed" \
--json number,url,updatedAt,assignees \
--jq ".[] | select(.updatedAt < \"$CUTOFF\") | .number" \
| while read -r NUMBER; do
ASSIGNEE=$(gh issue view "$NUMBER" --repo "$REPO" \
--json assignees --jq '.assignees[0].login // ""')
gh issue edit "$NUMBER" --repo "$REPO" --remove-label "status/claimed" --add-label "status/queued"
if [ -n "$ASSIGNEE" ]; then
gh issue edit "$NUMBER" --repo "$REPO" --remove-assignee "$ASSIGNEE"
cat << EOF > /tmp/comment.md
This issue has been claimed for 7 days with no PR activity, so it's back in the queue.
@${ASSIGNEE} β if you're still working on it, just \`/claim\` again and it's yours. Otherwise anyone can pick it up:
\`\`\`bash
git checkout upstream/main -b fix/issue-${NUMBER}
just validate && just build default && just boot-test && just lint
\`\`\`
EOF
else
cat << 'EOF' > /tmp/comment.md
This issue has been claimed for 7 days with no PR activity, so it's back in the queue. Comment `/claim` to take it.
EOF
fi
gh issue comment "$NUMBER" --repo "$REPO" --body-file /tmp/comment.md
done