@nanobpm/nano-workforce 0.111.0 → 0.112.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 +14 -0
- package/app/deliveryGraph.ts +46 -0
- package/app/deliveryGraphCompiler.test.ts +275 -0
- package/app/deliveryGraphCompiler.ts +487 -0
- package/app/escalationTaxonomy.test.ts +1 -1
- package/app/escalationTaxonomy.ts +4 -2
- package/app/github.test.ts +225 -1
- package/app/github.ts +102 -2
- package/app/planFanoutCleanTerminal.test.ts +40 -0
- package/app/pollUserTasks.test.ts +208 -0
- package/app/readiness.test.ts +2 -0
- package/app/readiness.ts +3 -1
- package/app/service.ts +216 -198
- package/app/userTasks.test.ts +20 -2
- package/app/userTasks.ts +11 -4
- package/nano.app.json +4 -0
- package/openapi.yaml +248 -0
- package/operations/compileDeliveryGraph.test.ts +60 -0
- package/operations/compileDeliveryGraph.ts +35 -0
- package/package.json +1 -1
- package/resources/processes/plan-fanout.bpmn +161 -135
- package/workers/record-wave/worker.test.ts +181 -0
- package/workers/record-wave/worker.ts +44 -11
- package/workers/record-wave-escalation/worker.test.ts +95 -0
- package/workers/record-wave-escalation/worker.ts +69 -0
package/openapi.yaml
CHANGED
|
@@ -1498,6 +1498,221 @@ components:
|
|
|
1498
1498
|
type: string
|
|
1499
1499
|
minLength: 1
|
|
1500
1500
|
description: The dependent node's id — proceeds once `from` is observed.
|
|
1501
|
+
DeliveryCompileError:
|
|
1502
|
+
description: >-
|
|
1503
|
+
One semantic-validation or compile failure, path-qualified at the offending input
|
|
1504
|
+
(`nodes[2].kind`, `edges[1].from`, …). Mirrors a `validateDeliveryGraph` (`app/deliveryGraph.ts`)
|
|
1505
|
+
error stripped to the wire pair `{ path, message }` (the stable `code` stays server-side).
|
|
1506
|
+
type: object
|
|
1507
|
+
additionalProperties: false
|
|
1508
|
+
required:
|
|
1509
|
+
- path
|
|
1510
|
+
- message
|
|
1511
|
+
properties:
|
|
1512
|
+
path:
|
|
1513
|
+
type: string
|
|
1514
|
+
description: JSON-path pointer at the offending node/edge/fact.
|
|
1515
|
+
message:
|
|
1516
|
+
type: string
|
|
1517
|
+
description: Human-actionable description of the failure.
|
|
1518
|
+
ResolvedDeliveryNode:
|
|
1519
|
+
description: >-
|
|
1520
|
+
A normalised node in the compiled graph (ADR 0005 slice S1) — its `id`, `kind`, the
|
|
1521
|
+
deterministic BPMN `element` id it compiled to, the engine-native `calledElement` body it
|
|
1522
|
+
delegates to (Decision 2 — absent for a `human` user task), its typed `emits[]`, and the
|
|
1523
|
+
upstream node ids it `dependsOn` (sorted).
|
|
1524
|
+
type: object
|
|
1525
|
+
additionalProperties: false
|
|
1526
|
+
required:
|
|
1527
|
+
- id
|
|
1528
|
+
- kind
|
|
1529
|
+
- element
|
|
1530
|
+
- emits
|
|
1531
|
+
- dependsOn
|
|
1532
|
+
properties:
|
|
1533
|
+
id:
|
|
1534
|
+
type: string
|
|
1535
|
+
description: The author's node id (unique across the graph).
|
|
1536
|
+
kind:
|
|
1537
|
+
type: string
|
|
1538
|
+
enum: [agent, wait, human, connector]
|
|
1539
|
+
description: The node's kind from the closed allowlist (the trust boundary).
|
|
1540
|
+
element:
|
|
1541
|
+
type: string
|
|
1542
|
+
description: The deterministic BPMN element id this node compiled to (e.g. `n0`).
|
|
1543
|
+
calledElement:
|
|
1544
|
+
type: string
|
|
1545
|
+
description: >-
|
|
1546
|
+
The engine-native sub-process/call-activity target this node delegates to (Decision 2).
|
|
1547
|
+
Absent for a `human` node (a native user task, not a call activity).
|
|
1548
|
+
emits:
|
|
1549
|
+
type: array
|
|
1550
|
+
items:
|
|
1551
|
+
$ref: "#/components/schemas/DeliveryFact"
|
|
1552
|
+
description: The node's typed emitted facts (empty when it emits nothing).
|
|
1553
|
+
dependsOn:
|
|
1554
|
+
type: array
|
|
1555
|
+
items:
|
|
1556
|
+
type: string
|
|
1557
|
+
description: The ids of the upstream nodes this node depends on, sorted for determinism.
|
|
1558
|
+
ResolvedDeliveryEdge:
|
|
1559
|
+
description: >-
|
|
1560
|
+
A resolved dependency edge — the author's `from`/`to` plus the resolved upstream `fromNode`
|
|
1561
|
+
and, when the `from` was qualified (`<nodeId>.<fact>`), the referenced `fromFact`.
|
|
1562
|
+
type: object
|
|
1563
|
+
additionalProperties: false
|
|
1564
|
+
required:
|
|
1565
|
+
- from
|
|
1566
|
+
- to
|
|
1567
|
+
- fromNode
|
|
1568
|
+
properties:
|
|
1569
|
+
from:
|
|
1570
|
+
type: string
|
|
1571
|
+
description: The author's `from` endpoint verbatim (`<nodeId>` or `<nodeId>.<fact>`).
|
|
1572
|
+
to:
|
|
1573
|
+
type: string
|
|
1574
|
+
description: The dependent node's id.
|
|
1575
|
+
fromNode:
|
|
1576
|
+
type: string
|
|
1577
|
+
description: The resolved upstream node id.
|
|
1578
|
+
fromFact:
|
|
1579
|
+
type: string
|
|
1580
|
+
description: The referenced emitted fact, when the edge `from` was qualified.
|
|
1581
|
+
ResolvedDeliveryGraph:
|
|
1582
|
+
description: >-
|
|
1583
|
+
The normalised graph the compiler resolved from the input (ADR 0005 slice S1) — nodes and
|
|
1584
|
+
edges sorted deterministically so the same JSON always yields the same preview.
|
|
1585
|
+
type: object
|
|
1586
|
+
additionalProperties: false
|
|
1587
|
+
required:
|
|
1588
|
+
- nodes
|
|
1589
|
+
- edges
|
|
1590
|
+
properties:
|
|
1591
|
+
name:
|
|
1592
|
+
type: string
|
|
1593
|
+
description: The graph's optional human-readable label, echoed from the input.
|
|
1594
|
+
nodes:
|
|
1595
|
+
type: array
|
|
1596
|
+
items:
|
|
1597
|
+
$ref: "#/components/schemas/ResolvedDeliveryNode"
|
|
1598
|
+
description: The normalised nodes, sorted by id.
|
|
1599
|
+
edges:
|
|
1600
|
+
type: array
|
|
1601
|
+
items:
|
|
1602
|
+
$ref: "#/components/schemas/ResolvedDeliveryEdge"
|
|
1603
|
+
description: The resolved edges, sorted deterministically.
|
|
1604
|
+
DeliveryHumanStop:
|
|
1605
|
+
description: >-
|
|
1606
|
+
A `human` node extracted for the preview — a point where the graph STOPS for a person (or an
|
|
1607
|
+
agent answering on their behalf). Carries the instruction, the optional attached form, and the
|
|
1608
|
+
typed facts the node will emit on completion.
|
|
1609
|
+
type: object
|
|
1610
|
+
additionalProperties: false
|
|
1611
|
+
required:
|
|
1612
|
+
- nodeId
|
|
1613
|
+
- emits
|
|
1614
|
+
properties:
|
|
1615
|
+
nodeId:
|
|
1616
|
+
type: string
|
|
1617
|
+
description: The human node's id.
|
|
1618
|
+
prompt:
|
|
1619
|
+
type: string
|
|
1620
|
+
description: The instruction shown to the human/agent ("now do X"), when declared.
|
|
1621
|
+
formKey:
|
|
1622
|
+
type: string
|
|
1623
|
+
description: The explicitly-attached form key, when declared.
|
|
1624
|
+
emits:
|
|
1625
|
+
type: array
|
|
1626
|
+
items:
|
|
1627
|
+
$ref: "#/components/schemas/DeliveryFact"
|
|
1628
|
+
description: The typed facts this human node will hand forward (empty for a "click done" stop).
|
|
1629
|
+
DeliverySideEffect:
|
|
1630
|
+
description: >-
|
|
1631
|
+
A side-effecting action the compiled graph WILL perform (ADR 0005 slice S1 preview) — an
|
|
1632
|
+
`agent` job run or a `connector` outbound action. Read-only `wait` gates and `human` stops are
|
|
1633
|
+
NOT side effects (they are surfaced separately). Lets a human see "what it will do" before
|
|
1634
|
+
approving (Decision 7).
|
|
1635
|
+
type: object
|
|
1636
|
+
additionalProperties: false
|
|
1637
|
+
required:
|
|
1638
|
+
- nodeId
|
|
1639
|
+
- kind
|
|
1640
|
+
- description
|
|
1641
|
+
properties:
|
|
1642
|
+
nodeId:
|
|
1643
|
+
type: string
|
|
1644
|
+
description: The id of the node that performs the side effect.
|
|
1645
|
+
kind:
|
|
1646
|
+
type: string
|
|
1647
|
+
enum: [agent, connector]
|
|
1648
|
+
description: The side-effecting node kind.
|
|
1649
|
+
description:
|
|
1650
|
+
type: string
|
|
1651
|
+
description: Human-readable summary of the effect (e.g. "runs agent job `senior:feature`").
|
|
1652
|
+
dedupeKey:
|
|
1653
|
+
type: string
|
|
1654
|
+
description: The connector's idempotency key, when declared (at-least-once safety, Decision 7).
|
|
1655
|
+
CompileDeliveryGraphResult:
|
|
1656
|
+
description: >-
|
|
1657
|
+
A successful compile (ADR 0005 slice S1) — the PURE, side-effect-free preview a co-designing
|
|
1658
|
+
agent iterates against. Carries the compiled one-shot `bpmn` (compile-to-native artifact), a
|
|
1659
|
+
human-readable `diagram` (mermaid), the `resolved` normalised graph, and the extracted
|
|
1660
|
+
`humanNodes[]` (where it stops for a person) and `sideEffects[]` (what it will do). NOTHING is
|
|
1661
|
+
deployed — `compile` and `start` are separate doors (Decision 5/7).
|
|
1662
|
+
type: object
|
|
1663
|
+
additionalProperties: false
|
|
1664
|
+
required:
|
|
1665
|
+
- ok
|
|
1666
|
+
- diagram
|
|
1667
|
+
- bpmn
|
|
1668
|
+
- resolved
|
|
1669
|
+
- humanNodes
|
|
1670
|
+
- sideEffects
|
|
1671
|
+
properties:
|
|
1672
|
+
ok:
|
|
1673
|
+
type: boolean
|
|
1674
|
+
enum: [true]
|
|
1675
|
+
description: Discriminant — `true` for a successful compile.
|
|
1676
|
+
diagram:
|
|
1677
|
+
type: string
|
|
1678
|
+
description: A human-readable mermaid `flowchart` of the resolved graph.
|
|
1679
|
+
bpmn:
|
|
1680
|
+
type: string
|
|
1681
|
+
description: >-
|
|
1682
|
+
The compiled one-shot BPMN process definition (compile-to-native). Deterministic — the same
|
|
1683
|
+
input graph always produces byte-identical XML. Not deployed here (S4 owns deployment).
|
|
1684
|
+
resolved:
|
|
1685
|
+
$ref: "#/components/schemas/ResolvedDeliveryGraph"
|
|
1686
|
+
humanNodes:
|
|
1687
|
+
type: array
|
|
1688
|
+
items:
|
|
1689
|
+
$ref: "#/components/schemas/DeliveryHumanStop"
|
|
1690
|
+
description: The human stop-points, sorted by node id.
|
|
1691
|
+
sideEffects:
|
|
1692
|
+
type: array
|
|
1693
|
+
items:
|
|
1694
|
+
$ref: "#/components/schemas/DeliverySideEffect"
|
|
1695
|
+
description: The side-effecting actions the graph will perform, sorted by node id.
|
|
1696
|
+
CompileDeliveryGraphErrors:
|
|
1697
|
+
description: >-
|
|
1698
|
+
A rejected compile (ADR 0005 slice S1) — the graph failed shape or semantic validation. Every
|
|
1699
|
+
error is path-qualified so the co-designing agent can fix the exact offending input and
|
|
1700
|
+
re-compile. Nothing was compiled or deployed.
|
|
1701
|
+
type: object
|
|
1702
|
+
additionalProperties: false
|
|
1703
|
+
required:
|
|
1704
|
+
- ok
|
|
1705
|
+
- errors
|
|
1706
|
+
properties:
|
|
1707
|
+
ok:
|
|
1708
|
+
type: boolean
|
|
1709
|
+
enum: [false]
|
|
1710
|
+
description: Discriminant — `false` for a rejected compile.
|
|
1711
|
+
errors:
|
|
1712
|
+
type: array
|
|
1713
|
+
items:
|
|
1714
|
+
$ref: "#/components/schemas/DeliveryCompileError"
|
|
1715
|
+
description: The path-qualified validation/compile failures (at least one).
|
|
1501
1716
|
FeatureStart:
|
|
1502
1717
|
description: The start-feature request body — a SINGLE-issue feature run. Names the target issue
|
|
1503
1718
|
by EXACTLY ONE of `issue` (an `owner/repo#123` reference) or `url` (a bare issue URL), plus a
|
|
@@ -2247,6 +2462,39 @@ paths:
|
|
|
2247
2462
|
application/json:
|
|
2248
2463
|
schema:
|
|
2249
2464
|
$ref: "#/components/schemas/ErrorBody"
|
|
2465
|
+
/actions/compile-delivery-graph:
|
|
2466
|
+
post:
|
|
2467
|
+
operationId: compileDeliveryGraph
|
|
2468
|
+
summary: Validate + compile a delivery graph into a preview (PURE — never deploys). (ADR 0005 slice S1)
|
|
2469
|
+
description: >-
|
|
2470
|
+
The fast, safe inner loop of the delivery-graph workflow (ADR 0005 Decision 5/6). Given an
|
|
2471
|
+
agent-authored `DeliveryGraph` (the closed `agent`/`wait`/`human`/`connector` node vocabulary —
|
|
2472
|
+
the trust boundary), it runs the pure `validateDeliveryGraph` semantic check and then the
|
|
2473
|
+
deterministic, human-written compiler, returning a preview: the compiled one-shot BPMN
|
|
2474
|
+
(compile-to-native), a mermaid diagram, the resolved/normalised graph, and the extracted human
|
|
2475
|
+
stop-points and side effects. It is PURE and side-effect-free — it validates and compiles but
|
|
2476
|
+
NEVER deploys or dispatches, so an agent can call it repeatedly while iterating. Deployment is a
|
|
2477
|
+
separate door (`startDeliveryGraph`, a later slice) — there is deliberately no `dryRun` flag on
|
|
2478
|
+
the start door (Decision 5/7). A malformed graph is a 400 carrying path-qualified errors.
|
|
2479
|
+
requestBody:
|
|
2480
|
+
required: true
|
|
2481
|
+
content:
|
|
2482
|
+
application/json:
|
|
2483
|
+
schema:
|
|
2484
|
+
$ref: "#/components/schemas/DeliveryGraph"
|
|
2485
|
+
responses:
|
|
2486
|
+
"200":
|
|
2487
|
+
description: The graph validated and compiled — the pure preview (nothing deployed).
|
|
2488
|
+
content:
|
|
2489
|
+
application/json:
|
|
2490
|
+
schema:
|
|
2491
|
+
$ref: "#/components/schemas/CompileDeliveryGraphResult"
|
|
2492
|
+
"400":
|
|
2493
|
+
description: The graph failed shape or semantic validation — path-qualified errors, nothing compiled.
|
|
2494
|
+
content:
|
|
2495
|
+
application/json:
|
|
2496
|
+
schema:
|
|
2497
|
+
$ref: "#/components/schemas/CompileDeliveryGraphErrors"
|
|
2250
2498
|
/actions/start/feature:
|
|
2251
2499
|
post:
|
|
2252
2500
|
operationId: startFeature
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Tests for the POST /app/api/actions/compile-delivery-graph operation `compileDeliveryGraph`
|
|
2
|
+
// (ADR 0005 slice S1). The delegate is a thin, PURE mapping of the compiler's discriminated result
|
|
3
|
+
// onto the HTTP status: a well-formed graph → 200 { ok:true, … preview }, a malformed one → 400
|
|
4
|
+
// { ok:false, errors }. It touches no data layer and has zero side effects, so the same body compiled
|
|
5
|
+
// twice returns the identical response (callable repeatedly). These tests assert that status mapping.
|
|
6
|
+
import { test } from "node:test";
|
|
7
|
+
import { assert, assertEquals } from "#test-assert";
|
|
8
|
+
import type { AppApi } from "@nanobpm/urban";
|
|
9
|
+
import { noopLog } from "../test/log.ts";
|
|
10
|
+
import handler from "./compileDeliveryGraph.ts";
|
|
11
|
+
|
|
12
|
+
const app = { log: noopLog() } as unknown as AppApi;
|
|
13
|
+
|
|
14
|
+
async function call(body: unknown) {
|
|
15
|
+
return (await handler({ req: {} as any, params: {}, query: {}, body } as any, app)) as any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const GOOD = {
|
|
19
|
+
name: "runbook",
|
|
20
|
+
nodes: [
|
|
21
|
+
{ id: "a", kind: "agent", agent: { jobType: "senior:feature" } },
|
|
22
|
+
{ id: "b", kind: "human", human: { prompt: "do X" } },
|
|
23
|
+
],
|
|
24
|
+
edges: [{ from: "a", to: "b" }],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
test("compile-delivery-graph: a well-formed graph → 200 with the pure preview", async () => {
|
|
28
|
+
const res = await call(GOOD);
|
|
29
|
+
assertEquals(res.status, 200);
|
|
30
|
+
assertEquals(res.body.ok, true);
|
|
31
|
+
assert(typeof res.body.bpmn === "string" && res.body.bpmn.length > 0);
|
|
32
|
+
assert(typeof res.body.diagram === "string" && res.body.diagram.length > 0);
|
|
33
|
+
assertEquals(res.body.resolved.nodes.length, 2);
|
|
34
|
+
assertEquals(res.body.humanNodes.length, 1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("compile-delivery-graph: a malformed graph → 400 with path-qualified errors", async () => {
|
|
38
|
+
const res = await call({
|
|
39
|
+
nodes: [{ id: "a", kind: "agent", agent: { jobType: "j" } }],
|
|
40
|
+
edges: [{ from: "a", to: "ghost" }],
|
|
41
|
+
});
|
|
42
|
+
assertEquals(res.status, 400);
|
|
43
|
+
assertEquals(res.body.ok, false);
|
|
44
|
+
assert(Array.isArray(res.body.errors) && res.body.errors.length > 0);
|
|
45
|
+
assert(res.body.errors.every((e: { path: string; message: string }) => typeof e.path === "string"));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("compile-delivery-graph: is side-effect-free — repeated calls return identical responses", async () => {
|
|
49
|
+
const a = await call(GOOD);
|
|
50
|
+
const b = await call(GOOD);
|
|
51
|
+
assertEquals(a.status, b.status);
|
|
52
|
+
assertEquals(a.body.bpmn, b.body.bpmn);
|
|
53
|
+
assertEquals(JSON.stringify(a.body.resolved), JSON.stringify(b.body.resolved));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("compile-delivery-graph: a missing/empty body → 400, never a 500", async () => {
|
|
57
|
+
const res = await call(undefined);
|
|
58
|
+
assertEquals(res.status, 400);
|
|
59
|
+
assertEquals(res.body.ok, false);
|
|
60
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// POST /app/api/actions/compile-delivery-graph → operationId `compileDeliveryGraph` (ADR 0005,
|
|
2
|
+
// slice S1). The PURE, side-effect-free compile door: the fast, safe inner loop a co-designing agent
|
|
3
|
+
// hammers while authoring a `DeliveryGraph`. It VALIDATES (the pure `validateDeliveryGraph` semantic
|
|
4
|
+
// check, run inside the compiler) and COMPILES the graph into a preview — the compiled one-shot BPMN
|
|
5
|
+
// (compile-to-native), a mermaid diagram, the resolved/normalised graph, and the extracted human
|
|
6
|
+
// stop-points + side effects — but NEVER deploys, dispatches, or mutates anything (Decision 5/6:
|
|
7
|
+
// `compile` and `start` are SEPARATE doors, and there is deliberately no `dryRun` flag on the start
|
|
8
|
+
// door). Because it has zero side effects, an agent may call it repeatedly: JSON → compile → fix.
|
|
9
|
+
//
|
|
10
|
+
// A well-formed graph is `200 { ok:true, diagram, bpmn, resolved, humanNodes, sideEffects }`; a
|
|
11
|
+
// malformed one is `400 { ok:false, errors:[{ path, message }] }`, every error path-qualified so the
|
|
12
|
+
// author can fix the exact offending input. The compiler is the single source of both truths — this
|
|
13
|
+
// delegate just maps its discriminated result onto the HTTP status.
|
|
14
|
+
|
|
15
|
+
import { compileDeliveryGraph } from "../app/deliveryGraphCompiler.ts";
|
|
16
|
+
import { defineOperation } from "../nano-generated/operations.ts";
|
|
17
|
+
|
|
18
|
+
export default defineOperation("compileDeliveryGraph", async ({ body }, app) => {
|
|
19
|
+
// The runtime validates the body's SHAPE against `DeliveryGraph` before we run; the compiler adds
|
|
20
|
+
// the SEMANTIC checks (acyclicity, edge integrity, fact resolution) the schema cannot express. A
|
|
21
|
+
// directly-invoked delegate could still pass `undefined` — the compiler reads its input as
|
|
22
|
+
// `unknown` and maps that to a clean `ok:false`, never a 500.
|
|
23
|
+
const result = compileDeliveryGraph(body);
|
|
24
|
+
if (!result.ok) {
|
|
25
|
+
app.log.warn("compile-delivery-graph rejected", { errors: result.errors.length });
|
|
26
|
+
return { status: 400, body: result };
|
|
27
|
+
}
|
|
28
|
+
app.log.info("compile-delivery-graph compiled", {
|
|
29
|
+
nodes: result.resolved.nodes.length,
|
|
30
|
+
edges: result.resolved.edges.length,
|
|
31
|
+
humanNodes: result.humanNodes.length,
|
|
32
|
+
sideEffects: result.sideEffects.length,
|
|
33
|
+
});
|
|
34
|
+
return { status: 200, body: result };
|
|
35
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/nano-workforce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.112.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",
|