@nanobpm/nano-workforce 0.148.2 → 0.150.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 +12 -0
- package/README.md +36 -3
- package/SPEC.md +1 -1
- package/app/deliveryGraphCompiler.test.ts +51 -0
- package/app/deliveryGraphCompiler.ts +12 -4
- package/app/deliveryRunner.test.ts +30 -0
- package/app/deliveryRunner.ts +1 -1
- package/app/mcpExclusions.test.ts +83 -0
- package/app/readiness.test.ts +140 -0
- package/app/readiness.ts +171 -6
- package/docs/agent-guide.md +51 -4
- package/docs/mcp-runbook.md +133 -0
- package/openapi.yaml +20 -3
- package/package.json +2 -2
- package/skills/README.md +7 -3
- package/skills/nano-workforce/SKILL.md +122 -75
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nano-workforce
|
|
3
|
-
description: Drive and debug a running Nano Workforce instance — submit PRs for review convergence, submit issues/epics for plan→implement→converge, submit agent-authored delivery graphs (ADR 0005), answer escalations, and unstick stuck instances. Use when the user asks to operate, drive, submit work to, or debug their Nano Workforce.
|
|
3
|
+
description: Drive and debug a running Nano Workforce instance — submit PRs for review convergence, submit issues/epics for plan→implement→converge, submit agent-authored delivery graphs (ADR 0005), answer escalations, and unstick stuck instances. Prefer the instance's MCP server (add it → its tools appear); fall back to the live operator guide. Use when the user asks to operate, drive, submit work to, or debug their Nano Workforce.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Nano Workforce operator skill
|
|
@@ -12,27 +12,88 @@ implements → converges** them across a fleet of coding agents, and runs
|
|
|
12
12
|
DAGs — ADR 0005).
|
|
13
13
|
|
|
14
14
|
**This skill is a thin bootstrap by design.** It does not describe the endpoints.
|
|
15
|
-
Every running nwf instance
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
versions and instances.
|
|
15
|
+
Every running nwf instance is self-describing — over **MCP** where your client
|
|
16
|
+
supports it, and over its **live operator guide** everywhere else. Your job is to
|
|
17
|
+
reach that live surface and follow it, never to work from a cached copy that drifts
|
|
18
|
+
across versions and instances.
|
|
19
|
+
|
|
20
|
+
There are two paths. **Prefer MCP (§A).** If your client has no MCP support, use the
|
|
21
|
+
fetch-the-live-guide fallback (§B). Both talk to the same app; MCP is a projection of
|
|
22
|
+
the same OpenAPI contract, not a different system (ADR 0067).
|
|
23
|
+
|
|
24
|
+
## A. Preferred — drive over MCP
|
|
25
|
+
|
|
26
|
+
The Urban runtime serves a Streamable-HTTP MCP endpoint at **`/app/mcp`** for every
|
|
27
|
+
instance, with **zero app-side MCP code**: the app's operations are projected into
|
|
28
|
+
tools from its OpenAPI spec, alongside a framework-owned engine-debug tool family
|
|
29
|
+
(process instances, wait states, variables, incidents) and the app's projection
|
|
30
|
+
reads. The operator **guide** (the same guide as §B) is itself one of those projected
|
|
31
|
+
tools — `GET /app/api/agent` becomes the `getAgentInstructions` read tool — so the
|
|
32
|
+
workflow knowledge (orient first, preview before dispatch, escalations are for humans)
|
|
33
|
+
is discoverable over the same channel as the drive tools; the runtime additionally
|
|
34
|
+
serves its derived **system brief** as an MCP resource plus an orientation prompt.
|
|
35
|
+
|
|
36
|
+
**Register one MCP server entry per instance.** Naming the instance
|
|
37
|
+
(`"drive workforce-merlin"`) makes the wrong-instance mistake structurally
|
|
38
|
+
impossible — tool calls are namespaced per server entry. For the Copilot CLI, in
|
|
39
|
+
`~/.copilot/mcp-config.json` (user-wide) or `.mcp.json` (repo-scoped):
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"workforce-local": {
|
|
45
|
+
"type": "http",
|
|
46
|
+
"url": "http://localhost:3000/app/mcp",
|
|
47
|
+
"tools": ["*"]
|
|
48
|
+
},
|
|
49
|
+
"workforce-merlin": {
|
|
50
|
+
"type": "http",
|
|
51
|
+
"url": "http://merlin.local:3000/app/mcp",
|
|
52
|
+
"headers": { "x-hook-secret": "$NANO_PR_WEBHOOK_SECRET" },
|
|
53
|
+
"tools": ["*"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or from the terminal:
|
|
60
|
+
`copilot mcp add --transport http workforce-local http://localhost:3000/app/mcp`
|
|
61
|
+
(add `--header` for a guarded instance). Claude/Cursor use the same server entries.
|
|
62
|
+
|
|
63
|
+
Then: start a session, confirm the `workforce-*` tools appear, and ask the agent to
|
|
64
|
+
use a **named** instance (`"Using workforce-local, show what's in flight and any open
|
|
65
|
+
escalations"`). It should call the status operation tool, not curl.
|
|
19
66
|
|
|
20
|
-
|
|
67
|
+
**Guard posture.** When `NANO_PR_WEBHOOK_SECRET` is **unset**, both reads (status,
|
|
68
|
+
instances, incidents, projections, the operator guide) and mutations (submit work,
|
|
69
|
+
answer escalations, cancel/retry/resolve) work from loopback with no credential. When it
|
|
70
|
+
**is set**, that secret is required as an `x-hook-secret` header on **both reads and
|
|
71
|
+
mutations** — read endpoints like `getAgentInstructions`/`getVersion` also return `401`
|
|
72
|
+
without it — put it in the server entry's `headers`, never in chat. **Operator-only doors stay operator-only:** the delivery-graph
|
|
73
|
+
dispatch/dismiss lifecycle (the human clicking Dispatch *is* the approval, ADR 0005)
|
|
74
|
+
is `x-mcp`-excluded and is **not** a tool — dispatch stays a human action in the
|
|
75
|
+
cockpit.
|
|
21
76
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
(an
|
|
25
|
-
submitting work, answering escalations, merging PRs — so targeting the wrong
|
|
26
|
-
instance is a real mistake, not a harmless one. **Never silently default to a
|
|
27
|
-
base URL.**
|
|
77
|
+
The full server-entry recipes (multiple instances, Basic-Auth-fronted instances,
|
|
78
|
+
LAN exposure) live in the **agent-configuration runbook** — see the repo README
|
|
79
|
+
("Configure an agent over MCP") and [`docs/mcp-runbook.md`](../../docs/mcp-runbook.md).
|
|
28
80
|
|
|
29
|
-
|
|
81
|
+
## B. Fallback — no MCP client? Fetch the live guide
|
|
30
82
|
|
|
31
|
-
|
|
83
|
+
Agents without MCP are unchanged: resolve the instance, then fetch and follow its
|
|
84
|
+
live guide.
|
|
32
85
|
|
|
33
|
-
1
|
|
34
|
-
|
|
35
|
-
|
|
86
|
+
### B.1 Confirm which instance you are driving — always
|
|
87
|
+
|
|
88
|
+
A user typically runs **several** instances — a local dev copy, one on the LAN
|
|
89
|
+
(`http://merlin.local:3000/app/api`), and a public tunnel (ngrok) when off the LAN.
|
|
90
|
+
Every action is **side-effecting**, so targeting the wrong instance is a real
|
|
91
|
+
mistake. **Never silently default to a base URL.** Gather candidates, in order:
|
|
92
|
+
|
|
93
|
+
1. A named-instance registry the user maintains — first that exists:
|
|
94
|
+
`$NANO_WORKFORCE_INSTANCES` (JSON `name → base URL`) or
|
|
95
|
+
`~/.config/nano-workforce/instances.json` (same shape). This is also the source
|
|
96
|
+
for the per-instance MCP server names in §A. Example:
|
|
36
97
|
|
|
37
98
|
```json
|
|
38
99
|
{ "local": "http://localhost:3000/app/api",
|
|
@@ -40,33 +101,25 @@ Gather candidates from, in order:
|
|
|
40
101
|
"remote": "https://<subdomain>.ngrok.app/app/api" }
|
|
41
102
|
```
|
|
42
103
|
|
|
43
|
-
2. `$NANO_WORKFORCE_URL`, if set (a single default;
|
|
44
|
-
|
|
104
|
+
2. `$NANO_WORKFORCE_URL`, if set (a single default; append `/app/api` only if it is
|
|
105
|
+
a bare origin).
|
|
45
106
|
3. Any URL the user names in the conversation.
|
|
46
107
|
4. Local fallback: `http://localhost:3000/app/api` (port `PR_REVIEW_PORT`, default `3000`).
|
|
47
108
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
- If the user **named an instance** (by name from the registry, or by URL), use it.
|
|
51
|
-
- Otherwise, **probe the candidates for reachability** and ask the user which to
|
|
52
|
-
use, offering the candidates as choices and marking which are live. Reachability
|
|
53
|
-
disambiguates the common case — off the LAN, `merlin.local` won't resolve, so the
|
|
54
|
-
tunnel instance is the live one:
|
|
109
|
+
If the user named an instance, use it. Otherwise probe candidates for reachability
|
|
110
|
+
and ask which to use, marking which are live:
|
|
55
111
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
- Only skip the question when exactly **one** candidate exists and is reachable —
|
|
64
|
-
and even then, **name the instance you're about to drive** before acting.
|
|
112
|
+
```bash
|
|
113
|
+
curl -sS --max-time 3 \
|
|
114
|
+
${NANO_PR_WEBHOOK_SECRET:+-H "x-hook-secret: $NANO_PR_WEBHOOK_SECRET"} \
|
|
115
|
+
"$BASE/version" | jq '{appVersion, gitSha, uptimeSeconds}'
|
|
116
|
+
```
|
|
65
117
|
|
|
66
|
-
|
|
67
|
-
|
|
118
|
+
Only skip the question when exactly **one** candidate exists and is reachable — and
|
|
119
|
+
even then, name the instance you're about to drive before acting. If the user has
|
|
120
|
+
`$NANO_PR_WEBHOOK_SECRET` set, send it as `x-hook-secret` on every request.
|
|
68
121
|
|
|
69
|
-
|
|
122
|
+
### B.2 Fetch the live guide — your real playbook
|
|
70
123
|
|
|
71
124
|
```bash
|
|
72
125
|
curl -sS ${NANO_PR_WEBHOOK_SECRET:+-H "x-hook-secret: $NANO_PR_WEBHOOK_SECRET"} \
|
|
@@ -74,53 +127,47 @@ curl -sS ${NANO_PR_WEBHOOK_SECRET:+-H "x-hook-secret: $NANO_PR_WEBHOOK_SECRET"}
|
|
|
74
127
|
```
|
|
75
128
|
|
|
76
129
|
`GET /app/api/agent` (`getAgentInstructions`) returns
|
|
77
|
-
`{ format, appVersion, generatedAt, baseUrl, engineBase, instructions }
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
**Always re-fetch the guide at the start of a session.** It is the source of truth;
|
|
84
|
-
this skill only tells you how to find it.
|
|
85
|
-
|
|
86
|
-
## 3. Orient before acting
|
|
130
|
+
`{ format, appVersion, generatedAt, baseUrl, engineBase, instructions }` — the
|
|
131
|
+
authoritative, version-matched operator guide, every example keyed to this
|
|
132
|
+
instance. Read it in full and follow it for orientation, submitting work, answering
|
|
133
|
+
escalations, and debugging. **Re-fetch it at the start of every session.** (This is
|
|
134
|
+
the same prose the `getAgentInstructions` MCP tool serves in §A.)
|
|
87
135
|
|
|
88
|
-
|
|
136
|
+
### B.3 Orient before acting
|
|
89
137
|
|
|
90
138
|
```bash
|
|
91
139
|
curl -sS "$BASE/version" | jq # app/urban version, git sha, uptime
|
|
92
140
|
curl -sS "$BASE/status" | jq # every PR/instance in flight + open escalations
|
|
93
141
|
```
|
|
94
142
|
|
|
95
|
-
`/status` is the primary situational-awareness endpoint — check it before you
|
|
96
|
-
|
|
143
|
+
`/status` is the primary situational-awareness endpoint — check it before you submit
|
|
144
|
+
or unstick anything.
|
|
97
145
|
|
|
98
|
-
|
|
146
|
+
### B.4 What you can drive (all detailed in the live guide)
|
|
99
147
|
|
|
100
148
|
- **Submit a PR** for review convergence — `POST $BASE/actions/start/convergence-loop`.
|
|
101
|
-
- **Submit an issue/epic** for plan → implement → converge
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
from `/status`, then use the engine REST base (`engineBase` from the guide) to
|
|
111
|
-
inspect and unstick it.
|
|
149
|
+
- **Submit an issue/epic** for plan → implement → converge — `POST $BASE/actions/start/plan-fanout`.
|
|
150
|
+
- **Submit a delivery graph** (ADR 0005) — propose → preview → approve → **dispatch**.
|
|
151
|
+
Compile/preview is a pure, side-effect-free path; **dispatch is an operator action
|
|
152
|
+
in the cockpit**, not an agent door.
|
|
153
|
+
- **Answer an escalation** — `POST $BASE/actions/complete-user-task`, or the agent
|
|
154
|
+
hook `POST $BASE/hooks/agent-complete` (`agentCompleteEscalation`).
|
|
155
|
+
- **Debug** — relate an in-flight PR to its engine process instance via `processKey`
|
|
156
|
+
from `/status`, then inspect and unstick it against the engine REST base
|
|
157
|
+
(`engineBase` from the guide).
|
|
112
158
|
|
|
113
159
|
## Principles
|
|
114
160
|
|
|
115
|
-
- **Discover, don't declare.** Prefer the live
|
|
116
|
-
|
|
117
|
-
wins.
|
|
118
|
-
- **Confirm the target instance.** Never run a side-effecting call against an
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
side-effecting
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
-
|
|
126
|
-
|
|
161
|
+
- **Discover, don't declare.** Prefer the live surface (MCP tools including the
|
|
162
|
+
operator-guide tool, or the live guide and `/status`) over any assumption baked into
|
|
163
|
+
this file. If this skill and the live surface disagree, the live surface wins.
|
|
164
|
+
- **Confirm the target instance.** Never run a side-effecting call against an assumed
|
|
165
|
+
base URL. With MCP, name the server entry; with the fallback, know — and when
|
|
166
|
+
ambiguous, ask — which instance you're driving.
|
|
167
|
+
- **Preview before dispatch.** For delivery graphs and any bulk action, use the pure
|
|
168
|
+
preview/validate path first and show the user the plan before the side-effecting
|
|
169
|
+
start. Dispatch itself is the operator's call.
|
|
170
|
+
- **Idempotency.** Submissions carry dedupe keys; re-submitting the same work must not
|
|
171
|
+
double-dispatch. Honour the keys the guide documents.
|
|
172
|
+
- **Escalations are for humans.** When the workforce parks on a human node, surface it
|
|
173
|
+
with options; don't silently auto-answer design/product decisions.
|