@nanobpm/nano-workforce 0.163.3 → 0.164.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/CHANGELOG.md +6 -0
- package/app/contracts.ts +15 -0
- package/app/mergeLoopBehaviour.test.ts +111 -0
- package/app/mergeableWait.test.ts +39 -0
- package/app/mergeableWait.ts +38 -0
- package/app/service.ts +26 -0
- package/nano.app.json +4 -0
- package/package.json +1 -1
- package/resources/processes/merge-loop.bpmn +339 -217
- package/workers/merge-stall-probe/worker.ts +66 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [0.164.0](https://github.com/nanobpm/nano-workforce/compare/v0.163.3...v0.164.0) (2026-08-31)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **merge-loop:** bound wait-mergeable with a timeout + stall probe ([#636](https://github.com/nanobpm/nano-workforce/issues/636)) ([#640](https://github.com/nanobpm/nano-workforce/issues/640)) ([e12dd04](https://github.com/nanobpm/nano-workforce/commit/e12dd0426d0c7b3defe24d339502397b24b9302c))
|
|
6
|
+
|
|
1
7
|
## [0.163.3](https://github.com/nanobpm/nano-workforce/compare/v0.163.2...v0.163.3) (2026-08-31)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
package/app/contracts.ts
CHANGED
|
@@ -124,6 +124,14 @@ export const ENV_CONTRACTS = {
|
|
|
124
124
|
semantics: "Maximum transient base/head-moved merge-race retries per PR before escalating.",
|
|
125
125
|
default: "5",
|
|
126
126
|
},
|
|
127
|
+
NANO_PR_MAX_MERGE_STALL_ROUNDS: {
|
|
128
|
+
category: "env",
|
|
129
|
+
name: "NANO_PR_MAX_MERGE_STALL_ROUNDS",
|
|
130
|
+
owner: "app/service.ts",
|
|
131
|
+
semantics:
|
|
132
|
+
"Maximum mergeable-wait-timeout stall-probe re-derivations (dead-poller backstop, #636) before escalating; 0 escalates on the first stall.",
|
|
133
|
+
default: "3",
|
|
134
|
+
},
|
|
127
135
|
NANO_PR_REVIEW_WAIT_TIMEOUT: {
|
|
128
136
|
category: "env",
|
|
129
137
|
name: "NANO_PR_REVIEW_WAIT_TIMEOUT",
|
|
@@ -143,6 +151,13 @@ export const ENV_CONTRACTS = {
|
|
|
143
151
|
semantics:
|
|
144
152
|
"How long the merge loop waits for a queued PR to actually land before escalating (FEEL/ISO-8601 duration).",
|
|
145
153
|
},
|
|
154
|
+
NANO_PR_MERGEABLE_WAIT_TIMEOUT: {
|
|
155
|
+
category: "env",
|
|
156
|
+
name: "NANO_PR_MERGEABLE_WAIT_TIMEOUT",
|
|
157
|
+
owner: "app/service.ts",
|
|
158
|
+
semantics:
|
|
159
|
+
"How long the merge loop waits for the poller's `merge-ready` before the stall-probe timer arm fires (dead-poller backstop, #636; FEEL/ISO-8601 duration).",
|
|
160
|
+
},
|
|
146
161
|
NANO_PR_AUTO_MERGE: {
|
|
147
162
|
category: "env",
|
|
148
163
|
name: "NANO_PR_AUTO_MERGE",
|
|
@@ -42,6 +42,7 @@ const MODEL = readFileSync("resources/processes/merge-loop.bpmn", "utf8");
|
|
|
42
42
|
|
|
43
43
|
const AGENT_SLA_MS = 30 * 60 * 1000; // matches the PT30M we start instances with
|
|
44
44
|
const LANDED_WAIT_MS = 30 * 60 * 1000; // matches the PT30M landedWaitTimeout we start instances with
|
|
45
|
+
const MERGEABLE_WAIT_MS = 30 * 60 * 1000; // matches the PT30M mergeableWaitTimeout we start instances with
|
|
45
46
|
|
|
46
47
|
type Output = Record<string, unknown>;
|
|
47
48
|
type Responder = Output | Output[] | ((job: { variables: Record<string, unknown> }) => Output);
|
|
@@ -49,6 +50,7 @@ type Responder = Output | Output[] | ((job: { variables: Record<string, unknown>
|
|
|
49
50
|
const ALL_JOB_TYPES = [
|
|
50
51
|
"pr.arm-merge",
|
|
51
52
|
"pr.merge",
|
|
53
|
+
"pr.merge-stall-probe",
|
|
52
54
|
"pr.mark-merged",
|
|
53
55
|
"senior:fix-ci",
|
|
54
56
|
"senior:rebase",
|
|
@@ -82,6 +84,9 @@ const DEFAULT_VARS: Record<string, unknown> = {
|
|
|
82
84
|
mergeRetryRound: 0,
|
|
83
85
|
agentSlaTimeout: "PT30M",
|
|
84
86
|
landedWaitTimeout: "PT30M",
|
|
87
|
+
mergeableWaitTimeout: "PT30M",
|
|
88
|
+
mergeStallRounds: 0,
|
|
89
|
+
mergeStallMax: 3,
|
|
85
90
|
abandonBrief: null,
|
|
86
91
|
failingChecksList: null,
|
|
87
92
|
status: null,
|
|
@@ -238,6 +243,112 @@ test("answering the landing-timeout escalation re-arms the merge poller (#556)",
|
|
|
238
243
|
assertThatInstance(engine, byProcessId("merge-loop")).isActive().hasActiveElement("wait-mergeable");
|
|
239
244
|
});
|
|
240
245
|
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// Mergeable-wait bounded-wait backstop (#636) — a stalled poller must never
|
|
248
|
+
// wedge the instance at `waiting_merge` forever. The `gw-merge-wait` event-based
|
|
249
|
+
// gateway races the poller's `merge-ready` message against a `mergeableWaitTimeout`
|
|
250
|
+
// timer; on timeout `merge-stall-probe` re-derives mergeability and the existing
|
|
251
|
+
// `gw-mergeable` routes to the correct arm — or, once `mergeStallMax` is exhausted,
|
|
252
|
+
// to human escalation.
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
test("wait-mergeable races merge-ready against a timer (no bare catch) (#636)", async () => {
|
|
256
|
+
// After the poller arms the merge, the token forks the event-based gateway: it parks at BOTH the
|
|
257
|
+
// `merge-ready` catch and the timeout timer. Before the fix, `wait-mergeable` was a bare catch —
|
|
258
|
+
// no timer sibling — so a dead poller wedged it forever.
|
|
259
|
+
const engine = await boot();
|
|
260
|
+
await engine.publishMessage({ name: "deps-cleared", correlationKey: "pr-1" });
|
|
261
|
+
assertThatInstance(engine, byProcessId("merge-loop"))
|
|
262
|
+
.isActive()
|
|
263
|
+
.hasActiveElements("wait-mergeable", "wait-mergeable-timeout");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("a dead poller's PR is probed on timeout and advances to merge — never wedges (#636)", async () => {
|
|
267
|
+
// No `merge-ready` is EVER published (the poller is dead). RED (before the fix): the instance sits
|
|
268
|
+
// at `wait-mergeable` forever. GREEN: the timer fires, `merge-stall-probe` re-derives `ready`, and
|
|
269
|
+
// the existing `ready` arm merges the PR.
|
|
270
|
+
const engine = await boot({
|
|
271
|
+
responses: {
|
|
272
|
+
"pr.merge-stall-probe": { mergeState: "ready", failingChecks: 0, failingChecksList: "" },
|
|
273
|
+
"pr.merge": { mergeStatus: "merged" },
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
await engine.publishMessage({ name: "deps-cleared", correlationKey: "pr-1" });
|
|
277
|
+
assertThatInstance(engine, byProcessId("merge-loop")).isActive().hasActiveElement("wait-mergeable");
|
|
278
|
+
await engine.advanceTime(MERGEABLE_WAIT_MS + 1);
|
|
279
|
+
assertThatInstance(engine, byProcessId("merge-loop"))
|
|
280
|
+
.hasCompleted()
|
|
281
|
+
.hasNoIncident()
|
|
282
|
+
.hasCompletedElements("merge-stall-probe", "attempt-merge", "mark-merged");
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("a CONFLICTING PR whose poller never signalled reaches the rebase arm via the timeout path (#636)", async () => {
|
|
286
|
+
// The incident that motivated #636: a `CONFLICTING`/`DIRTY` PR (base advanced under it) parked at
|
|
287
|
+
// `waiting_merge` because the poller never signalled — the auto-rebase arm was never reached. The
|
|
288
|
+
// timer path re-derives `conflict` and routes it to the bounded rebase agent.
|
|
289
|
+
const engine = await boot({
|
|
290
|
+
responses: {
|
|
291
|
+
"pr.merge-stall-probe": { mergeState: "conflict", failingChecks: 0, failingChecksList: "" },
|
|
292
|
+
"senior:rebase": { status: "rebased" },
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
await engine.publishMessage({ name: "deps-cleared", correlationKey: "pr-1" });
|
|
296
|
+
await engine.advanceTime(MERGEABLE_WAIT_MS + 1);
|
|
297
|
+
assertThatInstance(engine, byProcessId("merge-loop"))
|
|
298
|
+
.isActive()
|
|
299
|
+
.hasActiveElement("wait-mergeable")
|
|
300
|
+
.hasCompletedElements("merge-stall-probe", "rebase") // reached the rebase arm via the timeout path
|
|
301
|
+
.hasVariable("rebaseRound", 1);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("a blocked PR probed on timeout reaches the CI-fix arm via the timeout path (#636)", async () => {
|
|
305
|
+
const engine = await boot({
|
|
306
|
+
responses: {
|
|
307
|
+
"pr.merge-stall-probe": { mergeState: "blocked", failingChecks: 1, failingChecksList: "build" },
|
|
308
|
+
"senior:fix-ci": { status: "fixed" },
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
await engine.publishMessage({ name: "deps-cleared", correlationKey: "pr-1" });
|
|
312
|
+
await engine.advanceTime(MERGEABLE_WAIT_MS + 1);
|
|
313
|
+
assertThatInstance(engine, byProcessId("merge-loop"))
|
|
314
|
+
.isActive()
|
|
315
|
+
.hasActiveElement("wait-mergeable")
|
|
316
|
+
.hasCompletedElements("merge-stall-probe", "fix-ci")
|
|
317
|
+
.hasVariable("ciFixRound", 1);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("the probe advances mergeStallRounds and re-arms within budget (#636)", async () => {
|
|
321
|
+
// A re-derived `conflict` re-arms after the rebase; the stall counter advanced so a still-dead
|
|
322
|
+
// poller cannot loop the probe forever — it is bounded by mergeStallMax.
|
|
323
|
+
const engine = await boot({
|
|
324
|
+
responses: {
|
|
325
|
+
"pr.merge-stall-probe": { mergeState: "conflict", failingChecks: 0, failingChecksList: "" },
|
|
326
|
+
"senior:rebase": { status: "rebased" },
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
await engine.publishMessage({ name: "deps-cleared", correlationKey: "pr-1" });
|
|
330
|
+
await engine.advanceTime(MERGEABLE_WAIT_MS + 1);
|
|
331
|
+
assertThatInstance(engine, byProcessId("merge-loop")).hasVariable("mergeStallRounds", 1);
|
|
332
|
+
assert(!completedElementIds(engine).has("merge-esc-conflict"), "a within-budget stall must NOT escalate");
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test("the stall-round cap escalates to a human instead of looping forever (#636)", async () => {
|
|
336
|
+
// `mergeStallMax: 0` — escalate on the first stall. The probe runs once, `gw-merge-stall` finds the
|
|
337
|
+
// budget exhausted, and routes to `merge-esc-conflict` → the native `wait-merge-answer` user task
|
|
338
|
+
// rather than re-arming into another timeout round.
|
|
339
|
+
const engine = await boot({
|
|
340
|
+
vars: { mergeStallMax: 0 },
|
|
341
|
+
responses: {
|
|
342
|
+
"pr.merge-stall-probe": { mergeState: "conflict", failingChecks: 0, failingChecksList: "" },
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
await engine.publishMessage({ name: "deps-cleared", correlationKey: "pr-1" });
|
|
346
|
+
await engine.advanceTime(MERGEABLE_WAIT_MS + 1);
|
|
347
|
+
await assertThatUserTask(engine, { instance: byProcessId("merge-loop"), elementId: "wait-merge-answer" }).isCreated();
|
|
348
|
+
assertThatInstance(engine, byProcessId("merge-loop")).hasCompletedElements("merge-stall-probe", "merge-esc-conflict");
|
|
349
|
+
assert(!completedElementIds(engine).has("mark-merged"), "an exhausted stall must not mark-merged");
|
|
350
|
+
});
|
|
351
|
+
|
|
241
352
|
test("an evicted queued merge re-arms the poller rather than completing", async () => {
|
|
242
353
|
const engine = await boot({ responses: { "pr.merge": { mergeStatus: "queued" } } });
|
|
243
354
|
await engine.publishMessage({ name: "deps-cleared", correlationKey: "pr-1" });
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Unit coverage for the mergeable-wait liveness timeout policy. The value is baked into every
|
|
2
|
+
// merge-loop instance's `mergeableWaitTimeout` process variable and evaluated by the
|
|
3
|
+
// `wait-mergeable-timeout` timer catch (the timer arm of the `gw-merge-wait` event-based gateway),
|
|
4
|
+
// so a malformed operator env must never deploy an uninterpretable `<bpmn:timeDuration>` — it falls
|
|
5
|
+
// back to the default instead. Run with `node --test`.
|
|
6
|
+
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
import { DEFAULT_MERGEABLE_WAIT_TIMEOUT, mergeableWaitTimeout } from "./mergeableWait.ts";
|
|
10
|
+
|
|
11
|
+
test("mergeableWaitTimeout: blank / absent / malformed → default", () => {
|
|
12
|
+
assert.equal(mergeableWaitTimeout(undefined), DEFAULT_MERGEABLE_WAIT_TIMEOUT);
|
|
13
|
+
assert.equal(mergeableWaitTimeout(""), DEFAULT_MERGEABLE_WAIT_TIMEOUT);
|
|
14
|
+
assert.equal(mergeableWaitTimeout(" "), DEFAULT_MERGEABLE_WAIT_TIMEOUT);
|
|
15
|
+
assert.equal(mergeableWaitTimeout("30m"), DEFAULT_MERGEABLE_WAIT_TIMEOUT); // missing leading P/T
|
|
16
|
+
assert.equal(mergeableWaitTimeout("P"), DEFAULT_MERGEABLE_WAIT_TIMEOUT); // no component
|
|
17
|
+
assert.equal(mergeableWaitTimeout("PT"), DEFAULT_MERGEABLE_WAIT_TIMEOUT); // T with no time part
|
|
18
|
+
assert.equal(mergeableWaitTimeout("garbage"), DEFAULT_MERGEABLE_WAIT_TIMEOUT);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("mergeableWaitTimeout: a valid ISO-8601 duration is honoured and upper-cased", () => {
|
|
22
|
+
assert.equal(mergeableWaitTimeout("PT30M"), "PT30M");
|
|
23
|
+
assert.equal(mergeableWaitTimeout("pt2h"), "PT2H");
|
|
24
|
+
assert.equal(mergeableWaitTimeout("P1D"), "P1D");
|
|
25
|
+
assert.equal(mergeableWaitTimeout(" pt45m "), "PT45M");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("mergeableWaitTimeout: an explicit fallback is honoured for a bad value", () => {
|
|
29
|
+
assert.equal(mergeableWaitTimeout("nope", "PT10M"), "PT10M");
|
|
30
|
+
assert.equal(mergeableWaitTimeout("PT15M", "PT10M"), "PT15M");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("the default is itself a well-formed ISO-8601 duration (never an uninterpretable timer)", () => {
|
|
34
|
+
// Validate the default against the grammar with a *distinct* fallback: if the default were
|
|
35
|
+
// malformed it would fall through to the sentinel, so equality to itself proves it parses.
|
|
36
|
+
const sentinel = "PT1S";
|
|
37
|
+
assert.notEqual(DEFAULT_MERGEABLE_WAIT_TIMEOUT, sentinel);
|
|
38
|
+
assert.equal(mergeableWaitTimeout(DEFAULT_MERGEABLE_WAIT_TIMEOUT, sentinel), DEFAULT_MERGEABLE_WAIT_TIMEOUT);
|
|
39
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Mergeable-wait liveness policy — kept as a pure module (no env, no I/O) so it is trivially
|
|
2
|
+
// testable, mirroring app/mergeLandedWait.ts. `app/service.ts` seeds the validated
|
|
3
|
+
// `mergeableWaitTimeout` process variable when it starts the merge-loop; the merge-loop's
|
|
4
|
+
// `wait-mergeable-timeout` timer catch (the timer arm of the `gw-merge-wait` event-based gateway,
|
|
5
|
+
// racing the poller's `merge-ready` message) evaluates its `<bpmn:timeDuration>=mergeableWaitTimeout`
|
|
6
|
+
// at timer creation (FEEL-expression timer durations, engine-native).
|
|
7
|
+
//
|
|
8
|
+
// This closes the mergeable-wait liveness gap (issue #636): `wait-mergeable` was the only long-lived
|
|
9
|
+
// wait in the merge loop with NO bounded-wait timer — a bare `intermediateCatchEvent` woken *solely*
|
|
10
|
+
// by the in-process poller's `merge-ready` message. If that poller stalls (dies across a redeploy,
|
|
11
|
+
// errors on a PR, or skips a verdict) the instance parks at `waiting_merge` forever, with no timeout
|
|
12
|
+
// and no escalation — the entire conflict/CI remediation machinery (`gw-mergeable` → rebase / CI-fix
|
|
13
|
+
// / escalate) sits downstream of `wait-mergeable`, so it is never entered. Bounding the wait with a
|
|
14
|
+
// timer arm makes that impossible: when the timeout elapses the token routes to `merge-stall-probe`,
|
|
15
|
+
// which re-derives ground-truth mergeability from GitHub and feeds the existing `gw-mergeable` arms
|
|
16
|
+
// (or, after the stall-round cap is exhausted, escalates to a human). It is a durable, in-process
|
|
17
|
+
// backstop — no external watchdog required — mirroring the convergence loop's `wait-review-timeout`
|
|
18
|
+
// and the merge loop's own `wait-landed-timeout`.
|
|
19
|
+
|
|
20
|
+
import { isoDuration } from "./reviewWait.ts";
|
|
21
|
+
|
|
22
|
+
/** Default mergeable-wait timeout (ISO-8601 duration): how long the merge loop waits for the poller
|
|
23
|
+
* to publish `merge-ready` before the timer arm of the `gw-merge-wait` event-based gateway fires and
|
|
24
|
+
* `merge-stall-probe` re-derives mergeability from ground truth. Deliberately short — a healthy
|
|
25
|
+
* poller signals within one poll interval, so a 30-minute silence means the poller is dead and the
|
|
26
|
+
* PR must be reconciled — while still generous enough not to fire against a merely-slow poll pass. */
|
|
27
|
+
export const DEFAULT_MERGEABLE_WAIT_TIMEOUT = "PT30M";
|
|
28
|
+
|
|
29
|
+
/** Validate the operator-supplied mergeable-wait timeout (env `NANO_PR_MERGEABLE_WAIT_TIMEOUT`,
|
|
30
|
+
* ISO-8601 duration), falling back to {@link DEFAULT_MERGEABLE_WAIT_TIMEOUT} when absent, blank, or
|
|
31
|
+
* malformed — a bad env value must never deploy an uninterpretable timer expression. Derives its
|
|
32
|
+
* validation from the single canonical {@link isoDuration}. */
|
|
33
|
+
export function mergeableWaitTimeout(
|
|
34
|
+
raw: string | undefined,
|
|
35
|
+
def: string = DEFAULT_MERGEABLE_WAIT_TIMEOUT,
|
|
36
|
+
): string {
|
|
37
|
+
return isoDuration(raw, def);
|
|
38
|
+
}
|
package/app/service.ts
CHANGED
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
} from "./github.ts";
|
|
54
54
|
import { activeStatusesFor, derivedTrackingTable } from "./instanceTracking.ts";
|
|
55
55
|
import { pollLineage } from "./lineage.ts";
|
|
56
|
+
import { mergeableWaitTimeout } from "./mergeableWait.ts";
|
|
56
57
|
import { mergeLanes, readExclusions } from "./mergeExclusion.ts";
|
|
57
58
|
import { mergeLandedWaitTimeout } from "./mergeLandedWait.ts";
|
|
58
59
|
import {
|
|
@@ -143,6 +144,15 @@ export const MAX_REBASE_ROUNDS = clampCiFixBudget(process.env.NANO_PR_MAX_REBASE
|
|
|
143
144
|
* retry (a race escalates immediately). Reuses the CI-fix budget clamp (allows 0 = disable). */
|
|
144
145
|
export const MAX_MERGE_RETRIES = clampCiFixBudget(process.env.NANO_PR_MAX_MERGE_RETRIES, 5);
|
|
145
146
|
|
|
147
|
+
/** How many times the mergeable-wait timeout backstop (`merge-stall-probe`) will re-derive
|
|
148
|
+
* mergeability from ground truth and re-arm the merge stage before giving up and escalating to a
|
|
149
|
+
* human. Bounds the timer arm of the `gw-merge-wait` event-based gateway so a dead in-process poller
|
|
150
|
+
* (the #636 wedge) can never loop the probe forever: once the cap is exhausted, `gw-merge-stall`
|
|
151
|
+
* routes to `merge-esc-conflict` (human escalation) instead of re-arming. Default 3; set
|
|
152
|
+
* `NANO_PR_MAX_MERGE_STALL_ROUNDS=0` to escalate on the first stall. Reuses the CI-fix budget clamp
|
|
153
|
+
* (allows 0 = escalate immediately, ceiling-capped). */
|
|
154
|
+
export const MAX_MERGE_STALL_ROUNDS = clampCiFixBudget(process.env.NANO_PR_MAX_MERGE_STALL_ROUNDS, 3);
|
|
155
|
+
|
|
146
156
|
/** How long a merge-loop AGENT service task (rebase / fix-ci) may sit without completing before its
|
|
147
157
|
* interrupting timer boundary fires and the PR escalates for human attention. Seeded as the
|
|
148
158
|
* `agentSlaTimeout` process variable at merge start and evaluated by those tasks' boundary timers.
|
|
@@ -170,6 +180,19 @@ export const MERGE_LANDED_WAIT_TIMEOUT = mergeLandedWaitTimeout(
|
|
|
170
180
|
process.env.NANO_PR_MERGE_LANDED_WAIT_TIMEOUT,
|
|
171
181
|
);
|
|
172
182
|
|
|
183
|
+
/** How long the merge loop waits for the in-process poller to publish `merge-ready` before the timer
|
|
184
|
+
* arm of the `gw-merge-wait` event-based gateway fires and `merge-stall-probe` re-derives
|
|
185
|
+
* mergeability from ground truth. Seeded as the `mergeableWaitTimeout` process variable at merge
|
|
186
|
+
* start and evaluated by the merge-loop's `wait-mergeable-timeout` timer catch. `wait-mergeable` was
|
|
187
|
+
* the only long-lived wait in the merge loop with NO bounded-wait timer, so a stalled poller (dead
|
|
188
|
+
* across a redeploy, errored on a PR, or a skipped verdict) parked the instance at `waiting_merge`
|
|
189
|
+
* forever with no timeout and no escalation (issue #636). ISO-8601 duration; a malformed
|
|
190
|
+
* `NANO_PR_MERGEABLE_WAIT_TIMEOUT` falls back to the default so an uninterpretable timer is never
|
|
191
|
+
* deployed. */
|
|
192
|
+
export const MERGEABLE_WAIT_TIMEOUT = mergeableWaitTimeout(
|
|
193
|
+
process.env.NANO_PR_MERGEABLE_WAIT_TIMEOUT,
|
|
194
|
+
);
|
|
195
|
+
|
|
173
196
|
/** Cooldown (ms) between the poller's automatic Copilot re-request nudges for a single waiting PR.
|
|
174
197
|
* Copilot dismisses re-requests, so the poller retries — but not on every tick; this throttles it
|
|
175
198
|
* to one attempt per window. Set via `NANO_PR_REVIEW_NUDGE_MINUTES` (minutes). */
|
|
@@ -723,6 +746,9 @@ export async function startMerge(
|
|
|
723
746
|
mergeRetryMax: MAX_MERGE_RETRIES,
|
|
724
747
|
agentSlaTimeout: AGENT_SLA_TIMEOUT,
|
|
725
748
|
landedWaitTimeout: MERGE_LANDED_WAIT_TIMEOUT,
|
|
749
|
+
mergeableWaitTimeout: MERGEABLE_WAIT_TIMEOUT,
|
|
750
|
+
mergeStallRounds: 0,
|
|
751
|
+
mergeStallMax: MAX_MERGE_STALL_ROUNDS,
|
|
726
752
|
// Lineage (issue #245): thread the origin identity onto the merge instance (see startMerge).
|
|
727
753
|
rootRequestKey,
|
|
728
754
|
abandonUrl: abUrl,
|
package/nano.app.json
CHANGED
|
@@ -144,6 +144,10 @@
|
|
|
144
144
|
"taskType": "pr.mark-merged",
|
|
145
145
|
"handler": "workers/mark-merged/worker.ts"
|
|
146
146
|
},
|
|
147
|
+
{
|
|
148
|
+
"taskType": "pr.merge-stall-probe",
|
|
149
|
+
"handler": "workers/merge-stall-probe/worker.ts"
|
|
150
|
+
},
|
|
147
151
|
{
|
|
148
152
|
"taskType": "pr.record-dependency",
|
|
149
153
|
"handler": "workers/record-dependency/worker.ts"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/nano-workforce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.164.0",
|
|
4
4
|
"description": "Nano Workforce — an Agent Graph Orchestration application for Agentic SDLC: durable BPMN processes that coordinate a graph of AI agents across the software delivery lifecycle.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "main.ts",
|
|
@@ -44,6 +44,16 @@
|
|
|
44
44
|
<nano:shape id="MarkMergedIn" name="Mark merged — input">
|
|
45
45
|
<nano:extend name="prKey" type="string" />
|
|
46
46
|
</nano:shape>
|
|
47
|
+
<nano:shape id="MergeStallProbeIn" name="Merge stall probe — input">
|
|
48
|
+
<nano:extend name="prKey" type="string" />
|
|
49
|
+
<nano:extend name="repo" type="string" />
|
|
50
|
+
<nano:extend name="prNumber" type="integer" />
|
|
51
|
+
</nano:shape>
|
|
52
|
+
<nano:shape id="MergeStallProbeOut" name="Merge stall probe — result">
|
|
53
|
+
<nano:extend name="mergeState" type="string" />
|
|
54
|
+
<nano:extend name="failingChecks" type="integer" optional="true" />
|
|
55
|
+
<nano:extend name="failingChecksList" type="string" optional="true" />
|
|
56
|
+
</nano:shape>
|
|
47
57
|
<nano:shape id="MergeReady" name="merge-ready message payload">
|
|
48
58
|
<nano:extend name="mergeState" type="string" />
|
|
49
59
|
<nano:extend name="failingChecks" type="integer" optional="true" />
|
|
@@ -111,14 +121,50 @@
|
|
|
111
121
|
<bpmn:incoming>f_mr_go</bpmn:incoming>
|
|
112
122
|
<bpmn:outgoing>f_m_arm</bpmn:outgoing>
|
|
113
123
|
</bpmn:serviceTask>
|
|
114
|
-
<bpmn:
|
|
124
|
+
<bpmn:exclusiveGateway id="gw-merge-entry" name="enter merge wait">
|
|
115
125
|
<bpmn:incoming>f_m_arm</bpmn:incoming>
|
|
116
126
|
<bpmn:incoming>f_ci_recheck</bpmn:incoming>
|
|
127
|
+
<bpmn:outgoing>f_me_wait</bpmn:outgoing>
|
|
128
|
+
</bpmn:exclusiveGateway>
|
|
129
|
+
<bpmn:eventBasedGateway id="gw-merge-wait" name="mergeable or timeout?">
|
|
130
|
+
<bpmn:incoming>f_me_wait</bpmn:incoming>
|
|
131
|
+
<bpmn:outgoing>f_mw_ready</bpmn:outgoing>
|
|
132
|
+
<bpmn:outgoing>f_mw_timeout</bpmn:outgoing>
|
|
133
|
+
</bpmn:eventBasedGateway>
|
|
134
|
+
<bpmn:intermediateCatchEvent id="wait-mergeable" name="Wait: mergeable">
|
|
135
|
+
<bpmn:incoming>f_mw_ready</bpmn:incoming>
|
|
117
136
|
<bpmn:outgoing>f_m_ready</bpmn:outgoing>
|
|
118
137
|
<bpmn:messageEventDefinition id="med_mergeReady" messageRef="Message_mergeReady" />
|
|
119
138
|
</bpmn:intermediateCatchEvent>
|
|
139
|
+
<bpmn:intermediateCatchEvent id="wait-mergeable-timeout" name="Wait: mergeable timeout">
|
|
140
|
+
<bpmn:incoming>f_mw_timeout</bpmn:incoming>
|
|
141
|
+
<bpmn:outgoing>f_mw_stall</bpmn:outgoing>
|
|
142
|
+
<bpmn:timerEventDefinition id="ted_mergeableTimeout">
|
|
143
|
+
<bpmn:timeDuration xsi:type="bpmn:tFormalExpression">=mergeableWaitTimeout</bpmn:timeDuration>
|
|
144
|
+
</bpmn:timerEventDefinition>
|
|
145
|
+
</bpmn:intermediateCatchEvent>
|
|
146
|
+
<bpmn:serviceTask id="merge-stall-probe" name="Probe mergeability (poller stalled)">
|
|
147
|
+
<bpmn:extensionElements>
|
|
148
|
+
<zeebe:taskDefinition type="pr.merge-stall-probe" />
|
|
149
|
+
<zeebe:properties>
|
|
150
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="MergeStallProbeIn" />
|
|
151
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.out" value="MergeStallProbeOut" />
|
|
152
|
+
</zeebe:properties>
|
|
153
|
+
<zeebe:ioMapping>
|
|
154
|
+
<zeebe:output source="=mergeStallRounds + 1" target="mergeStallRounds" />
|
|
155
|
+
</zeebe:ioMapping>
|
|
156
|
+
</bpmn:extensionElements>
|
|
157
|
+
<bpmn:incoming>f_mw_stall</bpmn:incoming>
|
|
158
|
+
<bpmn:outgoing>f_msp_gate</bpmn:outgoing>
|
|
159
|
+
</bpmn:serviceTask>
|
|
160
|
+
<bpmn:exclusiveGateway id="gw-merge-stall" name="stall rounds within budget?" default="f_msp_giveup">
|
|
161
|
+
<bpmn:incoming>f_msp_gate</bpmn:incoming>
|
|
162
|
+
<bpmn:outgoing>f_msp_rederive</bpmn:outgoing>
|
|
163
|
+
<bpmn:outgoing>f_msp_giveup</bpmn:outgoing>
|
|
164
|
+
</bpmn:exclusiveGateway>
|
|
120
165
|
<bpmn:exclusiveGateway id="gw-mergeable" name="mergeable?" default="f_m_mBlocked">
|
|
121
166
|
<bpmn:incoming>f_m_ready</bpmn:incoming>
|
|
167
|
+
<bpmn:incoming>f_msp_rederive</bpmn:incoming>
|
|
122
168
|
<bpmn:outgoing>f_m_mReady</bpmn:outgoing>
|
|
123
169
|
<bpmn:outgoing>f_m_mCiFix</bpmn:outgoing>
|
|
124
170
|
<bpmn:outgoing>f_m_mRebase</bpmn:outgoing>
|
|
@@ -208,6 +254,7 @@
|
|
|
208
254
|
<bpmn:incoming>f_ci_giveup</bpmn:incoming>
|
|
209
255
|
<bpmn:incoming>f_reb_giveup</bpmn:incoming>
|
|
210
256
|
<bpmn:incoming>f_reb_escConflict</bpmn:incoming>
|
|
257
|
+
<bpmn:incoming>f_msp_giveup</bpmn:incoming>
|
|
211
258
|
<bpmn:outgoing>f_m_escC</bpmn:outgoing>
|
|
212
259
|
</bpmn:serviceTask>
|
|
213
260
|
<bpmn:serviceTask id="merge-esc-attempt" name="Escalate: merge blocked">
|
|
@@ -424,7 +471,7 @@
|
|
|
424
471
|
<bpmn:outgoing>f_ci_escAttempt</bpmn:outgoing>
|
|
425
472
|
<bpmn:outgoing>f_ci_rearm</bpmn:outgoing>
|
|
426
473
|
</bpmn:exclusiveGateway>
|
|
427
|
-
<bpmn:sequenceFlow id="f_ci_recheck" name="reconciled — re-check" sourceRef="gw-ci-outcome" targetRef="
|
|
474
|
+
<bpmn:sequenceFlow id="f_ci_recheck" name="reconciled — re-check" sourceRef="gw-ci-outcome" targetRef="gw-merge-entry">
|
|
428
475
|
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=ciOutcome = "recheck"</bpmn:conditionExpression>
|
|
429
476
|
</bpmn:sequenceFlow>
|
|
430
477
|
<bpmn:sequenceFlow id="f_ci_waitDep" name="waits on another PR" sourceRef="gw-ci-outcome" targetRef="record-merge-dep">
|
|
@@ -560,7 +607,16 @@
|
|
|
560
607
|
</bpmn:serviceTask>
|
|
561
608
|
<bpmn:sequenceFlow id="f_m_start" sourceRef="MergeStart" targetRef="wait-deps" />
|
|
562
609
|
<bpmn:sequenceFlow id="f_m_deps" sourceRef="wait-deps" targetRef="arm-merge" />
|
|
563
|
-
<bpmn:sequenceFlow id="f_m_arm" sourceRef="arm-merge" targetRef="
|
|
610
|
+
<bpmn:sequenceFlow id="f_m_arm" sourceRef="arm-merge" targetRef="gw-merge-entry" />
|
|
611
|
+
<bpmn:sequenceFlow id="f_me_wait" sourceRef="gw-merge-entry" targetRef="gw-merge-wait" />
|
|
612
|
+
<bpmn:sequenceFlow id="f_mw_ready" sourceRef="gw-merge-wait" targetRef="wait-mergeable" />
|
|
613
|
+
<bpmn:sequenceFlow id="f_mw_timeout" sourceRef="gw-merge-wait" targetRef="wait-mergeable-timeout" />
|
|
614
|
+
<bpmn:sequenceFlow id="f_mw_stall" sourceRef="wait-mergeable-timeout" targetRef="merge-stall-probe" />
|
|
615
|
+
<bpmn:sequenceFlow id="f_msp_gate" sourceRef="merge-stall-probe" targetRef="gw-merge-stall" />
|
|
616
|
+
<bpmn:sequenceFlow id="f_msp_rederive" name="re-derived — route to arm" sourceRef="gw-merge-stall" targetRef="gw-mergeable">
|
|
617
|
+
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=mergeStallRounds <= mergeStallMax</bpmn:conditionExpression>
|
|
618
|
+
</bpmn:sequenceFlow>
|
|
619
|
+
<bpmn:sequenceFlow id="f_msp_giveup" name="stall budget exhausted" sourceRef="gw-merge-stall" targetRef="merge-esc-conflict" />
|
|
564
620
|
<bpmn:sequenceFlow id="f_m_ready" sourceRef="wait-mergeable" targetRef="gw-mergeable" />
|
|
565
621
|
<bpmn:sequenceFlow id="f_m_mReady" name="ready" sourceRef="gw-mergeable" targetRef="attempt-merge">
|
|
566
622
|
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=mergeState = "ready"</bpmn:conditionExpression>
|
|
@@ -635,125 +691,152 @@
|
|
|
635
691
|
<bpmndi:BPMNShape id="BPMNShape_arm-merge" bpmnElement="arm-merge">
|
|
636
692
|
<dc:Bounds x="352" y="240" width="100" height="80" />
|
|
637
693
|
</bpmndi:BPMNShape>
|
|
694
|
+
<bpmndi:BPMNShape id="BPMNShape_gw-merge-entry" bpmnElement="gw-merge-entry" isMarkerVisible="true">
|
|
695
|
+
<dc:Bounds x="552" y="255" width="50" height="50" />
|
|
696
|
+
<bpmndi:BPMNLabel>
|
|
697
|
+
<dc:Bounds x="537" y="222" width="81" height="28" />
|
|
698
|
+
</bpmndi:BPMNLabel>
|
|
699
|
+
</bpmndi:BPMNShape>
|
|
700
|
+
<bpmndi:BPMNShape id="BPMNShape_gw-merge-wait" bpmnElement="gw-merge-wait" isMarkerVisible="true">
|
|
701
|
+
<dc:Bounds x="702" y="255" width="50" height="50" />
|
|
702
|
+
<bpmndi:BPMNLabel>
|
|
703
|
+
<dc:Bounds x="683" y="222" width="88" height="28" />
|
|
704
|
+
</bpmndi:BPMNLabel>
|
|
705
|
+
</bpmndi:BPMNShape>
|
|
638
706
|
<bpmndi:BPMNShape id="BPMNShape_wait-mergeable" bpmnElement="wait-mergeable">
|
|
639
|
-
<dc:Bounds x="
|
|
707
|
+
<dc:Bounds x="852" y="262" width="36" height="36" />
|
|
640
708
|
<bpmndi:BPMNLabel>
|
|
641
|
-
<dc:Bounds x="
|
|
709
|
+
<dc:Bounds x="835" y="303" width="70" height="28" />
|
|
710
|
+
</bpmndi:BPMNLabel>
|
|
711
|
+
</bpmndi:BPMNShape>
|
|
712
|
+
<bpmndi:BPMNShape id="BPMNShape_wait-mergeable-timeout" bpmnElement="wait-mergeable-timeout">
|
|
713
|
+
<dc:Bounds x="852" y="422" width="36" height="36" />
|
|
714
|
+
<bpmndi:BPMNLabel>
|
|
715
|
+
<dc:Bounds x="835" y="463" width="70" height="42" />
|
|
716
|
+
</bpmndi:BPMNLabel>
|
|
717
|
+
</bpmndi:BPMNShape>
|
|
718
|
+
<bpmndi:BPMNShape id="BPMNShape_merge-stall-probe" bpmnElement="merge-stall-probe">
|
|
719
|
+
<dc:Bounds x="988" y="400" width="100" height="80" />
|
|
720
|
+
</bpmndi:BPMNShape>
|
|
721
|
+
<bpmndi:BPMNShape id="BPMNShape_gw-merge-stall" bpmnElement="gw-merge-stall" isMarkerVisible="true">
|
|
722
|
+
<dc:Bounds x="1188" y="415" width="50" height="50" />
|
|
723
|
+
<bpmndi:BPMNLabel>
|
|
724
|
+
<dc:Bounds x="1191" y="470" width="85" height="42" />
|
|
642
725
|
</bpmndi:BPMNLabel>
|
|
643
726
|
</bpmndi:BPMNShape>
|
|
644
727
|
<bpmndi:BPMNShape id="BPMNShape_gw-mergeable" bpmnElement="gw-mergeable" isMarkerVisible="true">
|
|
645
|
-
<dc:Bounds x="
|
|
728
|
+
<dc:Bounds x="1338" y="255" width="50" height="50" />
|
|
646
729
|
<bpmndi:BPMNLabel>
|
|
647
|
-
<dc:Bounds x="
|
|
730
|
+
<dc:Bounds x="1325" y="236" width="77" height="14" />
|
|
648
731
|
</bpmndi:BPMNLabel>
|
|
649
732
|
</bpmndi:BPMNShape>
|
|
650
733
|
<bpmndi:BPMNShape id="BPMNShape_attempt-merge" bpmnElement="attempt-merge">
|
|
651
|
-
<dc:Bounds x="
|
|
734
|
+
<dc:Bounds x="1488" y="240" width="100" height="80" />
|
|
652
735
|
</bpmndi:BPMNShape>
|
|
653
736
|
<bpmndi:BPMNShape id="BPMNShape_gw-merge" bpmnElement="gw-merge" isMarkerVisible="true">
|
|
654
|
-
<dc:Bounds x="
|
|
737
|
+
<dc:Bounds x="1713" y="255" width="50" height="50" />
|
|
655
738
|
<bpmndi:BPMNLabel>
|
|
656
|
-
<dc:Bounds x="
|
|
739
|
+
<dc:Bounds x="1712" y="350" width="53" height="28" />
|
|
657
740
|
</bpmndi:BPMNLabel>
|
|
658
741
|
</bpmndi:BPMNShape>
|
|
659
742
|
<bpmndi:BPMNShape id="BPMNShape_gw-merge-retry" bpmnElement="gw-merge-retry" isMarkerVisible="true">
|
|
660
|
-
<dc:Bounds x="
|
|
743
|
+
<dc:Bounds x="1888" y="735" width="50" height="50" />
|
|
661
744
|
<bpmndi:BPMNLabel>
|
|
662
|
-
<dc:Bounds x="
|
|
745
|
+
<dc:Bounds x="1869" y="830" width="88" height="28" />
|
|
663
746
|
</bpmndi:BPMNLabel>
|
|
664
747
|
</bpmndi:BPMNShape>
|
|
665
748
|
<bpmndi:BPMNShape id="BPMNShape_eg-landed" bpmnElement="eg-landed" isMarkerVisible="true">
|
|
666
|
-
<dc:Bounds x="
|
|
749
|
+
<dc:Bounds x="1888" y="415" width="50" height="50" />
|
|
667
750
|
<bpmndi:BPMNLabel>
|
|
668
|
-
<dc:Bounds x="
|
|
751
|
+
<dc:Bounds x="1881" y="490" width="64" height="28" />
|
|
669
752
|
</bpmndi:BPMNLabel>
|
|
670
753
|
</bpmndi:BPMNShape>
|
|
671
754
|
<bpmndi:BPMNShape id="BPMNShape_wait-landed" bpmnElement="wait-landed">
|
|
672
|
-
<dc:Bounds x="
|
|
755
|
+
<dc:Bounds x="2070" y="422" width="36" height="36" />
|
|
673
756
|
<bpmndi:BPMNLabel>
|
|
674
|
-
<dc:Bounds x="
|
|
757
|
+
<dc:Bounds x="2066" y="483" width="85" height="28" />
|
|
675
758
|
</bpmndi:BPMNLabel>
|
|
676
759
|
</bpmndi:BPMNShape>
|
|
677
760
|
<bpmndi:BPMNShape id="BPMNShape_wait-evicted" bpmnElement="wait-evicted">
|
|
678
|
-
<dc:Bounds x="
|
|
761
|
+
<dc:Bounds x="2070" y="582" width="36" height="36" />
|
|
679
762
|
<bpmndi:BPMNLabel>
|
|
680
|
-
<dc:Bounds x="
|
|
763
|
+
<dc:Bounds x="2111" y="579" width="88" height="42" />
|
|
681
764
|
</bpmndi:BPMNLabel>
|
|
682
765
|
</bpmndi:BPMNShape>
|
|
683
766
|
<bpmndi:BPMNShape id="BPMNShape_wait-landed-timeout" bpmnElement="wait-landed-timeout">
|
|
684
|
-
<dc:Bounds x="
|
|
767
|
+
<dc:Bounds x="2070" y="102" width="36" height="36" />
|
|
685
768
|
<bpmndi:BPMNLabel>
|
|
686
|
-
<dc:Bounds x="
|
|
769
|
+
<dc:Bounds x="1981" y="92" width="84" height="56" />
|
|
687
770
|
</bpmndi:BPMNLabel>
|
|
688
771
|
</bpmndi:BPMNShape>
|
|
689
772
|
<bpmndi:BPMNShape id="BPMNShape_mark-merged" bpmnElement="mark-merged">
|
|
690
|
-
<dc:Bounds x="
|
|
773
|
+
<dc:Bounds x="2238" y="240" width="100" height="80" />
|
|
691
774
|
</bpmndi:BPMNShape>
|
|
692
775
|
<bpmndi:BPMNShape id="BPMNShape_MergeEnd" bpmnElement="MergeEnd">
|
|
693
|
-
<dc:Bounds x="
|
|
776
|
+
<dc:Bounds x="2445" y="262" width="36" height="36" />
|
|
694
777
|
<bpmndi:BPMNLabel>
|
|
695
|
-
<dc:Bounds x="
|
|
778
|
+
<dc:Bounds x="2438" y="303" width="50" height="14" />
|
|
696
779
|
</bpmndi:BPMNLabel>
|
|
697
780
|
</bpmndi:BPMNShape>
|
|
698
781
|
<bpmndi:BPMNShape id="BPMNShape_MergeAbandoned" bpmnElement="MergeAbandoned">
|
|
699
|
-
<dc:Bounds x="
|
|
782
|
+
<dc:Bounds x="1895" y="902" width="36" height="36" />
|
|
700
783
|
<bpmndi:BPMNLabel>
|
|
701
|
-
<dc:Bounds x="
|
|
784
|
+
<dc:Bounds x="1872" y="943" width="82" height="28" />
|
|
702
785
|
</bpmndi:BPMNLabel>
|
|
703
786
|
</bpmndi:BPMNShape>
|
|
704
787
|
<bpmndi:BPMNShape id="BPMNShape_merge-esc-conflict" bpmnElement="merge-esc-conflict">
|
|
705
|
-
<dc:Bounds x="
|
|
788
|
+
<dc:Bounds x="2038" y="1040" width="100" height="80" />
|
|
706
789
|
</bpmndi:BPMNShape>
|
|
707
790
|
<bpmndi:BPMNShape id="BPMNShape_merge-esc-attempt" bpmnElement="merge-esc-attempt">
|
|
708
|
-
<dc:Bounds x="
|
|
791
|
+
<dc:Bounds x="2038" y="720" width="100" height="80" />
|
|
709
792
|
</bpmndi:BPMNShape>
|
|
710
793
|
<bpmndi:BPMNShape id="BPMNShape_gw-merge-escalated" bpmnElement="gw-merge-escalated" isMarkerVisible="true">
|
|
711
|
-
<dc:Bounds x="
|
|
794
|
+
<dc:Bounds x="2438" y="95" width="50" height="50" />
|
|
712
795
|
<bpmndi:BPMNLabel>
|
|
713
|
-
<dc:Bounds x="
|
|
796
|
+
<dc:Bounds x="2406" y="150" width="74" height="28" />
|
|
714
797
|
</bpmndi:BPMNLabel>
|
|
715
798
|
</bpmndi:BPMNShape>
|
|
716
799
|
<bpmndi:BPMNShape id="BPMNShape_merge-esc-landed" bpmnElement="merge-esc-landed">
|
|
717
|
-
<dc:Bounds x="
|
|
800
|
+
<dc:Bounds x="2238" y="80" width="100" height="80" />
|
|
718
801
|
</bpmndi:BPMNShape>
|
|
719
802
|
<bpmndi:BPMNShape id="BPMNShape_wait-merge-answer" bpmnElement="wait-merge-answer">
|
|
720
|
-
<dc:Bounds x="
|
|
803
|
+
<dc:Bounds x="2588" y="80" width="100" height="80" />
|
|
721
804
|
</bpmndi:BPMNShape>
|
|
722
805
|
<bpmndi:BPMNShape id="BPMNShape_record-merge-answer" bpmnElement="record-merge-answer">
|
|
723
|
-
<dc:Bounds x="
|
|
806
|
+
<dc:Bounds x="2788" y="80" width="100" height="80" />
|
|
724
807
|
</bpmndi:BPMNShape>
|
|
725
808
|
<bpmndi:BPMNShape id="BPMNShape_gw-ci-fix" bpmnElement="gw-ci-fix" isMarkerVisible="true">
|
|
726
|
-
<dc:Bounds x="
|
|
809
|
+
<dc:Bounds x="1513" y="1055" width="50" height="50" />
|
|
727
810
|
<bpmndi:BPMNLabel>
|
|
728
|
-
<dc:Bounds x="
|
|
811
|
+
<dc:Bounds x="1494" y="1036" width="89" height="14" />
|
|
729
812
|
</bpmndi:BPMNLabel>
|
|
730
813
|
</bpmndi:BPMNShape>
|
|
731
814
|
<bpmndi:BPMNShape id="BPMNShape_SP_cifix" bpmnElement="SP_cifix">
|
|
732
|
-
<dc:Bounds x="
|
|
815
|
+
<dc:Bounds x="1688" y="1200" width="100" height="80" />
|
|
733
816
|
</bpmndi:BPMNShape>
|
|
734
817
|
<bpmndi:BPMNShape id="BPMNShape_gw-ci-outcome" bpmnElement="gw-ci-outcome" isMarkerVisible="true">
|
|
735
|
-
<dc:Bounds x="
|
|
818
|
+
<dc:Bounds x="1888" y="1215" width="50" height="50" />
|
|
736
819
|
<bpmndi:BPMNLabel>
|
|
737
|
-
<dc:Bounds x="
|
|
820
|
+
<dc:Bounds x="1882" y="1310" width="63" height="28" />
|
|
738
821
|
</bpmndi:BPMNLabel>
|
|
739
822
|
</bpmndi:BPMNShape>
|
|
740
823
|
<bpmndi:BPMNShape id="BPMNShape_gw-rebase" bpmnElement="gw-rebase" isMarkerVisible="true">
|
|
741
|
-
<dc:Bounds x="
|
|
824
|
+
<dc:Bounds x="1513" y="415" width="50" height="50" />
|
|
742
825
|
<bpmndi:BPMNLabel>
|
|
743
|
-
<dc:Bounds x="
|
|
826
|
+
<dc:Bounds x="1494" y="470" width="88" height="14" />
|
|
744
827
|
</bpmndi:BPMNLabel>
|
|
745
828
|
</bpmndi:BPMNShape>
|
|
746
829
|
<bpmndi:BPMNShape id="BPMNShape_SP_rebase" bpmnElement="SP_rebase">
|
|
747
|
-
<dc:Bounds x="
|
|
830
|
+
<dc:Bounds x="1688" y="1360" width="100" height="80" />
|
|
748
831
|
</bpmndi:BPMNShape>
|
|
749
832
|
<bpmndi:BPMNShape id="BPMNShape_gw-rebase-outcome" bpmnElement="gw-rebase-outcome" isMarkerVisible="true">
|
|
750
|
-
<dc:Bounds x="
|
|
833
|
+
<dc:Bounds x="1888" y="1375" width="50" height="50" />
|
|
751
834
|
<bpmndi:BPMNLabel>
|
|
752
|
-
<dc:Bounds x="
|
|
835
|
+
<dc:Bounds x="1922" y="1430" width="63" height="28" />
|
|
753
836
|
</bpmndi:BPMNLabel>
|
|
754
837
|
</bpmndi:BPMNShape>
|
|
755
838
|
<bpmndi:BPMNShape id="BPMNShape_record-merge-dep" bpmnElement="record-merge-dep">
|
|
756
|
-
<dc:Bounds x="
|
|
839
|
+
<dc:Bounds x="2038" y="1200" width="100" height="80" />
|
|
757
840
|
</bpmndi:BPMNShape>
|
|
758
841
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_start" bpmnElement="f_m_start">
|
|
759
842
|
<di:waypoint x="116" y="280" />
|
|
@@ -767,321 +850,360 @@
|
|
|
767
850
|
<di:waypoint x="452" y="280" />
|
|
768
851
|
<di:waypoint x="552" y="280" />
|
|
769
852
|
</bpmndi:BPMNEdge>
|
|
853
|
+
<bpmndi:BPMNEdge id="BPMNEdge_f_me_wait" bpmnElement="f_me_wait">
|
|
854
|
+
<di:waypoint x="602" y="280" />
|
|
855
|
+
<di:waypoint x="702" y="280" />
|
|
856
|
+
</bpmndi:BPMNEdge>
|
|
857
|
+
<bpmndi:BPMNEdge id="BPMNEdge_f_mw_ready" bpmnElement="f_mw_ready">
|
|
858
|
+
<di:waypoint x="752" y="280" />
|
|
859
|
+
<di:waypoint x="852" y="280" />
|
|
860
|
+
</bpmndi:BPMNEdge>
|
|
770
861
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_ready" bpmnElement="f_m_ready">
|
|
771
|
-
<di:waypoint x="
|
|
772
|
-
<di:waypoint x="
|
|
862
|
+
<di:waypoint x="888" y="280" />
|
|
863
|
+
<di:waypoint x="1338" y="280" />
|
|
773
864
|
</bpmndi:BPMNEdge>
|
|
774
865
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_mReady" bpmnElement="f_m_mReady">
|
|
775
|
-
<di:waypoint x="
|
|
776
|
-
<di:waypoint x="
|
|
866
|
+
<di:waypoint x="1388" y="280" />
|
|
867
|
+
<di:waypoint x="1488" y="280" />
|
|
777
868
|
<bpmndi:BPMNLabel>
|
|
778
|
-
<dc:Bounds x="
|
|
869
|
+
<dc:Bounds x="1419" y="258" width="39" height="14" />
|
|
779
870
|
</bpmndi:BPMNLabel>
|
|
780
871
|
</bpmndi:BPMNEdge>
|
|
781
872
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_attempt" bpmnElement="f_m_attempt">
|
|
782
|
-
<di:waypoint x="
|
|
783
|
-
<di:waypoint x="
|
|
873
|
+
<di:waypoint x="1588" y="280" />
|
|
874
|
+
<di:waypoint x="1713" y="280" />
|
|
784
875
|
</bpmndi:BPMNEdge>
|
|
785
876
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_gMerged" bpmnElement="f_m_gMerged">
|
|
786
|
-
<di:waypoint x="
|
|
787
|
-
<di:waypoint x="
|
|
877
|
+
<di:waypoint x="1763" y="280" />
|
|
878
|
+
<di:waypoint x="2238" y="280" />
|
|
788
879
|
<bpmndi:BPMNLabel>
|
|
789
|
-
<dc:Bounds x="
|
|
880
|
+
<dc:Bounds x="2051" y="258" width="49" height="14" />
|
|
790
881
|
</bpmndi:BPMNLabel>
|
|
791
882
|
</bpmndi:BPMNEdge>
|
|
792
883
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_done" bpmnElement="f_m_done">
|
|
793
|
-
<di:waypoint x="
|
|
794
|
-
<di:waypoint x="
|
|
884
|
+
<di:waypoint x="2338" y="280" />
|
|
885
|
+
<di:waypoint x="2445" y="280" />
|
|
795
886
|
</bpmndi:BPMNEdge>
|
|
796
887
|
<bpmndi:BPMNEdge id="BPMNEdge_f_ci_rearm" bpmnElement="f_ci_rearm">
|
|
797
|
-
<di:waypoint x="
|
|
798
|
-
<di:waypoint x="
|
|
888
|
+
<di:waypoint x="1913" y="1265" />
|
|
889
|
+
<di:waypoint x="1913" y="1300" />
|
|
799
890
|
<di:waypoint x="402" y="1300" />
|
|
800
891
|
<di:waypoint x="402" y="320" />
|
|
801
892
|
<bpmndi:BPMNLabel>
|
|
802
|
-
<dc:Bounds x="
|
|
893
|
+
<dc:Bounds x="446" y="1239" width="88" height="56" />
|
|
803
894
|
</bpmndi:BPMNLabel>
|
|
804
895
|
</bpmndi:BPMNEdge>
|
|
805
896
|
<bpmndi:BPMNEdge id="BPMNEdge_f_reb_rearm" bpmnElement="f_reb_rearm">
|
|
806
|
-
<di:waypoint x="
|
|
807
|
-
<di:waypoint x="
|
|
897
|
+
<di:waypoint x="1913" y="1425" />
|
|
898
|
+
<di:waypoint x="1913" y="1460" />
|
|
808
899
|
<di:waypoint x="402" y="1460" />
|
|
809
900
|
<di:waypoint x="402" y="320" />
|
|
810
901
|
<bpmndi:BPMNLabel>
|
|
811
|
-
<dc:Bounds x="
|
|
902
|
+
<dc:Bounds x="1119" y="1413" width="78" height="42" />
|
|
903
|
+
</bpmndi:BPMNLabel>
|
|
904
|
+
</bpmndi:BPMNEdge>
|
|
905
|
+
<bpmndi:BPMNEdge id="BPMNEdge_f_msp_giveup" bpmnElement="f_msp_giveup">
|
|
906
|
+
<di:waypoint x="1238" y="440" />
|
|
907
|
+
<di:waypoint x="1258" y="440" />
|
|
908
|
+
<di:waypoint x="1258" y="465" />
|
|
909
|
+
<di:waypoint x="1651" y="465" />
|
|
910
|
+
<di:waypoint x="1651" y="1060" />
|
|
911
|
+
<di:waypoint x="2038" y="1060" />
|
|
912
|
+
<bpmndi:BPMNLabel>
|
|
913
|
+
<dc:Bounds x="1378" y="470" width="85" height="28" />
|
|
812
914
|
</bpmndi:BPMNLabel>
|
|
813
915
|
</bpmndi:BPMNEdge>
|
|
814
916
|
<bpmndi:BPMNEdge id="BPMNEdge_f_ci_giveup" bpmnElement="f_ci_giveup">
|
|
815
|
-
<di:waypoint x="
|
|
816
|
-
<di:waypoint x="
|
|
917
|
+
<di:waypoint x="1563" y="1080" />
|
|
918
|
+
<di:waypoint x="2038" y="1080" />
|
|
817
919
|
<bpmndi:BPMNLabel>
|
|
818
|
-
<dc:Bounds x="
|
|
920
|
+
<dc:Bounds x="1757" y="1085" width="67" height="28" />
|
|
819
921
|
</bpmndi:BPMNLabel>
|
|
820
922
|
</bpmndi:BPMNEdge>
|
|
821
923
|
<bpmndi:BPMNEdge id="BPMNEdge_f_reb_giveup" bpmnElement="f_reb_giveup">
|
|
822
|
-
<di:waypoint x="
|
|
823
|
-
<di:waypoint x="
|
|
824
|
-
<di:waypoint x="
|
|
825
|
-
<di:waypoint x="
|
|
826
|
-
<di:waypoint x="
|
|
827
|
-
<di:waypoint x="
|
|
924
|
+
<di:waypoint x="1563" y="440" />
|
|
925
|
+
<di:waypoint x="1583" y="440" />
|
|
926
|
+
<di:waypoint x="1583" y="465" />
|
|
927
|
+
<di:waypoint x="1813" y="465" />
|
|
928
|
+
<di:waypoint x="1813" y="1060" />
|
|
929
|
+
<di:waypoint x="2038" y="1060" />
|
|
828
930
|
<bpmndi:BPMNLabel>
|
|
829
|
-
<dc:Bounds x="
|
|
931
|
+
<dc:Bounds x="1699" y="432" width="67" height="28" />
|
|
830
932
|
</bpmndi:BPMNLabel>
|
|
831
933
|
</bpmndi:BPMNEdge>
|
|
832
934
|
<bpmndi:BPMNEdge id="BPMNEdge_f_mr_giveup" bpmnElement="f_mr_giveup">
|
|
833
|
-
<di:waypoint x="
|
|
834
|
-
<di:waypoint x="
|
|
935
|
+
<di:waypoint x="1938" y="760" />
|
|
936
|
+
<di:waypoint x="2038" y="760" />
|
|
835
937
|
<bpmndi:BPMNLabel>
|
|
836
|
-
<dc:Bounds x="
|
|
938
|
+
<dc:Bounds x="1935" y="687" width="67" height="28" />
|
|
837
939
|
</bpmndi:BPMNLabel>
|
|
838
940
|
</bpmndi:BPMNEdge>
|
|
839
941
|
<bpmndi:BPMNEdge id="BPMNEdge_f_eg_landed" bpmnElement="f_eg_landed">
|
|
840
|
-
<di:waypoint x="
|
|
841
|
-
<di:waypoint x="
|
|
942
|
+
<di:waypoint x="1938" y="440" />
|
|
943
|
+
<di:waypoint x="2070" y="440" />
|
|
842
944
|
</bpmndi:BPMNEdge>
|
|
843
945
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_landed" bpmnElement="f_m_landed">
|
|
844
|
-
<di:waypoint x="
|
|
845
|
-
<di:waypoint x="
|
|
846
|
-
<di:waypoint x="
|
|
946
|
+
<di:waypoint x="2106" y="440" />
|
|
947
|
+
<di:waypoint x="2288" y="440" />
|
|
948
|
+
<di:waypoint x="2288" y="320" />
|
|
847
949
|
</bpmndi:BPMNEdge>
|
|
848
950
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_escC" bpmnElement="f_m_escC">
|
|
849
|
-
<di:waypoint x="
|
|
850
|
-
<di:waypoint x="
|
|
851
|
-
<di:waypoint x="
|
|
951
|
+
<di:waypoint x="2138" y="1080" />
|
|
952
|
+
<di:waypoint x="2638" y="1080" />
|
|
953
|
+
<di:waypoint x="2638" y="160" />
|
|
852
954
|
</bpmndi:BPMNEdge>
|
|
853
955
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_escA" bpmnElement="f_m_escA">
|
|
854
|
-
<di:waypoint x="
|
|
855
|
-
<di:waypoint x="
|
|
856
|
-
<di:waypoint x="
|
|
857
|
-
<di:waypoint x="
|
|
858
|
-
<di:waypoint x="
|
|
859
|
-
<di:waypoint x="
|
|
956
|
+
<di:waypoint x="2138" y="760" />
|
|
957
|
+
<di:waypoint x="2501" y="760" />
|
|
958
|
+
<di:waypoint x="2501" y="145" />
|
|
959
|
+
<di:waypoint x="2508" y="145" />
|
|
960
|
+
<di:waypoint x="2508" y="120" />
|
|
961
|
+
<di:waypoint x="2488" y="120" />
|
|
860
962
|
</bpmndi:BPMNEdge>
|
|
861
963
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_escReenter" bpmnElement="f_m_escReenter">
|
|
862
|
-
<di:waypoint x="
|
|
863
|
-
<di:waypoint x="
|
|
964
|
+
<di:waypoint x="2463" y="95" />
|
|
965
|
+
<di:waypoint x="2463" y="60" />
|
|
864
966
|
<di:waypoint x="402" y="60" />
|
|
865
967
|
<di:waypoint x="402" y="240" />
|
|
866
968
|
<bpmndi:BPMNLabel>
|
|
867
|
-
<dc:Bounds x="
|
|
969
|
+
<dc:Bounds x="1396" y="13" width="74" height="42" />
|
|
868
970
|
</bpmndi:BPMNLabel>
|
|
869
971
|
</bpmndi:BPMNEdge>
|
|
870
972
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_answerRecord" bpmnElement="f_m_answerRecord">
|
|
871
|
-
<di:waypoint x="
|
|
872
|
-
<di:waypoint x="
|
|
973
|
+
<di:waypoint x="2688" y="120" />
|
|
974
|
+
<di:waypoint x="2788" y="120" />
|
|
873
975
|
</bpmndi:BPMNEdge>
|
|
874
976
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_answer" bpmnElement="f_m_answer">
|
|
875
|
-
<di:waypoint x="
|
|
876
|
-
<di:waypoint x="
|
|
977
|
+
<di:waypoint x="2838" y="160" />
|
|
978
|
+
<di:waypoint x="2838" y="340" />
|
|
877
979
|
<di:waypoint x="402" y="340" />
|
|
878
980
|
<di:waypoint x="402" y="320" />
|
|
879
981
|
</bpmndi:BPMNEdge>
|
|
880
982
|
<bpmndi:BPMNEdge id="BPMNEdge_f_ci_escAttempt" bpmnElement="f_ci_escAttempt">
|
|
881
|
-
<di:waypoint x="
|
|
882
|
-
<di:waypoint x="
|
|
883
|
-
<di:waypoint x="
|
|
884
|
-
<di:waypoint x="
|
|
885
|
-
<di:waypoint x="
|
|
886
|
-
<di:waypoint x="
|
|
887
|
-
<di:waypoint x="
|
|
888
|
-
<di:waypoint x="
|
|
889
|
-
<di:waypoint x="
|
|
890
|
-
<bpmndi:BPMNLabel>
|
|
891
|
-
<dc:Bounds x="
|
|
983
|
+
<di:waypoint x="1913" y="1215" />
|
|
984
|
+
<di:waypoint x="1913" y="1195" />
|
|
985
|
+
<di:waypoint x="1168" y="1195" />
|
|
986
|
+
<di:waypoint x="1168" y="402" />
|
|
987
|
+
<di:waypoint x="1868" y="402" />
|
|
988
|
+
<di:waypoint x="1868" y="478" />
|
|
989
|
+
<di:waypoint x="2018" y="478" />
|
|
990
|
+
<di:waypoint x="2018" y="740" />
|
|
991
|
+
<di:waypoint x="2038" y="740" />
|
|
992
|
+
<bpmndi:BPMNLabel>
|
|
993
|
+
<dc:Bounds x="1551" y="1203" width="60" height="14" />
|
|
892
994
|
</bpmndi:BPMNLabel>
|
|
893
995
|
</bpmndi:BPMNEdge>
|
|
894
996
|
<bpmndi:BPMNEdge id="BPMNEdge_f_reb_waitDep" bpmnElement="f_reb_waitDep">
|
|
895
|
-
<di:waypoint x="
|
|
896
|
-
<di:waypoint x="
|
|
897
|
-
<di:waypoint x="
|
|
997
|
+
<di:waypoint x="1938" y="1400" />
|
|
998
|
+
<di:waypoint x="2088" y="1400" />
|
|
999
|
+
<di:waypoint x="2088" y="1280" />
|
|
898
1000
|
<bpmndi:BPMNLabel>
|
|
899
|
-
<dc:Bounds x="
|
|
1001
|
+
<dc:Bounds x="1996" y="1405" width="75" height="28" />
|
|
900
1002
|
</bpmndi:BPMNLabel>
|
|
901
1003
|
</bpmndi:BPMNEdge>
|
|
902
1004
|
<bpmndi:BPMNEdge id="BPMNEdge_f_reb_escAttempt" bpmnElement="f_reb_escAttempt">
|
|
903
|
-
<di:waypoint x="
|
|
904
|
-
<di:waypoint x="
|
|
905
|
-
<di:waypoint x="
|
|
906
|
-
<di:waypoint x="
|
|
907
|
-
<di:waypoint x="
|
|
908
|
-
<di:waypoint x="
|
|
909
|
-
<di:waypoint x="
|
|
910
|
-
<di:waypoint x="
|
|
911
|
-
<di:waypoint x="
|
|
912
|
-
<bpmndi:BPMNLabel>
|
|
913
|
-
<dc:Bounds x="
|
|
1005
|
+
<di:waypoint x="1913" y="1375" />
|
|
1006
|
+
<di:waypoint x="1913" y="1355" />
|
|
1007
|
+
<di:waypoint x="1168" y="1355" />
|
|
1008
|
+
<di:waypoint x="1168" y="402" />
|
|
1009
|
+
<di:waypoint x="1868" y="402" />
|
|
1010
|
+
<di:waypoint x="1868" y="478" />
|
|
1011
|
+
<di:waypoint x="2018" y="478" />
|
|
1012
|
+
<di:waypoint x="2018" y="740" />
|
|
1013
|
+
<di:waypoint x="2038" y="740" />
|
|
1014
|
+
<bpmndi:BPMNLabel>
|
|
1015
|
+
<dc:Bounds x="1173" y="1234" width="64" height="42" />
|
|
914
1016
|
</bpmndi:BPMNLabel>
|
|
915
1017
|
</bpmndi:BPMNEdge>
|
|
916
1018
|
<bpmndi:BPMNEdge id="BPMNEdge_f_reb_escConflict" bpmnElement="f_reb_escConflict">
|
|
917
|
-
<di:waypoint x="
|
|
918
|
-
<di:waypoint x="
|
|
919
|
-
<di:waypoint x="
|
|
920
|
-
<di:waypoint x="
|
|
1019
|
+
<di:waypoint x="1938" y="1400" />
|
|
1020
|
+
<di:waypoint x="2018" y="1400" />
|
|
1021
|
+
<di:waypoint x="2018" y="1080" />
|
|
1022
|
+
<di:waypoint x="2038" y="1080" />
|
|
921
1023
|
<bpmndi:BPMNLabel>
|
|
922
|
-
<dc:Bounds x="
|
|
1024
|
+
<dc:Bounds x="1953" y="1246" width="60" height="28" />
|
|
1025
|
+
</bpmndi:BPMNLabel>
|
|
1026
|
+
</bpmndi:BPMNEdge>
|
|
1027
|
+
<bpmndi:BPMNEdge id="BPMNEdge_f_mw_timeout" bpmnElement="f_mw_timeout">
|
|
1028
|
+
<di:waypoint x="727" y="305" />
|
|
1029
|
+
<di:waypoint x="727" y="440" />
|
|
1030
|
+
<di:waypoint x="852" y="440" />
|
|
1031
|
+
</bpmndi:BPMNEdge>
|
|
1032
|
+
<bpmndi:BPMNEdge id="BPMNEdge_f_msp_rederive" bpmnElement="f_msp_rederive">
|
|
1033
|
+
<di:waypoint x="1213" y="415" />
|
|
1034
|
+
<di:waypoint x="1213" y="280" />
|
|
1035
|
+
<di:waypoint x="1338" y="280" />
|
|
1036
|
+
<bpmndi:BPMNLabel>
|
|
1037
|
+
<dc:Bounds x="1218" y="354" width="85" height="28" />
|
|
923
1038
|
</bpmndi:BPMNLabel>
|
|
924
1039
|
</bpmndi:BPMNEdge>
|
|
925
1040
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_mCiFix" bpmnElement="f_m_mCiFix">
|
|
926
|
-
<di:waypoint x="
|
|
927
|
-
<di:waypoint x="
|
|
928
|
-
<di:waypoint x="
|
|
1041
|
+
<di:waypoint x="1363" y="305" />
|
|
1042
|
+
<di:waypoint x="1363" y="1080" />
|
|
1043
|
+
<di:waypoint x="1513" y="1080" />
|
|
929
1044
|
<bpmndi:BPMNLabel>
|
|
930
|
-
<dc:Bounds x="
|
|
1045
|
+
<dc:Bounds x="1368" y="739" width="60" height="42" />
|
|
931
1046
|
</bpmndi:BPMNLabel>
|
|
932
1047
|
</bpmndi:BPMNEdge>
|
|
933
1048
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_mRebase" bpmnElement="f_m_mRebase">
|
|
934
|
-
<di:waypoint x="
|
|
935
|
-
<di:waypoint x="
|
|
936
|
-
<di:waypoint x="
|
|
1049
|
+
<di:waypoint x="1363" y="305" />
|
|
1050
|
+
<di:waypoint x="1363" y="440" />
|
|
1051
|
+
<di:waypoint x="1513" y="440" />
|
|
937
1052
|
<bpmndi:BPMNLabel>
|
|
938
|
-
<dc:Bounds x="
|
|
1053
|
+
<dc:Bounds x="1408" y="418" width="60" height="14" />
|
|
939
1054
|
</bpmndi:BPMNLabel>
|
|
940
1055
|
</bpmndi:BPMNEdge>
|
|
941
1056
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_mBlocked" bpmnElement="f_m_mBlocked">
|
|
942
|
-
<di:waypoint x="
|
|
943
|
-
<di:waypoint x="
|
|
944
|
-
<di:waypoint x="
|
|
945
|
-
<di:waypoint x="
|
|
946
|
-
<di:waypoint x="
|
|
1057
|
+
<di:waypoint x="1363" y="305" />
|
|
1058
|
+
<di:waypoint x="1363" y="325" />
|
|
1059
|
+
<di:waypoint x="1875" y="325" />
|
|
1060
|
+
<di:waypoint x="1875" y="805" />
|
|
1061
|
+
<di:waypoint x="2126" y="805" />
|
|
1062
|
+
<di:waypoint x="2126" y="1040" />
|
|
947
1063
|
<bpmndi:BPMNLabel>
|
|
948
|
-
<dc:Bounds x="
|
|
1064
|
+
<dc:Bounds x="1880" y="558" width="85" height="14" />
|
|
949
1065
|
</bpmndi:BPMNLabel>
|
|
950
1066
|
</bpmndi:BPMNEdge>
|
|
951
1067
|
<bpmndi:BPMNEdge id="BPMNEdge_f_ci_go" bpmnElement="f_ci_go">
|
|
952
|
-
<di:waypoint x="
|
|
953
|
-
<di:waypoint x="
|
|
954
|
-
<di:waypoint x="
|
|
1068
|
+
<di:waypoint x="1538" y="1105" />
|
|
1069
|
+
<di:waypoint x="1538" y="1240" />
|
|
1070
|
+
<di:waypoint x="1688" y="1240" />
|
|
955
1071
|
<bpmndi:BPMNLabel>
|
|
956
|
-
<dc:Bounds x="
|
|
1072
|
+
<dc:Bounds x="1543" y="1159" width="49" height="28" />
|
|
957
1073
|
</bpmndi:BPMNLabel>
|
|
958
1074
|
</bpmndi:BPMNEdge>
|
|
959
1075
|
<bpmndi:BPMNEdge id="BPMNEdge_f_reb_go" bpmnElement="f_reb_go">
|
|
960
|
-
<di:waypoint x="
|
|
961
|
-
<di:waypoint x="
|
|
962
|
-
<di:waypoint x="
|
|
963
|
-
<di:waypoint x="
|
|
1076
|
+
<di:waypoint x="1563" y="440" />
|
|
1077
|
+
<di:waypoint x="1668" y="440" />
|
|
1078
|
+
<di:waypoint x="1668" y="1400" />
|
|
1079
|
+
<di:waypoint x="1688" y="1400" />
|
|
964
1080
|
<bpmndi:BPMNLabel>
|
|
965
|
-
<dc:Bounds x="
|
|
1081
|
+
<dc:Bounds x="1673" y="906" width="49" height="28" />
|
|
966
1082
|
</bpmndi:BPMNLabel>
|
|
967
1083
|
</bpmndi:BPMNEdge>
|
|
968
1084
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_gQueued" bpmnElement="f_m_gQueued">
|
|
969
|
-
<di:waypoint x="
|
|
970
|
-
<di:waypoint x="
|
|
971
|
-
<di:waypoint x="
|
|
1085
|
+
<di:waypoint x="1763" y="280" />
|
|
1086
|
+
<di:waypoint x="1913" y="280" />
|
|
1087
|
+
<di:waypoint x="1913" y="415" />
|
|
972
1088
|
<bpmndi:BPMNLabel>
|
|
973
|
-
<dc:Bounds x="
|
|
1089
|
+
<dc:Bounds x="1918" y="361" width="46" height="14" />
|
|
974
1090
|
</bpmndi:BPMNLabel>
|
|
975
1091
|
</bpmndi:BPMNEdge>
|
|
976
1092
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_gRetry" bpmnElement="f_m_gRetry">
|
|
977
|
-
<di:waypoint x="
|
|
978
|
-
<di:waypoint x="
|
|
979
|
-
<di:waypoint x="
|
|
980
|
-
<di:waypoint x="
|
|
1093
|
+
<di:waypoint x="1763" y="280" />
|
|
1094
|
+
<di:waypoint x="1868" y="280" />
|
|
1095
|
+
<di:waypoint x="1868" y="760" />
|
|
1096
|
+
<di:waypoint x="1888" y="760" />
|
|
981
1097
|
<bpmndi:BPMNLabel>
|
|
982
|
-
<dc:Bounds x="
|
|
1098
|
+
<dc:Bounds x="1785" y="419" width="78" height="28" />
|
|
983
1099
|
</bpmndi:BPMNLabel>
|
|
984
1100
|
</bpmndi:BPMNEdge>
|
|
985
1101
|
<bpmndi:BPMNEdge id="BPMNEdge_f_eg_evicted" bpmnElement="f_eg_evicted">
|
|
986
|
-
<di:waypoint x="
|
|
987
|
-
<di:waypoint x="
|
|
988
|
-
<di:waypoint x="
|
|
989
|
-
<di:waypoint x="
|
|
1102
|
+
<di:waypoint x="1938" y="440" />
|
|
1103
|
+
<di:waypoint x="2050" y="440" />
|
|
1104
|
+
<di:waypoint x="2050" y="600" />
|
|
1105
|
+
<di:waypoint x="2070" y="600" />
|
|
990
1106
|
</bpmndi:BPMNEdge>
|
|
991
1107
|
<bpmndi:BPMNEdge id="BPMNEdge_f_eg_landedTimeout" bpmnElement="f_eg_landedTimeout">
|
|
992
|
-
<di:waypoint x="
|
|
993
|
-
<di:waypoint x="
|
|
994
|
-
<di:waypoint x="
|
|
995
|
-
<di:waypoint x="
|
|
996
|
-
<di:waypoint x="
|
|
997
|
-
<di:waypoint x="
|
|
998
|
-
<di:waypoint x="
|
|
999
|
-
<di:waypoint x="
|
|
1000
|
-
<di:waypoint x="
|
|
1001
|
-
<di:waypoint x="
|
|
1002
|
-
<di:waypoint x="
|
|
1108
|
+
<di:waypoint x="1938" y="440" />
|
|
1109
|
+
<di:waypoint x="1958" y="440" />
|
|
1110
|
+
<di:waypoint x="1958" y="415" />
|
|
1111
|
+
<di:waypoint x="2070" y="415" />
|
|
1112
|
+
<di:waypoint x="2070" y="478" />
|
|
1113
|
+
<di:waypoint x="2508" y="478" />
|
|
1114
|
+
<di:waypoint x="2508" y="235" />
|
|
1115
|
+
<di:waypoint x="2070" y="235" />
|
|
1116
|
+
<di:waypoint x="2070" y="158" />
|
|
1117
|
+
<di:waypoint x="2088" y="158" />
|
|
1118
|
+
<di:waypoint x="2088" y="138" />
|
|
1003
1119
|
</bpmndi:BPMNEdge>
|
|
1004
1120
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_gBlocked" bpmnElement="f_m_gBlocked">
|
|
1005
|
-
<di:waypoint x="
|
|
1006
|
-
<di:waypoint x="
|
|
1007
|
-
<di:waypoint x="
|
|
1008
|
-
<di:waypoint x="
|
|
1009
|
-
<di:waypoint x="
|
|
1010
|
-
<di:waypoint x="
|
|
1011
|
-
<di:waypoint x="
|
|
1012
|
-
<di:waypoint x="
|
|
1121
|
+
<di:waypoint x="1763" y="280" />
|
|
1122
|
+
<di:waypoint x="1783" y="280" />
|
|
1123
|
+
<di:waypoint x="1783" y="305" />
|
|
1124
|
+
<di:waypoint x="1868" y="305" />
|
|
1125
|
+
<di:waypoint x="1868" y="820" />
|
|
1126
|
+
<di:waypoint x="2018" y="820" />
|
|
1127
|
+
<di:waypoint x="2018" y="780" />
|
|
1128
|
+
<di:waypoint x="2038" y="780" />
|
|
1013
1129
|
<bpmndi:BPMNLabel>
|
|
1014
|
-
<dc:Bounds x="
|
|
1130
|
+
<dc:Bounds x="1977" y="828" width="53" height="14" />
|
|
1015
1131
|
</bpmndi:BPMNLabel>
|
|
1016
1132
|
</bpmndi:BPMNEdge>
|
|
1017
1133
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_gAbandoned" bpmnElement="f_m_gAbandoned">
|
|
1018
|
-
<di:waypoint x="
|
|
1019
|
-
<di:waypoint x="
|
|
1020
|
-
<di:waypoint x="
|
|
1021
|
-
<di:waypoint x="
|
|
1022
|
-
<di:waypoint x="
|
|
1023
|
-
<di:waypoint x="
|
|
1024
|
-
<di:waypoint x="
|
|
1025
|
-
<di:waypoint x="1263" y="882" />
|
|
1026
|
-
<di:waypoint x="1263" y="902" />
|
|
1134
|
+
<di:waypoint x="1738" y="255" />
|
|
1135
|
+
<di:waypoint x="1738" y="242" />
|
|
1136
|
+
<di:waypoint x="2126" y="242" />
|
|
1137
|
+
<di:waypoint x="2126" y="220" />
|
|
1138
|
+
<di:waypoint x="2568" y="220" />
|
|
1139
|
+
<di:waypoint x="2568" y="920" />
|
|
1140
|
+
<di:waypoint x="1931" y="920" />
|
|
1027
1141
|
<bpmndi:BPMNLabel>
|
|
1028
|
-
<dc:Bounds x="
|
|
1142
|
+
<dc:Bounds x="2306" y="187" width="82" height="28" />
|
|
1029
1143
|
</bpmndi:BPMNLabel>
|
|
1030
1144
|
</bpmndi:BPMNEdge>
|
|
1031
1145
|
<bpmndi:BPMNEdge id="BPMNEdge_f_ci_waitDep" bpmnElement="f_ci_waitDep">
|
|
1032
|
-
<di:waypoint x="
|
|
1033
|
-
<di:waypoint x="
|
|
1146
|
+
<di:waypoint x="1938" y="1240" />
|
|
1147
|
+
<di:waypoint x="2038" y="1240" />
|
|
1034
1148
|
<bpmndi:BPMNLabel>
|
|
1035
|
-
<dc:Bounds x="
|
|
1149
|
+
<dc:Bounds x="1931" y="1167" width="75" height="28" />
|
|
1036
1150
|
</bpmndi:BPMNLabel>
|
|
1037
1151
|
</bpmndi:BPMNEdge>
|
|
1152
|
+
<bpmndi:BPMNEdge id="BPMNEdge_f_mw_stall" bpmnElement="f_mw_stall">
|
|
1153
|
+
<di:waypoint x="888" y="440" />
|
|
1154
|
+
<di:waypoint x="988" y="440" />
|
|
1155
|
+
</bpmndi:BPMNEdge>
|
|
1156
|
+
<bpmndi:BPMNEdge id="BPMNEdge_f_msp_gate" bpmnElement="f_msp_gate">
|
|
1157
|
+
<di:waypoint x="1088" y="440" />
|
|
1158
|
+
<di:waypoint x="1188" y="440" />
|
|
1159
|
+
</bpmndi:BPMNEdge>
|
|
1038
1160
|
<bpmndi:BPMNEdge id="BPMNEdge_f_ci_spdone" bpmnElement="f_ci_spdone">
|
|
1039
|
-
<di:waypoint x="
|
|
1040
|
-
<di:waypoint x="
|
|
1161
|
+
<di:waypoint x="1788" y="1240" />
|
|
1162
|
+
<di:waypoint x="1888" y="1240" />
|
|
1041
1163
|
</bpmndi:BPMNEdge>
|
|
1042
1164
|
<bpmndi:BPMNEdge id="BPMNEdge_f_reb_spdone" bpmnElement="f_reb_spdone">
|
|
1043
|
-
<di:waypoint x="
|
|
1044
|
-
<di:waypoint x="
|
|
1165
|
+
<di:waypoint x="1788" y="1400" />
|
|
1166
|
+
<di:waypoint x="1888" y="1400" />
|
|
1045
1167
|
</bpmndi:BPMNEdge>
|
|
1046
1168
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_landedStalled" bpmnElement="f_m_landedStalled">
|
|
1047
|
-
<di:waypoint x="
|
|
1048
|
-
<di:waypoint x="
|
|
1169
|
+
<di:waypoint x="2106" y="120" />
|
|
1170
|
+
<di:waypoint x="2238" y="120" />
|
|
1049
1171
|
</bpmndi:BPMNEdge>
|
|
1050
1172
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_escLanded" bpmnElement="f_m_escLanded">
|
|
1051
|
-
<di:waypoint x="
|
|
1052
|
-
<di:waypoint x="
|
|
1173
|
+
<di:waypoint x="2338" y="120" />
|
|
1174
|
+
<di:waypoint x="2438" y="120" />
|
|
1053
1175
|
</bpmndi:BPMNEdge>
|
|
1054
1176
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_escWait" bpmnElement="f_m_escWait">
|
|
1055
|
-
<di:waypoint x="
|
|
1056
|
-
<di:waypoint x="
|
|
1177
|
+
<di:waypoint x="2488" y="120" />
|
|
1178
|
+
<di:waypoint x="2588" y="120" />
|
|
1057
1179
|
</bpmndi:BPMNEdge>
|
|
1058
1180
|
<bpmndi:BPMNEdge id="BPMNEdge_f_ci_recheck" bpmnElement="f_ci_recheck">
|
|
1059
|
-
<di:waypoint x="
|
|
1060
|
-
<di:waypoint x="
|
|
1061
|
-
<di:waypoint x="
|
|
1062
|
-
<di:waypoint x="
|
|
1181
|
+
<di:waypoint x="1913" y="1265" />
|
|
1182
|
+
<di:waypoint x="1913" y="1300" />
|
|
1183
|
+
<di:waypoint x="577" y="1300" />
|
|
1184
|
+
<di:waypoint x="577" y="305" />
|
|
1063
1185
|
<bpmndi:BPMNLabel>
|
|
1064
|
-
<dc:Bounds x="
|
|
1186
|
+
<dc:Bounds x="582" y="769" width="85" height="28" />
|
|
1065
1187
|
</bpmndi:BPMNLabel>
|
|
1066
1188
|
</bpmndi:BPMNEdge>
|
|
1067
1189
|
<bpmndi:BPMNEdge id="BPMNEdge_f_dep_rewait" bpmnElement="f_dep_rewait">
|
|
1068
|
-
<di:waypoint x="
|
|
1069
|
-
<di:waypoint x="
|
|
1190
|
+
<di:waypoint x="2088" y="1280" />
|
|
1191
|
+
<di:waypoint x="2088" y="1480" />
|
|
1070
1192
|
<di:waypoint x="234" y="1480" />
|
|
1071
1193
|
<di:waypoint x="234" y="298" />
|
|
1072
1194
|
</bpmndi:BPMNEdge>
|
|
1073
1195
|
<bpmndi:BPMNEdge id="BPMNEdge_f_mr_go" bpmnElement="f_mr_go">
|
|
1074
|
-
<di:waypoint x="
|
|
1075
|
-
<di:waypoint x="
|
|
1196
|
+
<di:waypoint x="1913" y="785" />
|
|
1197
|
+
<di:waypoint x="1913" y="805" />
|
|
1076
1198
|
<di:waypoint x="402" y="805" />
|
|
1077
1199
|
<di:waypoint x="402" y="320" />
|
|
1078
1200
|
<bpmndi:BPMNLabel>
|
|
1079
|
-
<dc:Bounds x="
|
|
1201
|
+
<dc:Bounds x="1114" y="772" width="49" height="28" />
|
|
1080
1202
|
</bpmndi:BPMNLabel>
|
|
1081
1203
|
</bpmndi:BPMNEdge>
|
|
1082
1204
|
<bpmndi:BPMNEdge id="BPMNEdge_f_m_evicted" bpmnElement="f_m_evicted">
|
|
1083
|
-
<di:waypoint x="
|
|
1084
|
-
<di:waypoint x="
|
|
1205
|
+
<di:waypoint x="2088" y="618" />
|
|
1206
|
+
<di:waypoint x="2088" y="638" />
|
|
1085
1207
|
<di:waypoint x="402" y="638" />
|
|
1086
1208
|
<di:waypoint x="402" y="320" />
|
|
1087
1209
|
</bpmndi:BPMNEdge>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// pr.merge-stall-probe — the mergeable-wait timer fired: the in-process poller never published
|
|
2
|
+
// `merge-ready` within `mergeableWaitTimeout` (it died across a redeploy, errored on this PR, or
|
|
3
|
+
// skipped the verdict), so the instance would otherwise sit wedged at `waiting_merge` forever with
|
|
4
|
+
// no timeout and no escalation (issue #636). This is the bounded-wait backstop: re-derive
|
|
5
|
+
// ground-truth mergeability directly from GitHub — reusing the poller's own classifier
|
|
6
|
+
// (`classifyMergeability` over a fresh `fetchPrState`, protocol-aware via `loadMergeProtocol`) — and
|
|
7
|
+
// emit `mergeState` so the *existing* `gw-mergeable` routes the token to the correct arm:
|
|
8
|
+
// • ready → attempt-merge
|
|
9
|
+
// • conflict → rebase arm
|
|
10
|
+
// • blocked → CI-fix arm
|
|
11
|
+
// • draft / waiting (still computing) → not-landable escalation (gw-mergeable default)
|
|
12
|
+
// The timer only guarantees the remediation machinery is ENTERED when the poller is dead; every
|
|
13
|
+
// downstream arm is the same one the live poller feeds. A bounded `mergeStallRounds` counter
|
|
14
|
+
// (incremented by this task's output mapping, capped by `mergeStallMax`) stops the probe from
|
|
15
|
+
// re-arming forever: once exhausted, `gw-merge-stall` routes to human escalation instead.
|
|
16
|
+
import type { AppJobHandler } from "@nanobpm/urban";
|
|
17
|
+
import { classifyMergeability, fetchPrState } from "../../app/github.ts";
|
|
18
|
+
import { loadMergeProtocol } from "../../app/mergeProtocol.ts";
|
|
19
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
20
|
+
|
|
21
|
+
// Input typed off the model data envelope (`MergeStallProbeIn` in merge-loop.bpmn) — ADR 0040.
|
|
22
|
+
type In = WorkerInputs["pr.merge-stall-probe"];
|
|
23
|
+
|
|
24
|
+
interface Out extends Record<string, unknown> {
|
|
25
|
+
// Mirrors the poller's `merge-ready {mergeState}` payload so the existing `gw-mergeable` FEEL
|
|
26
|
+
// routes it. `waiting` (GitHub still computing / no verdict) is surfaced verbatim; gw-mergeable
|
|
27
|
+
// has no `waiting` arm, so it falls to the not-landable default and escalates — the correct
|
|
28
|
+
// backstop when the poller is dead and 30 minutes on GitHub still cannot settle the PR.
|
|
29
|
+
mergeState: string;
|
|
30
|
+
failingChecks: number;
|
|
31
|
+
failingChecksList: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const handler: AppJobHandler<In, Out> = async (job, app) => {
|
|
35
|
+
const { repo, prNumber } = job.variables;
|
|
36
|
+
const token = process.env.GITHUB_TOKEN ?? "";
|
|
37
|
+
|
|
38
|
+
// Re-derive ground truth exactly as the poller does (service.ts block 2). Load the repo's merge
|
|
39
|
+
// protocol so the protocol-aware backstop (#392) gates a red DECLARED-required check even when
|
|
40
|
+
// GitHub reports UNSTABLE. Any transport hiccup is treated as "still waiting" — never a spurious
|
|
41
|
+
// ready/conflict verdict — so a bad read is surfaced as `waiting`, which `gw-mergeable` routes to
|
|
42
|
+
// its not-landable default (human escalation), erring toward a human rather than misrouting the
|
|
43
|
+
// token to a wrong remediation arm.
|
|
44
|
+
const st = await fetchPrState(repo, prNumber, token).catch(() => null);
|
|
45
|
+
if (st === null) {
|
|
46
|
+
return { mergeState: "waiting", failingChecks: 0, failingChecksList: "" };
|
|
47
|
+
}
|
|
48
|
+
const protocol = await loadMergeProtocol(repo, token).catch(() => null);
|
|
49
|
+
const mergeState = classifyMergeability(st, protocol ?? undefined);
|
|
50
|
+
|
|
51
|
+
app.log.info("merge-stall-probe: poller stalled — re-derived mergeability", {
|
|
52
|
+
prKey: job.variables.prKey,
|
|
53
|
+
mergeState,
|
|
54
|
+
mergeStateStatus: st.mergeStateStatus,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
mergeState,
|
|
59
|
+
// Carried for the CI-fix arm (mergeState "blocked" = a failed required check), mirroring the
|
|
60
|
+
// poller's `merge-ready` payload so fix-ci knows which gates to green.
|
|
61
|
+
failingChecks: st.failingChecks,
|
|
62
|
+
failingChecksList: st.failingCheckNames.join("\n"),
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default handler;
|