@nanobpm/nano-workforce 0.35.0 → 0.35.2
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/README.md +12 -6
- package/SPEC.md +12 -4
- package/app/version.ts +1 -1
- package/deno.json +1 -1
- package/deno.lock +2 -2
- package/nano.app.json +0 -1
- package/openapi.json +1 -1
- package/operations/getVersion.test.ts +2 -2
- package/operations/getVersion.ts +1 -1
- package/operations/listActivePrs.test.ts +2 -2
- package/operations/listActivePrs.ts +1 -1
- package/operations/postMessage.ts +1 -1
- package/operations/startConvergenceLoop.ts +1 -1
- package/operations/startPlanFanout.ts +1 -1
- package/package.json +2 -2
- package/pages/epic.page.json +1 -1
- package/pages/home.page.json +7 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [0.35.2](https://github.com/nanobpm/nano-workforce/compare/v0.35.1...v0.35.2) (2026-08-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **pages:** route-driven page actions via @nanobpm/urban@0.33.0 ([#106](https://github.com/nanobpm/nano-workforce/issues/106)) ([0ee4ab2](https://github.com/nanobpm/nano-workforce/commit/0ee4ab29aa8cfca6a04af6eb141a96d7878a6b1a))
|
|
7
|
+
|
|
8
|
+
## [0.35.1](https://github.com/nanobpm/nano-workforce/compare/v0.35.0...v0.35.1) (2026-08-10)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **api:** move OpenAPI base off the reserved /app page-runtime namespace ([#103](https://github.com/nanobpm/nano-workforce/issues/103)) ([6dfb77f](https://github.com/nanobpm/nano-workforce/commit/6dfb77ff82b532965c4a9823d771d46d4ffa920e)), closes [#102](https://github.com/nanobpm/nano-workforce/issues/102)
|
|
14
|
+
|
|
1
15
|
# [0.35.0](https://github.com/nanobpm/nano-workforce/compare/v0.34.0...v0.35.0) (2026-08-10)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -133,15 +133,15 @@ app emits, then put it to work:
|
|
|
133
133
|
c8ctl nano hire \
|
|
134
134
|
--name fleet \
|
|
135
135
|
--rank senior \
|
|
136
|
-
--capabilities pr-review plan plan-review feature fix-ci \
|
|
136
|
+
--capabilities pr-review plan plan-review feature trial-merge fix-ci rebase retro \
|
|
137
137
|
--command 'copilot -p - --allow-all-tools' \
|
|
138
138
|
--model <your-model>
|
|
139
139
|
|
|
140
140
|
c8ctl nano work fleet # polls every senior:* agent task below until Ctrl-C
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
`--rank senior` × those
|
|
144
|
-
task types the
|
|
143
|
+
`--rank senior` × those eight capabilities subscribes the worker to exactly the agent
|
|
144
|
+
task types the four workflows emit (one `senior:<capability>` job type per
|
|
145
145
|
capability):
|
|
146
146
|
|
|
147
147
|
| Capability | Job type | Workflow | Task |
|
|
@@ -150,7 +150,10 @@ capability):
|
|
|
150
150
|
| `plan` | `senior:plan` | `plan-fanout` | Plan an issue into levelized tasks |
|
|
151
151
|
| `plan-review` | `senior:plan-review` | `plan-fanout` | Review the plan before fan-out |
|
|
152
152
|
| `feature` | `senior:feature` | `plan-fanout` | Implement one planned task → open a PR |
|
|
153
|
+
| `trial-merge` | `senior:trial-merge` | `plan-fanout` | Integration gate: trial-merge a wave, catch semantic conflicts CI can't see |
|
|
153
154
|
| `fix-ci` | `senior:fix-ci` | `merge-loop` | Green a `blocked` PR's failing checks |
|
|
155
|
+
| `rebase` | `senior:rebase` | `merge-loop` | Rebase a conflicting PR up to date with its base |
|
|
156
|
+
| `retro` | `senior:retro` | `retro` | Synthesize a finished epic's learnings and promote the recurring ones |
|
|
154
157
|
|
|
155
158
|
- `--command 'copilot -p - --allow-all-tools'` starts the Copilot CLI reading its
|
|
156
159
|
prompt from **stdin** (`-p -`). The harness pipes the whole job JSON (prompt +
|
|
@@ -169,9 +172,12 @@ whichever PRs/tasks are ready — that is the idle time you reclaim. Run more th
|
|
|
169
172
|
job per worker with `--max-parallel 2`. The app-hosted `pr.*` workers (record-plan,
|
|
170
173
|
select-wave, finalize, merge, …) run **inside** the app, not on an agent worker.
|
|
171
174
|
|
|
172
|
-
Already have a narrower worker? Extend it in place instead of re-hiring —
|
|
173
|
-
|
|
174
|
-
|
|
175
|
+
Already have a narrower worker? Extend it in place instead of re-hiring — this unions
|
|
176
|
+
the roles onto the profile, then restart its worker:
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
c8ctl nano assign <name> pr-review plan plan-review feature trial-merge fix-ci rebase retro
|
|
180
|
+
```
|
|
175
181
|
|
|
176
182
|
---
|
|
177
183
|
|
package/SPEC.md
CHANGED
|
@@ -261,13 +261,21 @@ child grids, a lazily-loaded transcript, and a conditional **answer** form shown
|
|
|
261
261
|
when the PR has an open escalation (`open_escalation_id`, denormalised onto the
|
|
262
262
|
row by migration `003`).
|
|
263
263
|
|
|
264
|
-
|
|
265
|
-
|
|
264
|
+
The app-specific business-logic endpoints are **OpenAPI operations** mounted
|
|
265
|
+
under `api.base` (`/app/api`), each implemented by a delegate module in
|
|
266
|
+
`operations/`, plus a `/hooks/*` webhook. The runtime serves them all; `main.ts`
|
|
267
|
+
only starts the runtime and the review-ready poller. The full, authoritative
|
|
268
|
+
contract is `openapi.json` (Swagger UI at `/app/api-docs`); the OpenAPI rows
|
|
269
|
+
below are the complete set of operations, `/hooks/submit` is one of the `/hooks/*`
|
|
270
|
+
webhooks:
|
|
266
271
|
|
|
267
272
|
| method | route | purpose |
|
|
268
273
|
|---|---|---|
|
|
269
|
-
| `
|
|
270
|
-
| `
|
|
274
|
+
| `GET` | `/app/api/status` | list tracked PRs + count |
|
|
275
|
+
| `GET` | `/app/api/version` | app + engine version |
|
|
276
|
+
| `POST` | `/app/api/actions/start/convergence-loop` | parse the PR ref → create the aggregate + start the process |
|
|
277
|
+
| `POST` | `/app/api/actions/start/plan-fanout` | start a plan fan-out run |
|
|
278
|
+
| `POST` | `/app/api/actions/message` (`escalation-answered`) | answer an open escalation → publish `escalation-answered` |
|
|
271
279
|
| `POST` | `/hooks/submit` | webhook submit (shared-secret auth) → start the process |
|
|
272
280
|
|
|
273
281
|
Everything else (`GET /`, `GET /app/pages/*`, `GET /app/data/*`, the renderer) is
|
package/app/version.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// debugging a stuck instance can confirm the process is on the code they think it is.
|
|
10
10
|
//
|
|
11
11
|
// Every probe is best-effort: a missing file or unavailable `.git` yields `null` for that field
|
|
12
|
-
// rather than throwing, so `/app/version` never fails just because one source is absent.
|
|
12
|
+
// rather than throwing, so `/app/api/version` never fails just because one source is absent.
|
|
13
13
|
import { readFileSync } from "node:fs";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { dirname, join, resolve, isAbsolute } from "node:path";
|
package/deno.json
CHANGED
package/deno.lock
CHANGED
|
@@ -1756,11 +1756,11 @@
|
|
|
1756
1756
|
},
|
|
1757
1757
|
"workspace": {
|
|
1758
1758
|
"dependencies": [
|
|
1759
|
-
"npm:@nanobpm/urban
|
|
1759
|
+
"npm:@nanobpm/urban@0.33"
|
|
1760
1760
|
],
|
|
1761
1761
|
"packageJson": {
|
|
1762
1762
|
"dependencies": [
|
|
1763
|
-
"npm:@nanobpm/urban
|
|
1763
|
+
"npm:@nanobpm/urban@0.33",
|
|
1764
1764
|
"npm:@semantic-release/changelog@^6.0.3",
|
|
1765
1765
|
"npm:@semantic-release/git@^10.0.1",
|
|
1766
1766
|
"npm:@semantic-release/npm@^13.1.5",
|
package/nano.app.json
CHANGED
package/openapi.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Nano Workforce control API",
|
|
5
5
|
"version": "1.0.0",
|
|
6
|
-
"description": "The externally-facing control surface an operator, an automation harness, or an LLM uses to observe and steer PR-convergence and planning runs. Contract-first (ADR 0058): the toolkit derives typed request/response contracts + runtime validators from this document and each `operationId` is implemented by a delegate module in `operations/`. Mounted under base `/app
|
|
6
|
+
"description": "The externally-facing control surface an operator, an automation harness, or an LLM uses to observe and steer PR-convergence and planning runs. Contract-first (ADR 0058): the toolkit derives typed request/response contracts + runtime validators from this document and each `operationId` is implemented by a delegate module in `operations/`. Mounted under base `/app/api`, kept off the framework-reserved `/app` page-runtime namespace (which owns `/app/runtime.js`, `/app/pages/*`, `/app/data/*`). The `/hooks/*` webhook endpoints stay on `actions[]` because they live outside the `/app` namespace."
|
|
7
7
|
},
|
|
8
8
|
"components": {
|
|
9
9
|
"securitySchemes": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Tests for GET /app/version → operation `getVersion` (ADR 0058 OpenAPI surface).
|
|
1
|
+
// Tests for GET /app/api/version → operation `getVersion` (ADR 0058 OpenAPI surface).
|
|
2
2
|
// Ported from the previous actions/version.test.ts. Method handling now belongs to the router
|
|
3
3
|
// (only GET is routed here), so there is no 405 case to test at the delegate level.
|
|
4
4
|
import { assert, assertEquals } from "jsr:@std/assert@1";
|
|
@@ -12,7 +12,7 @@ function input(headers: Record<string, string> = {}) {
|
|
|
12
12
|
return {
|
|
13
13
|
req: {
|
|
14
14
|
method: "GET",
|
|
15
|
-
path: "/app/version",
|
|
15
|
+
path: "/app/api/version",
|
|
16
16
|
query: new URLSearchParams(),
|
|
17
17
|
headers: new Headers(headers),
|
|
18
18
|
text: async () => "",
|
package/operations/getVersion.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GET /app/version → operationId `getVersion` (ADR 0058 OpenAPI surface, mounted under base /app).
|
|
1
|
+
// GET /app/api/version → operationId `getVersion` (ADR 0058 OpenAPI surface, mounted under base /app/api).
|
|
2
2
|
// The running app's identity (app + resolved @nanobpm/urban versions, git commit/branch, JS runtime,
|
|
3
3
|
// pid, uptime). Because the app runs its `.ts` sources directly from a checkout with no build step,
|
|
4
4
|
// restarts alone don't tell you whether a fix is live; this endpoint does.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Tests for GET /app/status → operation `listActivePrs` (ADR 0058 OpenAPI surface).
|
|
1
|
+
// Tests for GET /app/api/status → operation `listActivePrs` (ADR 0058 OpenAPI surface).
|
|
2
2
|
// Covers the happy path (count/prs projection) and the optional shared-secret guard. A minimal
|
|
3
3
|
// in-memory DataLayer backs `activePrs` (it reads the `pull_requests` table via `.all()`).
|
|
4
4
|
import { assert, assertEquals } from "jsr:@std/assert@1";
|
|
@@ -21,7 +21,7 @@ function input(headers: Record<string, string> = {}) {
|
|
|
21
21
|
return {
|
|
22
22
|
req: {
|
|
23
23
|
method: "GET",
|
|
24
|
-
path: "/app/status",
|
|
24
|
+
path: "/app/api/status",
|
|
25
25
|
query: new URLSearchParams(),
|
|
26
26
|
headers: new Headers(headers),
|
|
27
27
|
text: async () => "",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GET /app/status → operationId `listActivePrs` (ADR 0058 OpenAPI surface, mounted under base /app).
|
|
1
|
+
// GET /app/api/status → operationId `listActivePrs` (ADR 0058 OpenAPI surface, mounted under base /app/api).
|
|
2
2
|
// List the PRs currently in flight (every tracked PR not converged/abandoned) so an operator or an
|
|
3
3
|
// external automation harness can see active work — and grab a `processKey` to cancel — without
|
|
4
4
|
// opening the DB or the UI. Read-only projection over the datasource.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// POST /app/actions/message → operationId `postMessage` (ADR 0058, base /app).
|
|
1
|
+
// POST /app/api/actions/message → operationId `postMessage` (ADR 0058, base /app/api).
|
|
2
2
|
// Replaces the hand-rolled action that overrode the generic publishMessage action. For the
|
|
3
3
|
// `escalation-answered` message we run the review answer flow, and for `feature-escalation-answered`
|
|
4
4
|
// (issue #25) the implementation-phase (per-task) answer flow: record the answer, resume the parked
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// POST /app/actions/start/convergence-loop → operationId `startConvergenceLoop` (ADR 0058, base /app).
|
|
1
|
+
// POST /app/api/actions/start/convergence-loop → operationId `startConvergenceLoop` (ADR 0058, base /app/api).
|
|
2
2
|
// Replaces the hand-rolled action that overrode the generic "start process" palette action: parse the
|
|
3
3
|
// PR reference and register/refresh the PR aggregate (idempotent on prKey) before starting the loop.
|
|
4
4
|
//
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// POST /app/actions/start/plan-fanout → operationId `startPlanFanout` (ADR 0058, base /app).
|
|
1
|
+
// POST /app/api/actions/start/plan-fanout → operationId `startPlanFanout` (ADR 0058, base /app/api).
|
|
2
2
|
// Replaces the hand-rolled action that overrode the generic "start process" palette action: parse the
|
|
3
3
|
// issue reference and register/refresh the plan aggregate (idempotent on planKey) before starting the
|
|
4
4
|
// planning fan-out. An unparseable reference is a 400; an already-running plan short-circuits.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/nano-workforce",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.2",
|
|
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",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"test": "deno test -A"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@nanobpm/urban": "^0.
|
|
43
|
+
"@nanobpm/urban": "^0.33.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@semantic-release/changelog": "^6.0.3",
|
package/pages/epic.page.json
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"props": {
|
|
34
34
|
"title": "Hand an issue to the fleet",
|
|
35
35
|
"submitLabel": "Plan & implement",
|
|
36
|
-
"action": { "
|
|
36
|
+
"action": { "path": "/app/api/actions/start/plan-fanout" },
|
|
37
37
|
"fields": [
|
|
38
38
|
{ "key": "issue", "label": "owner/repo#123 or a GitHub issue URL", "type": "text" }
|
|
39
39
|
]
|
package/pages/home.page.json
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"props": {
|
|
34
34
|
"title": "Submit a pull request",
|
|
35
35
|
"submitLabel": "Start review",
|
|
36
|
-
"action": { "
|
|
36
|
+
"action": { "path": "/app/api/actions/start/convergence-loop" },
|
|
37
37
|
"fields": [
|
|
38
38
|
{ "key": "pr", "label": "owner/repo#123 or a GitHub PR URL", "type": "text" },
|
|
39
39
|
{ "key": "maxRounds", "label": "Max review rounds (blank = fleet default)", "type": "number" }
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"label": "Cancel",
|
|
83
83
|
"confirm": "Cancel this PR's review run?",
|
|
84
84
|
"showWhenField": "process_key",
|
|
85
|
-
"action": { "
|
|
85
|
+
"action": { "path": "/app/actions/cancel", "body": { "processInstanceKey": "{{row.process_key}}" } }
|
|
86
86
|
}
|
|
87
87
|
],
|
|
88
88
|
"detail": {
|
|
@@ -149,9 +149,8 @@
|
|
|
149
149
|
"inputLabel": "Your answer",
|
|
150
150
|
"submitLabel": "Send answer",
|
|
151
151
|
"action": {
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
"correlationKeyField": "pr_key"
|
|
152
|
+
"path": "/app/api/actions/message",
|
|
153
|
+
"body": { "name": "escalation-answered", "correlationKey": "{{row.pr_key}}", "variables": "{{form}}" }
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
156
|
}
|
|
@@ -192,7 +191,7 @@
|
|
|
192
191
|
"label": "Cancel",
|
|
193
192
|
"confirm": "Cancel this plan's fan-out run?",
|
|
194
193
|
"showWhenField": "process_key",
|
|
195
|
-
"action": { "
|
|
194
|
+
"action": { "path": "/app/actions/cancel", "body": { "processInstanceKey": "{{row.process_key}}" } }
|
|
196
195
|
}
|
|
197
196
|
],
|
|
198
197
|
"detail": {
|
|
@@ -301,9 +300,8 @@
|
|
|
301
300
|
"inputLabel": "Your answer",
|
|
302
301
|
"submitLabel": "Send answer",
|
|
303
302
|
"action": {
|
|
304
|
-
"
|
|
305
|
-
"
|
|
306
|
-
"correlationKeyField": "open_task_corr_key"
|
|
303
|
+
"path": "/app/api/actions/message",
|
|
304
|
+
"body": { "name": "feature-escalation-answered", "correlationKey": "{{row.open_task_corr_key}}", "variables": "{{form}}" }
|
|
307
305
|
}
|
|
308
306
|
}
|
|
309
307
|
}
|