@nanobpm/nano-workforce 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +60 -0
- package/.github/workflows/release.yml +58 -0
- package/.releaserc.json +17 -0
- package/AGENTS.md +168 -0
- package/CHANGELOG.md +231 -0
- package/LICENSE +202 -0
- package/README.md +303 -0
- package/SPEC.md +492 -0
- package/actions/abandon.test.ts +93 -0
- package/actions/abandon.ts +23 -0
- package/actions/blackboard.test.ts +195 -0
- package/actions/blackboard.ts +76 -0
- package/actions/cancel.ts +29 -0
- package/actions/feature-answer-hook.ts +44 -0
- package/actions/message.ts +49 -0
- package/actions/plan-hook.ts +19 -0
- package/actions/plan-start.ts +17 -0
- package/actions/start.ts +19 -0
- package/actions/status.ts +22 -0
- package/actions/webhook-submit.ts +21 -0
- package/app/abandon.test.ts +97 -0
- package/app/abandon.ts +105 -0
- package/app/baseGuard.test.ts +35 -0
- package/app/baseGuard.ts +62 -0
- package/app/blackboard.test.ts +295 -0
- package/app/blackboard.ts +301 -0
- package/app/github.test.ts +59 -0
- package/app/github.ts +647 -0
- package/app/mergeExclusion.test.ts +168 -0
- package/app/mergeExclusion.ts +211 -0
- package/app/mergeProtocol.test.ts +124 -0
- package/app/mergeProtocol.ts +193 -0
- package/app/mergeRebaseArm.test.ts +72 -0
- package/app/mergeTrain.test.ts +91 -0
- package/app/mergeTrain.ts +117 -0
- package/app/persist-escalation.test.ts +119 -0
- package/app/persist-round.test.ts +65 -0
- package/app/plan.test.ts +317 -0
- package/app/plan.ts +321 -0
- package/app/record-plan-review.test.ts +38 -0
- package/app/reviewWait.test.ts +70 -0
- package/app/reviewWait.ts +59 -0
- package/app/rounds.test.ts +74 -0
- package/app/rounds.ts +48 -0
- package/app/service.test.ts +101 -0
- package/app/service.ts +895 -0
- package/app/taskDelta.test.ts +144 -0
- package/app/taskDelta.ts +175 -0
- package/app/trialMerge.test.ts +15 -0
- package/app/trialMerge.ts +102 -0
- package/app/waves.test.ts +128 -0
- package/app/waves.ts +116 -0
- package/assets/icon.svg +13 -0
- package/components/review-round.json +69 -0
- package/db/migrations/001_init.sql +46 -0
- package/db/migrations/002_transcript.sql +7 -0
- package/db/migrations/003_open_escalation.sql +8 -0
- package/db/migrations/004_merge.sql +36 -0
- package/db/migrations/004_planning.sql +37 -0
- package/db/migrations/005_job_activation.sql +15 -0
- package/db/migrations/005_plan_deps.sql +20 -0
- package/db/migrations/006_plan_review.sql +22 -0
- package/db/migrations/006_task_escalation.sql +52 -0
- package/db/migrations/007_plan_review_job_key.sql +14 -0
- package/db/migrations/007_wave_gate.sql +16 -0
- package/db/migrations/008_review_nudge.sql +9 -0
- package/db/migrations/009_plan_blackboard.sql +46 -0
- package/db/migrations/010_plan_task_deltas.sql +27 -0
- package/db/migrations/011_plan_merge_exclusions.sql +26 -0
- package/db/migrations/012_merge_protocol_attempt.sql +4 -0
- package/db/migrations/013_merge_train_waiting_lane.sql +6 -0
- package/db/migrations/014_plan_trial_merges.sql +21 -0
- package/db/migrations/015_pr_abandon_token.sql +9 -0
- package/deno.json +24 -0
- package/deno.lock +1776 -0
- package/main.ts +71 -0
- package/nano-ide.ext.json +7 -0
- package/nano.app.json +138 -0
- package/nanobpm.project.json +20 -0
- package/package.json +56 -0
- package/pages/epic.page.json +195 -0
- package/pages/home.page.json +296 -0
- package/prompts/feature.md +132 -0
- package/prompts/fix-ci.md +65 -0
- package/prompts/plan-review.md +69 -0
- package/prompts/plan.md +183 -0
- package/prompts/rebase.md +82 -0
- package/prompts/review-round.md +171 -0
- package/prompts/trial-merge.md +43 -0
- package/renovate.json +21 -0
- package/resources/processes/convergence-loop.bpmn +399 -0
- package/resources/processes/merge-loop.bpmn +585 -0
- package/resources/processes/plan-fanout.bpmn +546 -0
- package/scripts/check-agent-prompts.test.ts +84 -0
- package/scripts/check-agent-prompts.ts +143 -0
- package/scripts/layout-bpmn.ts +99 -0
- package/scripts/purge-db.ts +57 -0
- package/scripts/upgrade-from-pack.ts +334 -0
- package/tsconfig.json +51 -0
- package/workers/arm-merge/worker.ts +18 -0
- package/workers/finalize/worker.ts +89 -0
- package/workers/mark-merged/worker.ts +21 -0
- package/workers/merge/worker.ts +119 -0
- package/workers/persist-escalation/worker.ts +107 -0
- package/workers/persist-round/worker.ts +52 -0
- package/workers/persist-task-escalation/worker.ts +112 -0
- package/workers/record-plan/worker.ts +135 -0
- package/workers/record-plan-review/worker.ts +92 -0
- package/workers/record-results/worker.ts +30 -0
- package/workers/record-trial-merge/worker.test.ts +104 -0
- package/workers/record-trial-merge/worker.ts +88 -0
- package/workers/record-wave/worker.test.ts +221 -0
- package/workers/record-wave/worker.ts +308 -0
- package/workers/select-wave/worker.test.ts +130 -0
- package/workers/select-wave/worker.ts +84 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0",
|
|
3
|
+
"title": "PR Review Convergence",
|
|
4
|
+
"nodes": [
|
|
5
|
+
{
|
|
6
|
+
"type": "text",
|
|
7
|
+
"id": "title",
|
|
8
|
+
"props": { "text": "PR Review Convergence", "variant": "heading" }
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"type": "text",
|
|
12
|
+
"id": "subtitle",
|
|
13
|
+
"props": {
|
|
14
|
+
"text": "Submit a pull request to run the autonomous review-convergence loop. Answer escalations inline; cancel a run at any time.",
|
|
15
|
+
"variant": "sub"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"type": "actionForm",
|
|
20
|
+
"id": "submit",
|
|
21
|
+
"props": {
|
|
22
|
+
"title": "Submit a pull request",
|
|
23
|
+
"submitLabel": "Start review",
|
|
24
|
+
"action": { "kind": "startProcess", "process": "convergence-loop" },
|
|
25
|
+
"fields": [
|
|
26
|
+
{ "key": "pr", "label": "owner/repo#123 or a GitHub PR URL", "type": "text" },
|
|
27
|
+
{ "key": "maxRounds", "label": "Max review rounds (blank = fleet default)", "type": "number" }
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "actionForm",
|
|
33
|
+
"id": "plan-submit",
|
|
34
|
+
"props": {
|
|
35
|
+
"title": "Hand an issue to the fleet",
|
|
36
|
+
"submitLabel": "Plan & implement",
|
|
37
|
+
"action": { "kind": "startProcess", "process": "plan-fanout" },
|
|
38
|
+
"fields": [
|
|
39
|
+
{ "key": "issue", "label": "owner/repo#123 or a GitHub issue URL", "type": "text" }
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "dataGrid",
|
|
45
|
+
"id": "prs",
|
|
46
|
+
"props": {
|
|
47
|
+
"title": "Pull requests",
|
|
48
|
+
"collapsible": true,
|
|
49
|
+
"rowKey": "pr_key",
|
|
50
|
+
"refreshMs": 5000,
|
|
51
|
+
"data": {
|
|
52
|
+
"kind": "datasource",
|
|
53
|
+
"source": "app",
|
|
54
|
+
"table": "pull_requests",
|
|
55
|
+
"orderBy": { "field": "updated_at", "dir": "desc" },
|
|
56
|
+
"filter": [
|
|
57
|
+
{ "field": "status", "in": ["converging", "waiting_review", "escalated", "waiting_deps", "waiting_merge", "queued", "merging"] }
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"tabs": [
|
|
61
|
+
{
|
|
62
|
+
"label": "Active",
|
|
63
|
+
"filter": [
|
|
64
|
+
{ "field": "status", "in": ["converging", "waiting_review", "escalated", "waiting_deps", "waiting_merge", "queued", "merging"] }
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"label": "History",
|
|
69
|
+
"filter": [{ "field": "status", "in": ["converged", "merged", "abandoned"] }]
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"columns": [
|
|
73
|
+
{ "field": "pr_key", "header": "PR", "linkField": "url" },
|
|
74
|
+
{ "field": "status", "header": "Status" },
|
|
75
|
+
{ "field": "current_round", "header": "Round" },
|
|
76
|
+
{ "field": "active_worker", "header": "Agent" },
|
|
77
|
+
{ "field": "updated_at", "header": "Updated" }
|
|
78
|
+
],
|
|
79
|
+
"rowActions": [
|
|
80
|
+
{
|
|
81
|
+
"label": "Cancel",
|
|
82
|
+
"confirm": "Cancel this PR's review run?",
|
|
83
|
+
"showWhenField": "process_key",
|
|
84
|
+
"action": { "kind": "cancelProcess", "keyField": "process_key" }
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"detail": {
|
|
88
|
+
"linkField": "url",
|
|
89
|
+
"fields": [
|
|
90
|
+
{ "field": "repo", "label": "Repository" },
|
|
91
|
+
{ "field": "number", "label": "PR number" },
|
|
92
|
+
{ "field": "active_worker", "label": "Agent (leasing worker)" },
|
|
93
|
+
{ "field": "lease_until", "label": "Activation lease until" },
|
|
94
|
+
{ "field": "merged_at", "label": "Merged at" },
|
|
95
|
+
{ "field": "outcome", "label": "Outcome" }
|
|
96
|
+
],
|
|
97
|
+
"children": [
|
|
98
|
+
{
|
|
99
|
+
"title": "Rounds",
|
|
100
|
+
"source": "app",
|
|
101
|
+
"table": "rounds",
|
|
102
|
+
"parentField": "pr_key",
|
|
103
|
+
"childField": "pr_key",
|
|
104
|
+
"orderBy": { "field": "round_no", "dir": "asc" },
|
|
105
|
+
"columns": [
|
|
106
|
+
{ "field": "round_no", "header": "#" },
|
|
107
|
+
{ "field": "status", "header": "Result" },
|
|
108
|
+
{ "field": "summary", "header": "Summary" }
|
|
109
|
+
],
|
|
110
|
+
"lazyField": { "field": "transcript", "label": "Transcript", "lazy": true }
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"title": "Escalations",
|
|
114
|
+
"source": "app",
|
|
115
|
+
"table": "escalations",
|
|
116
|
+
"parentField": "pr_key",
|
|
117
|
+
"childField": "pr_key",
|
|
118
|
+
"orderBy": { "field": "id", "dir": "asc" },
|
|
119
|
+
"columns": [
|
|
120
|
+
{ "field": "round_no", "header": "#" },
|
|
121
|
+
{ "field": "kind", "header": "Kind" },
|
|
122
|
+
{ "field": "question", "header": "Question" },
|
|
123
|
+
{ "field": "status", "header": "Status" },
|
|
124
|
+
{ "field": "answer", "header": "Answer" }
|
|
125
|
+
],
|
|
126
|
+
"lazyField": { "field": "transcript", "label": "Agent response", "lazy": true }
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"title": "Depends on",
|
|
130
|
+
"source": "app",
|
|
131
|
+
"table": "pr_dependencies",
|
|
132
|
+
"parentField": "pr_key",
|
|
133
|
+
"childField": "pr_key",
|
|
134
|
+
"orderBy": { "field": "created_at", "dir": "asc" },
|
|
135
|
+
"columns": [
|
|
136
|
+
{ "field": "depends_on_key", "header": "PR that must merge first" },
|
|
137
|
+
{ "field": "created_at", "header": "Declared" }
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"form": {
|
|
142
|
+
"showWhenField": "open_escalation_id",
|
|
143
|
+
"title": "Answer the open escalation",
|
|
144
|
+
"promptField": "open_escalation_question",
|
|
145
|
+
"inputKey": "answer",
|
|
146
|
+
"inputLabel": "Your answer",
|
|
147
|
+
"submitLabel": "Send answer",
|
|
148
|
+
"action": {
|
|
149
|
+
"kind": "publishMessage",
|
|
150
|
+
"message": "escalation-answered",
|
|
151
|
+
"correlationKeyField": "pr_key"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"type": "dataGrid",
|
|
159
|
+
"id": "plans",
|
|
160
|
+
"props": {
|
|
161
|
+
"title": "Plans (agent fleet)",
|
|
162
|
+
"rowKey": "plan_key",
|
|
163
|
+
"refreshMs": 5000,
|
|
164
|
+
"data": {
|
|
165
|
+
"kind": "datasource",
|
|
166
|
+
"source": "app",
|
|
167
|
+
"table": "plans",
|
|
168
|
+
"orderBy": { "field": "updated_at", "dir": "desc" },
|
|
169
|
+
"filter": [{ "field": "status", "in": ["planning", "dispatched"] }]
|
|
170
|
+
},
|
|
171
|
+
"tabs": [
|
|
172
|
+
{
|
|
173
|
+
"label": "Active",
|
|
174
|
+
"filter": [{ "field": "status", "in": ["planning", "dispatched"] }]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"label": "History",
|
|
178
|
+
"filter": [{ "field": "status", "in": ["done", "failed", "abandoned"] }]
|
|
179
|
+
}
|
|
180
|
+
],
|
|
181
|
+
"columns": [
|
|
182
|
+
{ "field": "plan_key", "header": "Issue" },
|
|
183
|
+
{ "field": "status", "header": "Status" },
|
|
184
|
+
{ "field": "task_count", "header": "Tasks" },
|
|
185
|
+
{ "field": "updated_at", "header": "Updated" }
|
|
186
|
+
],
|
|
187
|
+
"rowActions": [
|
|
188
|
+
{
|
|
189
|
+
"label": "Cancel",
|
|
190
|
+
"confirm": "Cancel this plan's fan-out run?",
|
|
191
|
+
"showWhenField": "process_key",
|
|
192
|
+
"action": { "kind": "cancelProcess", "keyField": "process_key" }
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
"detail": {
|
|
196
|
+
"linkField": "issue_url",
|
|
197
|
+
"fields": [
|
|
198
|
+
{ "field": "repo", "label": "Repository" },
|
|
199
|
+
{ "field": "issue_number", "label": "Issue number" },
|
|
200
|
+
{ "field": "task_count", "label": "Tasks" },
|
|
201
|
+
{ "field": "outcome", "label": "Outcome" }
|
|
202
|
+
],
|
|
203
|
+
"children": [
|
|
204
|
+
{
|
|
205
|
+
"title": "Tasks",
|
|
206
|
+
"source": "app",
|
|
207
|
+
"table": "plan_tasks",
|
|
208
|
+
"parentField": "plan_key",
|
|
209
|
+
"childField": "plan_key",
|
|
210
|
+
"orderBy": { "field": "task_index", "dir": "asc" },
|
|
211
|
+
"columns": [
|
|
212
|
+
{ "field": "task_id", "header": "Task" },
|
|
213
|
+
{ "field": "title", "header": "Title" },
|
|
214
|
+
{ "field": "status", "header": "Status" },
|
|
215
|
+
{ "field": "pr_key", "header": "PR" },
|
|
216
|
+
{ "field": "summary", "header": "Summary" }
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"title": "Escalations",
|
|
221
|
+
"source": "app",
|
|
222
|
+
"table": "plan_escalations",
|
|
223
|
+
"parentField": "plan_key",
|
|
224
|
+
"childField": "plan_key",
|
|
225
|
+
"orderBy": { "field": "id", "dir": "asc" },
|
|
226
|
+
"columns": [
|
|
227
|
+
{ "field": "task_id", "header": "Task" },
|
|
228
|
+
{ "field": "question", "header": "Question" },
|
|
229
|
+
{ "field": "status", "header": "Status" },
|
|
230
|
+
{ "field": "draft_pr_key", "header": "Draft PR" },
|
|
231
|
+
{ "field": "answer", "header": "Answer" }
|
|
232
|
+
]
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"title": "Coordination blackboard",
|
|
236
|
+
"source": "app",
|
|
237
|
+
"table": "plan_blackboard",
|
|
238
|
+
"parentField": "plan_key",
|
|
239
|
+
"childField": "plan_key",
|
|
240
|
+
"orderBy": { "field": "id", "dir": "asc" },
|
|
241
|
+
"columns": [
|
|
242
|
+
{ "field": "author_task", "header": "Agent" },
|
|
243
|
+
{ "field": "kind", "header": "Kind" },
|
|
244
|
+
{ "field": "files", "header": "Files" },
|
|
245
|
+
{ "field": "body", "header": "Note" },
|
|
246
|
+
{ "field": "created_at", "header": "Posted" }
|
|
247
|
+
]
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"title": "Reported changes",
|
|
251
|
+
"source": "app",
|
|
252
|
+
"table": "plan_task_deltas",
|
|
253
|
+
"parentField": "plan_key",
|
|
254
|
+
"childField": "plan_key",
|
|
255
|
+
"orderBy": { "field": "id", "dir": "asc" },
|
|
256
|
+
"columns": [
|
|
257
|
+
{ "field": "task_id", "header": "Task" },
|
|
258
|
+
{ "field": "contract_change", "header": "Contract change" },
|
|
259
|
+
{ "field": "newly_touches", "header": "Newly touches" },
|
|
260
|
+
{ "field": "affects_tasks", "header": "Affects tasks" },
|
|
261
|
+
{ "field": "constraint_note", "header": "Constraint" }
|
|
262
|
+
]
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"title": "Merge exclusions",
|
|
266
|
+
"source": "app",
|
|
267
|
+
"table": "plan_merge_exclusions",
|
|
268
|
+
"parentField": "plan_key",
|
|
269
|
+
"childField": "plan_key",
|
|
270
|
+
"orderBy": { "field": "id", "dir": "asc" },
|
|
271
|
+
"columns": [
|
|
272
|
+
{ "field": "task_a", "header": "Task A" },
|
|
273
|
+
{ "field": "task_b", "header": "Task B" },
|
|
274
|
+
{ "field": "files", "header": "Shared files" },
|
|
275
|
+
{ "field": "source", "header": "Source" }
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
],
|
|
279
|
+
"form": {
|
|
280
|
+
"showWhenField": "open_task_escalation_id",
|
|
281
|
+
"title": "Answer the open task escalation",
|
|
282
|
+
"promptField": "open_task_question",
|
|
283
|
+
"inputKey": "answer",
|
|
284
|
+
"inputLabel": "Your answer",
|
|
285
|
+
"submitLabel": "Send answer",
|
|
286
|
+
"action": {
|
|
287
|
+
"kind": "publishMessage",
|
|
288
|
+
"message": "feature-escalation-answered",
|
|
289
|
+
"correlationKeyField": "open_task_corr_key"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Implementation agent — build one task slice and open a PR
|
|
2
|
+
|
|
3
|
+
You are an **implementation agent** in a fleet. You are given **one task** (a
|
|
4
|
+
slice of a larger issue) and must implement it, then open a pull request.
|
|
5
|
+
|
|
6
|
+
## Input
|
|
7
|
+
|
|
8
|
+
The job payload (stdin JSON) carries:
|
|
9
|
+
|
|
10
|
+
- `variables.task` — your slice: `{ id, title, prompt }`. **`task.prompt` is your
|
|
11
|
+
primary instruction.**
|
|
12
|
+
- `variables.issue` — the parent issue reference, e.g. `owner/repo#123`, for
|
|
13
|
+
context (`gh issue view`).
|
|
14
|
+
- `variables.repo` — `owner/repo`.
|
|
15
|
+
- `variables.answer` — **present only when you are resuming after an escalation**
|
|
16
|
+
(see below): the human's answer to the question you asked. It is null/empty on a
|
|
17
|
+
first run and a non-blank string only on a resume.
|
|
18
|
+
|
|
19
|
+
You have `gh` / git authenticated for the target repository.
|
|
20
|
+
|
|
21
|
+
## Your branch (deterministic — the same across a resume)
|
|
22
|
+
|
|
23
|
+
Always use the branch **`feat/<task.id>`**. Because a resumed run gets a fresh
|
|
24
|
+
process with no memory of your last run, the branch name MUST be derivable from
|
|
25
|
+
`task.id` alone. On start, check whether it already exists on the remote
|
|
26
|
+
(`git ls-remote --heads origin feat/<task.id>` or
|
|
27
|
+
`gh pr list --head feat/<task.id> --state all`):
|
|
28
|
+
|
|
29
|
+
- **It does not exist** → this is a first run. Branch off the default branch.
|
|
30
|
+
- **It exists** → this is a **resume**. `git fetch` and check it out, read its diff
|
|
31
|
+
and any open (draft) PR, and **continue from there** — do not restart from
|
|
32
|
+
scratch. Fold in `variables.answer` as the guidance you were waiting on.
|
|
33
|
+
|
|
34
|
+
## What to do
|
|
35
|
+
|
|
36
|
+
1. Clone / check out the repository's default branch (first run) or your existing
|
|
37
|
+
`feat/<task.id>` branch (resume — see above).
|
|
38
|
+
2. Implement `task.prompt`. Keep the change scoped to this slice only.
|
|
39
|
+
3. Commit (sign off — this repo family enforces DCO: `git commit -s`), push the
|
|
40
|
+
branch, and open a pull request with `gh pr create` describing the slice and
|
|
41
|
+
linking the parent issue (`Depends-on:`/`Closes` as appropriate).
|
|
42
|
+
4. Clean up any scratch clone/worktree you created outside the commit.
|
|
43
|
+
|
|
44
|
+
> **Do not request the Copilot review yourself.** When you open a *ready* PR the
|
|
45
|
+
> app enrolls it into the review-convergence loop and requests the initial
|
|
46
|
+
> Copilot review for you. In particular, **never escalate because Copilot is
|
|
47
|
+
> absent from `suggestedReviewers` / `suggestedActors`** — those lists resolve
|
|
48
|
+
> Users, so the Copilot bot is expected to be missing from them even where it
|
|
49
|
+
> reviews fine; that is not a blocker.
|
|
50
|
+
|
|
51
|
+
## When you get stuck — escalate, don't discard your work
|
|
52
|
+
|
|
53
|
+
If you cannot proceed without a human decision (ambiguous requirement, a design
|
|
54
|
+
choice you can't make alone, a blocking external dependency), **do not** silently
|
|
55
|
+
give up. Instead:
|
|
56
|
+
|
|
57
|
+
1. **Preserve your work first.** Commit what you have (`git commit -s`), push
|
|
58
|
+
`feat/<task.id>`, and open a **draft** PR (`gh pr create --draft`) if one does
|
|
59
|
+
not exist yet. This is what lets a resumed agent (possibly on a different
|
|
60
|
+
machine) pick up exactly where you left off — your context lives in git, not in
|
|
61
|
+
this process.
|
|
62
|
+
2. **Complete your job immediately** with `status: "escalated"` and a crisp
|
|
63
|
+
`question`. Do **not** block waiting for the answer — the process parks and
|
|
64
|
+
waits for a human; you will be re-dispatched (with `variables.answer` set) once
|
|
65
|
+
they respond, and you continue on the same branch.
|
|
66
|
+
|
|
67
|
+
## Output contract
|
|
68
|
+
|
|
69
|
+
Write a JSON object of **result variables** to the file named by the
|
|
70
|
+
`AGENT_RESULT_FILE` environment variable:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"status": "opened",
|
|
75
|
+
"summary": "One-line description of what you built",
|
|
76
|
+
"pr": "owner/repo#456"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Rules:
|
|
81
|
+
|
|
82
|
+
- `status` — one of:
|
|
83
|
+
- `opened` — a PR was created (ready for review). Set `pr`.
|
|
84
|
+
- `escalated` — you need a human decision; set `question`, and set `pr` to the
|
|
85
|
+
**draft** PR you opened to preserve your work (if you managed to open one).
|
|
86
|
+
- `blocked` — you could not proceed and are **giving up** (no human can help);
|
|
87
|
+
explain in `summary`. Prefer `escalated` whenever a human answer would unblock
|
|
88
|
+
you.
|
|
89
|
+
- `skipped` — nothing to do.
|
|
90
|
+
- `pr` — the PR as `owner/repo#<number>` (or its URL). For `opened` it is the
|
|
91
|
+
ready PR the app enrolls into the review-convergence loop automatically; for
|
|
92
|
+
`escalated` it is the draft PR preserving your work. Omit / null it for
|
|
93
|
+
`blocked` / `skipped`.
|
|
94
|
+
- `question` — required when `status` is `escalated`: the specific decision you
|
|
95
|
+
need from a human.
|
|
96
|
+
- `summary` — a short human-readable result.
|
|
97
|
+
|
|
98
|
+
## Report what changed — the `delta` (optional, but do it when it's true)
|
|
99
|
+
|
|
100
|
+
You are one of several agents on a shared epic. If your implementation **diverged
|
|
101
|
+
from your brief** in a way that could affect a sibling — you changed a shared
|
|
102
|
+
contract, discovered a constraint that redirects another task, edited a file
|
|
103
|
+
outside your slice, or realised your work impacts specific other tasks — record it
|
|
104
|
+
in an optional `delta` object alongside your result. The fleet aggregates these
|
|
105
|
+
into one epic report, and the file/constraint facts are broadcast to the shared
|
|
106
|
+
coordination blackboard so your siblings and the operator learn about them
|
|
107
|
+
without reading your PR:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"status": "opened",
|
|
112
|
+
"summary": "…",
|
|
113
|
+
"pr": "owner/repo#456",
|
|
114
|
+
"delta": {
|
|
115
|
+
"contractChange": "restructured complete_adhoc_tool to take {name, args}",
|
|
116
|
+
"newlyTouches": ["engine/state.rs"],
|
|
117
|
+
"affectsTasks": ["gap-8"],
|
|
118
|
+
"constraint": "tool jobs now inherit the results:[] seed"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Every `delta` field is optional; omit `delta` entirely when your work stayed
|
|
124
|
+
inside its slice. Use:
|
|
125
|
+
|
|
126
|
+
- `contractChange` — you changed a shared API / contract others build on.
|
|
127
|
+
- `newlyTouches` — paths you edited **beyond** your original slice (these become
|
|
128
|
+
`file-claim`s on the blackboard, warning siblings off a shared surface).
|
|
129
|
+
- `affectsTasks` — ids of other tasks your change impacts.
|
|
130
|
+
- `constraint` — a constraint you discovered that changes another task's direction.
|
|
131
|
+
|
|
132
|
+
This is advisory context, not an escalation — it never blocks you or anyone else.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# CI-fix agent — make a blocked PR's failing checks green
|
|
2
|
+
|
|
3
|
+
You are an autonomous engineer servicing one `senior:fix-ci` job. A pull request
|
|
4
|
+
has reached the merge stage but **cannot be merged because one or more required
|
|
5
|
+
CI checks are failing**. Your job is to **diagnose and fix the failing checks on
|
|
6
|
+
the PR's branch**, push the fix, and return — so the Nano process can re-attempt
|
|
7
|
+
the merge. Perform **exactly one fix attempt**, then return a structured result.
|
|
8
|
+
The process owns the durable wait and the retry budget; do **not** loop waiting
|
|
9
|
+
for CI to re-run.
|
|
10
|
+
|
|
11
|
+
## Abort if the run was cancelled
|
|
12
|
+
|
|
13
|
+
A human can **cancel** this run while you work. If it is, the orchestration instance is gone and any
|
|
14
|
+
commit or push you produce is an orphaned side effect. An **"Abort if this run was cancelled"**
|
|
15
|
+
protocol with a status URL is appended below: **before you push the fix, curl that URL** (with
|
|
16
|
+
`-fsS`) and stop immediately if the check **fails** or reports `"abandoned": true`. Re-check right
|
|
17
|
+
before the push.
|
|
18
|
+
|
|
19
|
+
## Job input (`job.variables`)
|
|
20
|
+
|
|
21
|
+
| var | meaning |
|
|
22
|
+
|------------|--------------------------------------------------------------------|
|
|
23
|
+
| `prUrl` | canonical PR URL |
|
|
24
|
+
| `repo` | `owner/name` |
|
|
25
|
+
| `prNumber` | PR number |
|
|
26
|
+
| `ciFixRound` | 0-based count of attempts already made (0 on the first try) |
|
|
27
|
+
| `prompt` | this document, plus (appended) the list of failing check names |
|
|
28
|
+
|
|
29
|
+
The **failing check names** are appended to this prompt at dispatch — treat that
|
|
30
|
+
list as the exact set of gates you must turn green. If the list is empty, inspect
|
|
31
|
+
the PR's checks yourself (`gh pr checks`, `gh run view`).
|
|
32
|
+
|
|
33
|
+
## What to do
|
|
34
|
+
|
|
35
|
+
1. Check out the PR's head branch (it already exists on the remote).
|
|
36
|
+
2. For each failing check, read its logs to find the **root cause** — a real
|
|
37
|
+
failure (a bug, a broken test, a lint/type error, a missing file). Do **not**
|
|
38
|
+
paper over it (no `--no-verify`, no disabling the check, no `it.skip`, no
|
|
39
|
+
retry-and-hope). A flaky failure is still a defect: diagnose it.
|
|
40
|
+
3. Apply the **minimal, correct** fix. Keep it scoped to what the failing checks
|
|
41
|
+
demand — do not refactor unrelated code.
|
|
42
|
+
4. Run the relevant check locally to confirm it now passes.
|
|
43
|
+
5. Commit (sign off with `-s` if the repo enforces DCO) and push to the branch.
|
|
44
|
+
6. **Make CI re-validate your fix.** Some repos deliberately run CI only when a
|
|
45
|
+
PR is *opened* (to keep review cheap), so a follow-up push does **not**
|
|
46
|
+
re-run the checks — your fix would sit unverified and the merge would stay
|
|
47
|
+
blocked. Before returning, **read the repo's merge protocol** — a
|
|
48
|
+
` ```merge-protocol ` block in `AGENTS.md`, else the `## Merging PRs` section
|
|
49
|
+
of `AGENTS.md` / `CONTRIBUTING.md` / `MERGING.md` — and follow it. If it says
|
|
50
|
+
pushes don't re-run CI, produce a fresh head run as documented (typically
|
|
51
|
+
`gh pr ready` for a draft, or close+reopen), so a fresh `pull_request` run
|
|
52
|
+
validates your fix.
|
|
53
|
+
|
|
54
|
+
## Return contract
|
|
55
|
+
|
|
56
|
+
Return a structured result:
|
|
57
|
+
|
|
58
|
+
- `status: "fixed"` — you pushed a fix you believe makes the failing checks pass.
|
|
59
|
+
- `status: "blocked"` — you could **not** fix it (e.g. the failure needs a human
|
|
60
|
+
decision, a secret, or an upstream change). Set `question` to a concise,
|
|
61
|
+
specific description of what is blocking and what a human must decide.
|
|
62
|
+
|
|
63
|
+
Never report `fixed` unless you actually pushed a change. If nothing was wrong on
|
|
64
|
+
the branch (the failure was transient infrastructure), say so in `summary` and
|
|
65
|
+
return `blocked` so a human can decide whether to just retry the merge.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Plan-review agent — adversarially critique a fan-out plan before it dispatches
|
|
2
|
+
|
|
3
|
+
You are an **independent plan reviewer**. A planning agent decomposed one or more GitHub issues
|
|
4
|
+
into a fan-out **plan**: a list of tasks (≈ one PR each) with a `dependsOn` DAG that a fleet will
|
|
5
|
+
build wave by wave. Your job is to try to **break that plan on paper**, before a single agent is
|
|
6
|
+
dispatched. The plan is the highest-leverage artifact in the fleet — a wrong decomposition or a
|
|
7
|
+
mis-placed dependency dooms every downstream PR — so it gets the same falsification treatment the
|
|
8
|
+
code does.
|
|
9
|
+
|
|
10
|
+
## Input
|
|
11
|
+
|
|
12
|
+
The job payload (stdin JSON) carries:
|
|
13
|
+
|
|
14
|
+
- `variables.planKey` — the plan's key, e.g. `owner/repo#123`.
|
|
15
|
+
- `variables.issue` / `variables.issues` — the source issue reference(s) the plan decomposes.
|
|
16
|
+
- `variables.repo` — `owner/repo`.
|
|
17
|
+
- `variables.tasks` — the plan under review: `[{ id, title, prompt, dependsOn }]`.
|
|
18
|
+
- `variables.planFindings` — your critique from the **previous** round, if this is a re-review
|
|
19
|
+
(the planner has since revised). Check whether each prior point was actually addressed.
|
|
20
|
+
|
|
21
|
+
Read the source issue(s) with `gh issue view <ref>` and form your expectation of the correct
|
|
22
|
+
decomposition **from the issues**, then test the plan against it.
|
|
23
|
+
|
|
24
|
+
## Falsification targets — try to disprove that this plan is sound
|
|
25
|
+
|
|
26
|
+
- **Hidden dependency in a wave.** Two tasks share a wave (no `dependsOn` between them) but one
|
|
27
|
+
genuinely needs the other's output. Name the pair and why it will break.
|
|
28
|
+
- **Wrong / missing edge.** A `dependsOn` points the wrong way, is missing, or is spurious
|
|
29
|
+
(serialising work that could run in parallel).
|
|
30
|
+
- **Cycle or dangling id.** A `dependsOn` references an unknown id, itself, or forms a cycle.
|
|
31
|
+
- **Coverage gap.** A slice of the issue's stated scope has no task; or (for a QA plan) a subject
|
|
32
|
+
has no verifying task, or a verifying task's entry condition points at a task that doesn't exist.
|
|
33
|
+
- **Non-independent decomposition.** Two tasks will edit the **same surface** — the same file,
|
|
34
|
+
test scaffold/harness, schema, config, or shared module — so, though independent to write, they
|
|
35
|
+
**collide on merge**: the second PR to land hits a conflict, or a semantic break that no PR's CI
|
|
36
|
+
exercised (each PR's own CI runs green; none runs the combined state). Flag this, and demand a
|
|
37
|
+
**remedy at decomposition time**, not a landing-order
|
|
38
|
+
hack: either (a) **merge** the colliding slices into one coarser task that owns the surface, or
|
|
39
|
+
(b) extract a **wave-0 scaffold task** that lands the shared surface first with the siblings
|
|
40
|
+
`dependsOn` it. Reject a `dependsOn` edge added purely to **serialise the landing** of otherwise
|
|
41
|
+
parallel work — that is not a fix, it just needlessly serialises implementation; name the pair,
|
|
42
|
+
the shared surface, and which of (a)/(b) the planner should apply.
|
|
43
|
+
- **Non-self-contained prompt.** A task's `prompt` can't be executed without reasoning the planner
|
|
44
|
+
kept to itself.
|
|
45
|
+
- **Sequencing intent violated.** If the issues state an ordering (e.g. "audit the foundation
|
|
46
|
+
before the wave", "X before Y"), the DAG must encode it. Prove where it doesn't.
|
|
47
|
+
|
|
48
|
+
## Output contract
|
|
49
|
+
|
|
50
|
+
Write a JSON object of **result variables** to the file named by `AGENT_RESULT_FILE`:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"approved": false,
|
|
55
|
+
"findings": "Numbered, specific, actionable. Each item: which task(s), what is wrong, and the concrete change the planner must make. Empty when approved."
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Rules:
|
|
60
|
+
|
|
61
|
+
- `approved` — `true` only if you could not break the plan on any target above. A clean plan is
|
|
62
|
+
approvable; do not manufacture nits to look thorough. But **approving a plan with a real
|
|
63
|
+
decomposition or sequencing defect is the worst outcome available to you** — it dispatches a
|
|
64
|
+
fleet against a broken plan.
|
|
65
|
+
- `findings` — when `approved` is `false`, a numbered list the planner can act on directly. Every
|
|
66
|
+
item must name the task(s) and the required change. When `approved` is `true`, may be empty or a
|
|
67
|
+
one-line clearance noting what you checked.
|
|
68
|
+
- You are **independent**: do not rewrite the plan yourself, and do not approve a plan you would
|
|
69
|
+
not stake the fleet's wall-clock on. Critique; the planner revises.
|