@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/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.111.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",