@nanobpm/nano-workforce 0.208.2 → 0.208.4
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/AGENTS.md +17 -0
- package/CHANGELOG.md +12 -0
- package/app/contracts.ts +1 -1
- package/app/feature.ts +5 -2
- package/app/implementBranchContract.test.ts +168 -0
- package/app/implementReconcile.ts +8 -2
- package/app/loopTermination.test.ts +412 -0
- package/app/mcpExclusions.test.ts +1 -1
- package/app/mergeBlockedThreads.test.ts +62 -2
- package/app/pullRequestReadModel.test.ts +1 -1
- package/app/repoEnvelope.test.ts +11 -75
- package/app/repoEnvelope.ts +122 -0
- package/app/service.ts +300 -30
- package/app/terminalReaderBehaviour.test.ts +15 -4
- package/build-info.json +4 -4
- package/db/migrations/130_pull_requests_termination_reason.sql +17 -0
- package/db/migrations/131_pull_requests_read_model_termination_reason.sql +48 -0
- package/e2e/feature-preflight.e2e.ts +2 -2
- package/e2e/plan-fanout.e2e.ts +105 -9
- package/nano.app.json +4 -0
- package/openapi.yaml +53 -0
- package/operations/cancelInstance.test.ts +9 -0
- package/operations/cancelInstance.ts +28 -2
- package/operations/cancelPrRun.ts +16 -0
- package/package.json +1 -1
- package/pages/home.page.json +1 -1
- package/resources/processes/implement-cell.bpmn +80 -65
- package/workers/caps-prepare/worker.test.ts +11 -0
- package/workers/slice-branch/worker.test.ts +31 -0
- package/workers/slice-branch/worker.ts +39 -0
|
@@ -17,8 +17,15 @@
|
|
|
17
17
|
</nano:shapes>
|
|
18
18
|
</bpmn:extensionElements>
|
|
19
19
|
<bpmn:startEvent id="Start" name="Task">
|
|
20
|
-
<bpmn:outgoing>
|
|
20
|
+
<bpmn:outgoing>ic_toSliceBranch</bpmn:outgoing>
|
|
21
21
|
</bpmn:startEvent>
|
|
22
|
+
<bpmn:serviceTask id="slice-branch" name="Stamp slice branch">
|
|
23
|
+
<bpmn:extensionElements>
|
|
24
|
+
<zeebe:taskDefinition type="pr.slice-branch" />
|
|
25
|
+
</bpmn:extensionElements>
|
|
26
|
+
<bpmn:incoming>ic_toSliceBranch</bpmn:incoming>
|
|
27
|
+
<bpmn:outgoing>ic_toImplement</bpmn:outgoing>
|
|
28
|
+
</bpmn:serviceTask>
|
|
22
29
|
<bpmn:serviceTask id="implement-task" name="Implement (agent)">
|
|
23
30
|
<bpmn:extensionElements>
|
|
24
31
|
<zeebe:taskDefinition type="senior:feature" />
|
|
@@ -143,7 +150,8 @@
|
|
|
143
150
|
<bpmn:incoming>ic_reconciled</bpmn:incoming>
|
|
144
151
|
<bpmn:incoming>ic_abandon</bpmn:incoming>
|
|
145
152
|
</bpmn:endEvent>
|
|
146
|
-
<bpmn:sequenceFlow id="
|
|
153
|
+
<bpmn:sequenceFlow id="ic_toSliceBranch" sourceRef="Start" targetRef="slice-branch" />
|
|
154
|
+
<bpmn:sequenceFlow id="ic_toImplement" sourceRef="slice-branch" targetRef="implement-task" />
|
|
147
155
|
<bpmn:sequenceFlow id="ic_toGw" sourceRef="implement-task" targetRef="ic_gw" />
|
|
148
156
|
<bpmn:sequenceFlow id="ic_done" name="done" sourceRef="ic_gw" targetRef="ic_end">
|
|
149
157
|
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=status = "opened" or status = "blocked" or status = "skipped"</bpmn:conditionExpression>
|
|
@@ -175,149 +183,156 @@
|
|
|
175
183
|
<dc:Bounds x="81" y="143" width="34" height="14" />
|
|
176
184
|
</bpmndi:BPMNLabel>
|
|
177
185
|
</bpmndi:BPMNShape>
|
|
178
|
-
<bpmndi:BPMNShape id="
|
|
186
|
+
<bpmndi:BPMNShape id="BPMNShape_slice-branch" bpmnElement="slice-branch">
|
|
179
187
|
<dc:Bounds x="216" y="80" width="100" height="80" />
|
|
180
188
|
</bpmndi:BPMNShape>
|
|
189
|
+
<bpmndi:BPMNShape id="BPMNShape_implement-task" bpmnElement="implement-task">
|
|
190
|
+
<dc:Bounds x="416" y="80" width="100" height="80" />
|
|
191
|
+
</bpmndi:BPMNShape>
|
|
181
192
|
<bpmndi:BPMNShape id="BPMNShape_ic_gw" bpmnElement="ic_gw" isMarkerVisible="true">
|
|
182
|
-
<dc:Bounds x="
|
|
193
|
+
<dc:Bounds x="641" y="95" width="50" height="50" />
|
|
183
194
|
<bpmndi:BPMNLabel>
|
|
184
|
-
<dc:Bounds x="
|
|
195
|
+
<dc:Bounds x="631" y="62" width="70" height="28" />
|
|
185
196
|
</bpmndi:BPMNLabel>
|
|
186
197
|
</bpmndi:BPMNShape>
|
|
187
198
|
<bpmndi:BPMNShape id="BPMNShape_reconcile-implement" bpmnElement="reconcile-implement">
|
|
188
|
-
<dc:Bounds x="
|
|
199
|
+
<dc:Bounds x="816" y="80" width="100" height="80" />
|
|
189
200
|
</bpmndi:BPMNShape>
|
|
190
201
|
<bpmndi:BPMNShape id="BPMNShape_ic_reconcile_gw" bpmnElement="ic_reconcile_gw" isMarkerVisible="true">
|
|
191
|
-
<dc:Bounds x="
|
|
202
|
+
<dc:Bounds x="1016" y="95" width="50" height="50" />
|
|
192
203
|
<bpmndi:BPMNLabel>
|
|
193
|
-
<dc:Bounds x="
|
|
204
|
+
<dc:Bounds x="1001" y="76" width="81" height="14" />
|
|
194
205
|
</bpmndi:BPMNLabel>
|
|
195
206
|
</bpmndi:BPMNShape>
|
|
196
207
|
<bpmndi:BPMNShape id="BPMNShape_escalate" bpmnElement="escalate">
|
|
197
|
-
<dc:Bounds x="
|
|
208
|
+
<dc:Bounds x="1366" y="80" width="100" height="80" />
|
|
198
209
|
</bpmndi:BPMNShape>
|
|
199
210
|
<bpmndi:BPMNShape id="BPMNShape_record-escalation" bpmnElement="record-escalation">
|
|
200
|
-
<dc:Bounds x="
|
|
211
|
+
<dc:Bounds x="1166" y="80" width="100" height="80" />
|
|
201
212
|
</bpmndi:BPMNShape>
|
|
202
213
|
<bpmndi:BPMNShape id="BPMNShape_record-escalation-sla" bpmnElement="record-escalation-sla">
|
|
203
|
-
<dc:Bounds x="
|
|
214
|
+
<dc:Bounds x="616" y="400" width="100" height="80" />
|
|
204
215
|
</bpmndi:BPMNShape>
|
|
205
216
|
<bpmndi:BPMNShape id="BPMNShape_ic_gw_answer" bpmnElement="ic_gw_answer" isMarkerVisible="true">
|
|
206
|
-
<dc:Bounds x="
|
|
217
|
+
<dc:Bounds x="1566" y="95" width="50" height="50" />
|
|
207
218
|
<bpmndi:BPMNLabel>
|
|
208
|
-
<dc:Bounds x="
|
|
219
|
+
<dc:Bounds x="1523" y="150" width="56" height="14" />
|
|
209
220
|
</bpmndi:BPMNLabel>
|
|
210
221
|
</bpmndi:BPMNShape>
|
|
211
222
|
<bpmndi:BPMNShape id="BPMNShape_record-implementing" bpmnElement="record-implementing">
|
|
212
|
-
<dc:Bounds x="
|
|
223
|
+
<dc:Bounds x="1716" y="240" width="100" height="80" />
|
|
213
224
|
</bpmndi:BPMNShape>
|
|
214
225
|
<bpmndi:BPMNShape id="BPMNShape_ic_end" bpmnElement="ic_end">
|
|
215
|
-
<dc:Bounds x="
|
|
226
|
+
<dc:Bounds x="1748" y="102" width="36" height="36" />
|
|
216
227
|
<bpmndi:BPMNLabel>
|
|
217
|
-
<dc:Bounds x="
|
|
228
|
+
<dc:Bounds x="1693" y="143" width="66" height="14" />
|
|
218
229
|
</bpmndi:BPMNLabel>
|
|
219
230
|
</bpmndi:BPMNShape>
|
|
220
231
|
<bpmndi:BPMNShape id="BPMNShape_be_implement_sla" bpmnElement="be_implement_sla">
|
|
221
|
-
<dc:Bounds x="
|
|
232
|
+
<dc:Bounds x="448" y="142" width="36" height="36" />
|
|
222
233
|
<bpmndi:BPMNLabel>
|
|
223
|
-
<dc:Bounds x="
|
|
234
|
+
<dc:Bounds x="370" y="183" width="72" height="28" />
|
|
224
235
|
</bpmndi:BPMNLabel>
|
|
225
236
|
</bpmndi:BPMNShape>
|
|
226
|
-
<bpmndi:BPMNEdge id="
|
|
237
|
+
<bpmndi:BPMNEdge id="BPMNEdge_ic_toSliceBranch" bpmnElement="ic_toSliceBranch">
|
|
227
238
|
<di:waypoint x="116" y="120" />
|
|
228
239
|
<di:waypoint x="216" y="120" />
|
|
229
240
|
</bpmndi:BPMNEdge>
|
|
230
|
-
<bpmndi:BPMNEdge id="
|
|
241
|
+
<bpmndi:BPMNEdge id="BPMNEdge_ic_toImplement" bpmnElement="ic_toImplement">
|
|
231
242
|
<di:waypoint x="316" y="120" />
|
|
232
|
-
<di:waypoint x="
|
|
243
|
+
<di:waypoint x="416" y="120" />
|
|
244
|
+
</bpmndi:BPMNEdge>
|
|
245
|
+
<bpmndi:BPMNEdge id="BPMNEdge_ic_toGw" bpmnElement="ic_toGw">
|
|
246
|
+
<di:waypoint x="516" y="120" />
|
|
247
|
+
<di:waypoint x="641" y="120" />
|
|
233
248
|
</bpmndi:BPMNEdge>
|
|
234
249
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_escalate" bpmnElement="ic_escalate">
|
|
235
|
-
<di:waypoint x="
|
|
236
|
-
<di:waypoint x="
|
|
250
|
+
<di:waypoint x="691" y="120" />
|
|
251
|
+
<di:waypoint x="816" y="120" />
|
|
237
252
|
<bpmndi:BPMNLabel>
|
|
238
|
-
<dc:Bounds x="
|
|
253
|
+
<dc:Bounds x="720" y="98" width="67" height="14" />
|
|
239
254
|
</bpmndi:BPMNLabel>
|
|
240
255
|
</bpmndi:BPMNEdge>
|
|
241
256
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_toReconcileGw" bpmnElement="ic_toReconcileGw">
|
|
242
|
-
<di:waypoint x="
|
|
243
|
-
<di:waypoint x="
|
|
257
|
+
<di:waypoint x="916" y="120" />
|
|
258
|
+
<di:waypoint x="1016" y="120" />
|
|
244
259
|
</bpmndi:BPMNEdge>
|
|
245
260
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_toRecordEscalation" bpmnElement="ic_toRecordEscalation">
|
|
246
|
-
<di:waypoint x="
|
|
247
|
-
<di:waypoint x="
|
|
261
|
+
<di:waypoint x="1066" y="120" />
|
|
262
|
+
<di:waypoint x="1166" y="120" />
|
|
248
263
|
<bpmndi:BPMNLabel>
|
|
249
|
-
<dc:Bounds x="
|
|
264
|
+
<dc:Bounds x="1083" y="128" width="67" height="14" />
|
|
250
265
|
</bpmndi:BPMNLabel>
|
|
251
266
|
</bpmndi:BPMNEdge>
|
|
252
267
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_toEscalate" bpmnElement="ic_toEscalate">
|
|
253
|
-
<di:waypoint x="1066" y="120" />
|
|
254
|
-
<di:waypoint x="1166" y="120" />
|
|
255
|
-
</bpmndi:BPMNEdge>
|
|
256
|
-
<bpmndi:BPMNEdge id="BPMNEdge_ic_toAnswerGw" bpmnElement="ic_toAnswerGw">
|
|
257
268
|
<di:waypoint x="1266" y="120" />
|
|
258
269
|
<di:waypoint x="1366" y="120" />
|
|
259
270
|
</bpmndi:BPMNEdge>
|
|
271
|
+
<bpmndi:BPMNEdge id="BPMNEdge_ic_toAnswerGw" bpmnElement="ic_toAnswerGw">
|
|
272
|
+
<di:waypoint x="1466" y="120" />
|
|
273
|
+
<di:waypoint x="1566" y="120" />
|
|
274
|
+
</bpmndi:BPMNEdge>
|
|
260
275
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_abandon" bpmnElement="ic_abandon">
|
|
261
|
-
<di:waypoint x="
|
|
262
|
-
<di:waypoint x="
|
|
276
|
+
<di:waypoint x="1616" y="120" />
|
|
277
|
+
<di:waypoint x="1748" y="120" />
|
|
263
278
|
<bpmndi:BPMNLabel>
|
|
264
|
-
<dc:Bounds x="
|
|
279
|
+
<dc:Bounds x="1656" y="98" width="53" height="14" />
|
|
265
280
|
</bpmndi:BPMNLabel>
|
|
266
281
|
</bpmndi:BPMNEdge>
|
|
267
282
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_retry" bpmnElement="ic_retry">
|
|
268
|
-
<di:waypoint x="
|
|
269
|
-
<di:waypoint x="
|
|
270
|
-
<di:waypoint x="
|
|
283
|
+
<di:waypoint x="1041" y="145" />
|
|
284
|
+
<di:waypoint x="1041" y="280" />
|
|
285
|
+
<di:waypoint x="1716" y="280" />
|
|
271
286
|
<bpmndi:BPMNLabel>
|
|
272
|
-
<dc:Bounds x="
|
|
287
|
+
<dc:Bounds x="1046" y="223" width="39" height="14" />
|
|
273
288
|
</bpmndi:BPMNLabel>
|
|
274
289
|
</bpmndi:BPMNEdge>
|
|
275
290
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_answerLoop" bpmnElement="ic_answerLoop">
|
|
276
|
-
<di:waypoint x="
|
|
277
|
-
<di:waypoint x="
|
|
278
|
-
<di:waypoint x="
|
|
291
|
+
<di:waypoint x="1591" y="145" />
|
|
292
|
+
<di:waypoint x="1591" y="280" />
|
|
293
|
+
<di:waypoint x="1716" y="280" />
|
|
279
294
|
<bpmndi:BPMNLabel>
|
|
280
|
-
<dc:Bounds x="
|
|
295
|
+
<dc:Bounds x="1596" y="206" width="49" height="14" />
|
|
281
296
|
</bpmndi:BPMNLabel>
|
|
282
297
|
</bpmndi:BPMNEdge>
|
|
283
298
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_done" bpmnElement="ic_done">
|
|
284
|
-
<di:waypoint x="
|
|
285
|
-
<di:waypoint x="
|
|
286
|
-
<di:waypoint x="
|
|
287
|
-
<di:waypoint x="
|
|
299
|
+
<di:waypoint x="666" y="145" />
|
|
300
|
+
<di:waypoint x="666" y="200" />
|
|
301
|
+
<di:waypoint x="1766" y="200" />
|
|
302
|
+
<di:waypoint x="1766" y="138" />
|
|
288
303
|
<bpmndi:BPMNLabel>
|
|
289
|
-
<dc:Bounds x="
|
|
304
|
+
<dc:Bounds x="1200" y="208" width="32" height="14" />
|
|
290
305
|
</bpmndi:BPMNLabel>
|
|
291
306
|
</bpmndi:BPMNEdge>
|
|
292
307
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_reconciled" bpmnElement="ic_reconciled">
|
|
293
|
-
<di:waypoint x="
|
|
294
|
-
<di:waypoint x="
|
|
295
|
-
<di:waypoint x="
|
|
296
|
-
<di:waypoint x="
|
|
308
|
+
<di:waypoint x="1041" y="145" />
|
|
309
|
+
<di:waypoint x="1041" y="180" />
|
|
310
|
+
<di:waypoint x="1766" y="180" />
|
|
311
|
+
<di:waypoint x="1766" y="138" />
|
|
297
312
|
<bpmndi:BPMNLabel>
|
|
298
|
-
<dc:Bounds x="
|
|
313
|
+
<dc:Bounds x="1297" y="158" width="53" height="14" />
|
|
299
314
|
</bpmndi:BPMNLabel>
|
|
300
315
|
</bpmndi:BPMNEdge>
|
|
301
316
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_slaToEscalate" bpmnElement="ic_slaToEscalate">
|
|
302
|
-
<di:waypoint x="
|
|
303
|
-
<di:waypoint x="
|
|
304
|
-
<di:waypoint x="
|
|
305
|
-
<di:waypoint x="
|
|
317
|
+
<di:waypoint x="716" y="440" />
|
|
318
|
+
<di:waypoint x="1836" y="440" />
|
|
319
|
+
<di:waypoint x="1836" y="82" />
|
|
320
|
+
<di:waypoint x="1466" y="82" />
|
|
306
321
|
</bpmndi:BPMNEdge>
|
|
307
322
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_sla" bpmnElement="ic_sla">
|
|
308
|
-
<di:waypoint x="
|
|
309
|
-
<di:waypoint x="
|
|
310
|
-
<di:waypoint x="
|
|
323
|
+
<di:waypoint x="466" y="178" />
|
|
324
|
+
<di:waypoint x="466" y="440" />
|
|
325
|
+
<di:waypoint x="616" y="440" />
|
|
311
326
|
<bpmndi:BPMNLabel>
|
|
312
|
-
<dc:Bounds x="
|
|
327
|
+
<dc:Bounds x="471" y="295" width="70" height="28" />
|
|
313
328
|
</bpmndi:BPMNLabel>
|
|
314
329
|
</bpmndi:BPMNEdge>
|
|
315
330
|
<bpmndi:BPMNEdge id="BPMNEdge_ic_reImplement" bpmnElement="ic_reImplement">
|
|
316
|
-
<di:waypoint x="
|
|
331
|
+
<di:waypoint x="1716" y="280" />
|
|
317
332
|
<di:waypoint x="60" y="280" />
|
|
318
333
|
<di:waypoint x="60" y="60" />
|
|
319
|
-
<di:waypoint x="
|
|
320
|
-
<di:waypoint x="
|
|
334
|
+
<di:waypoint x="466" y="60" />
|
|
335
|
+
<di:waypoint x="466" y="80" />
|
|
321
336
|
</bpmndi:BPMNEdge>
|
|
322
337
|
</bpmndi:BPMNPlane>
|
|
323
338
|
</bpmndi:BPMNDiagram>
|
|
@@ -60,3 +60,14 @@ test("caps-prepare: a malformed need is dropped — only a valid remainder gates
|
|
|
60
60
|
);
|
|
61
61
|
assertEquals(mixed.hasNeeds, true, "a valid need still gates even alongside a malformed one");
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
// Issue #945: caps-prepare runs in the plan-fanout MI body, whose un-declared result keys land on the
|
|
65
|
+
// SHARED epic root — so it must NOT stamp the per-slice branch.create (a sibling would overwrite it).
|
|
66
|
+
// The emitter lives in each implement-cell instance (`pr.slice-branch`) instead.
|
|
67
|
+
test("caps-prepare: does NOT emit the per-slice branch.create (it would leak across MI siblings) (#945)", async () => {
|
|
68
|
+
const out = await handler(
|
|
69
|
+
{ variables: { planKey: "owner/repo#7", taskId: "t1", needs: [] } } as any,
|
|
70
|
+
{} as any,
|
|
71
|
+
);
|
|
72
|
+
assertEquals(Object.keys(out).filter((k) => k.startsWith("io.nanobpm.agentTask")), []);
|
|
73
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Unit coverage for `pr.slice-branch` (issue #945): the per-slice deterministic `branch.create`,
|
|
2
|
+
// emitted as the FLAT dotted variable the c8ctl harness reads, derived from the ONE canonical
|
|
3
|
+
// `sliceBranchCreate`.
|
|
4
|
+
import { test } from "node:test";
|
|
5
|
+
import { assertEquals } from "#test-assert";
|
|
6
|
+
import { AGENT_TASK_BRANCH_CREATE_VAR, sliceBranchCreate } from "../../app/repoEnvelope.ts";
|
|
7
|
+
import handler from "./worker.ts";
|
|
8
|
+
|
|
9
|
+
async function run(task: unknown): Promise<Record<string, unknown>> {
|
|
10
|
+
return (await handler({ variables: { task } } as any, {} as any)) ?? {};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
test("slice-branch: stamps feat/<task.id> as the flat dotted branch.create (#945)", async () => {
|
|
14
|
+
const out = await run({ id: "t1", prompt: "x" });
|
|
15
|
+
assertEquals(out[AGENT_TASK_BRANCH_CREATE_VAR], "feat/t1");
|
|
16
|
+
assertEquals(out[AGENT_TASK_BRANCH_CREATE_VAR], sliceBranchCreate("t1"));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("slice-branch: distinct slices derive distinct branches (sibling isolation is per-instance) (#945)", async () => {
|
|
20
|
+
const a = await run({ id: "t1" });
|
|
21
|
+
const b = await run({ id: "t2" });
|
|
22
|
+
assertEquals(a[AGENT_TASK_BRANCH_CREATE_VAR], "feat/t1");
|
|
23
|
+
assertEquals(b[AGENT_TASK_BRANCH_CREATE_VAR], "feat/t2");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("slice-branch: a missing/blank/implausible/non-string id emits nothing (#945)", async () => {
|
|
27
|
+
for (const task of [undefined, null, "t1", [], {}, { id: "" }, { id: " " }, { id: "bad..id" }, { id: 7 }]) {
|
|
28
|
+
const out = await run(task);
|
|
29
|
+
assertEquals(AGENT_TASK_BRANCH_CREATE_VAR in out, false, `no branch.create for ${JSON.stringify(task)}`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// pr.slice-branch — stamp the implement-cell agent job's deterministic per-slice `branch.create`
|
|
2
|
+
// (issue #945).
|
|
3
|
+
//
|
|
4
|
+
// Runs as the FIRST step of the `implement-cell` process, i.e. inside each slice's OWN called process
|
|
5
|
+
// instance. That is what makes the value child-local: a worker result key is propagated to the root
|
|
6
|
+
// of the process instance the job belongs to, and every implement-cell call is a separate instance —
|
|
7
|
+
// so sibling slices of one plan-fanout wave can never overwrite one another's branch (a key emitted
|
|
8
|
+
// from the plan-fanout MI body would land on the shared epic root, where a sibling's prepare could
|
|
9
|
+
// clobber it while this slice waits at its capability barrier).
|
|
10
|
+
//
|
|
11
|
+
// The value rides a result key literally NAMED `io.nanobpm.agentTask.branch.create`, which the engine
|
|
12
|
+
// materialises as a FLAT dotted process variable the c8ctl harness's `collectEnvelopeFrom` deep-merges
|
|
13
|
+
// with the whole-object seed envelope. A `<zeebe:ioMapping>` dotted target canNOT do this (it builds a
|
|
14
|
+
// nested object the harness never reads). It is derived via the ONE canonical `sliceBranchCreate`, so
|
|
15
|
+
// the envelope, the prompt's `feat/<task.id>` instruction, and the reconcile lookup share one source;
|
|
16
|
+
// a retry (record-implementing → implement-task) stays in the same instance and re-reads the SAME
|
|
17
|
+
// value, so the durable WIP checkpoint restore matches.
|
|
18
|
+
//
|
|
19
|
+
// PURE: no I/O. An implausible/missing `task.id` emits nothing (degrades to the agent cutting its own
|
|
20
|
+
// branch) rather than an unusable branch.create.
|
|
21
|
+
import type { AppJobHandler } from "@nanobpm/urban";
|
|
22
|
+
import { AGENT_TASK_BRANCH_CREATE_VAR, sliceBranchCreate } from "../../app/repoEnvelope.ts";
|
|
23
|
+
|
|
24
|
+
interface In {
|
|
25
|
+
task?: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function sliceTaskId(task: unknown): string | null {
|
|
29
|
+
if (task === null || typeof task !== "object" || Array.isArray(task)) return null;
|
|
30
|
+
const id: unknown = Reflect.get(task, "id");
|
|
31
|
+
return typeof id === "string" ? id : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const handler: AppJobHandler<In, Record<string, unknown>> = async (job) => {
|
|
35
|
+
const branchCreate = sliceBranchCreate(sliceTaskId(job.variables.task));
|
|
36
|
+
return branchCreate !== null ? { [AGENT_TASK_BRANCH_CREATE_VAR]: branchCreate } : {};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default handler;
|