@nanobpm/nano-workforce 0.170.0 → 0.170.1
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/agentGuide.ts +1 -0
- package/docs/agent-guide.md +198 -42
- package/docs/mcp-runbook.md +21 -4
- package/openapi.yaml +1 -1
- package/package.json +1 -1
- package/pages/overview.page.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [0.170.1](https://github.com/nanobpm/nano-workforce/compare/v0.170.0...v0.170.1) (2026-08-31)
|
|
2
|
+
|
|
3
|
+
### Documentation
|
|
4
|
+
|
|
5
|
+
* **agent-guide:** make the operator guide tool-aware + tool↔curl crosswalk (N1) ([#669](https://github.com/nanobpm/nano-workforce/issues/669)) ([44c5937](https://github.com/nanobpm/nano-workforce/commit/44c5937fb3a1cdaa5a106d67ed16225c35d614f6)), closes [#664](https://github.com/nanobpm/nano-workforce/issues/664) [#666](https://github.com/nanobpm/nano-workforce/issues/666) [#667](https://github.com/nanobpm/nano-workforce/issues/667) [#665](https://github.com/nanobpm/nano-workforce/issues/665) [666/#667](https://github.com/666/nano-workforce/issues/667) [#666](https://github.com/nanobpm/nano-workforce/issues/666) [#667](https://github.com/nanobpm/nano-workforce/issues/667) [#665](https://github.com/nanobpm/nano-workforce/issues/665)
|
|
6
|
+
|
|
1
7
|
## [0.170.0](https://github.com/nanobpm/nano-workforce/compare/v0.169.0...v0.170.0) (2026-08-31)
|
|
2
8
|
|
|
3
9
|
### Features
|
package/app/agentGuide.ts
CHANGED
|
@@ -106,6 +106,7 @@ export const GUIDE_SECTIONS: readonly GuideSectionMeta[] = [
|
|
|
106
106
|
{ id: "unstick", summary: "Unstick a wedged process — publish a correlating message, cancel, or otherwise recover a stalled instance." },
|
|
107
107
|
{ id: "raise-issue", summary: "Raise an issue or open a PR against the nano-workforce repository itself." },
|
|
108
108
|
{ id: "delivery-graphs", summary: "Author, preview, compile/stage and run an agent-authored delivery graph (ADR 0005): node/wait/connector vocabulary." },
|
|
109
|
+
{ id: "tool-crosswalk", summary: "Tool↔curl crosswalk: map every guide action to its projected MCP tool (status, version, urban_debug_* engine reads, escalation answer, cancel) with the curl no-MCP fallback." },
|
|
109
110
|
] as const;
|
|
110
111
|
|
|
111
112
|
/** A parsed section: its stable id + summary (from the registry), the derived heading `title` (the
|
package/docs/agent-guide.md
CHANGED
|
@@ -25,18 +25,28 @@ in full so you can tell which are under the control-API base and which are not.
|
|
|
25
25
|
|
|
26
26
|
## 0. Orient yourself first
|
|
27
27
|
|
|
28
|
-
Before acting, confirm what is running and what is in flight
|
|
28
|
+
Before acting, confirm what is running and what is in flight. **Prefer the projected
|
|
29
|
+
tools** (`getVersion`, `listActivePrs`); curl is the no-MCP fallback only. Every step in
|
|
30
|
+
this guide that has a tool is mapped in the **tool↔curl crosswalk (§10)** — reach for the
|
|
31
|
+
tool first and drop to curl only when you are not driving this instance over MCP.
|
|
29
32
|
|
|
30
|
-
```
|
|
33
|
+
```text
|
|
31
34
|
# Which code is live (app version, urban version, git sha/branch, uptime):
|
|
32
|
-
|
|
35
|
+
tool: getVersion # GET /version
|
|
33
36
|
|
|
34
37
|
# Every PR currently in flight (not converged/abandoned), with its engine
|
|
35
38
|
# process key, status, round, and any open escalation:
|
|
39
|
+
tool: listActivePrs # GET /status
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# No-MCP fallback (agent without the projected tools):
|
|
44
|
+
curl -sS __BASE__/version | jq
|
|
36
45
|
curl -sS __BASE__/status | jq
|
|
37
46
|
```
|
|
38
47
|
|
|
39
|
-
`/status` is your primary situational-awareness
|
|
48
|
+
`listActivePrs` (the `/status` endpoint) is your primary situational-awareness surface.
|
|
49
|
+
Each entry carries:
|
|
40
50
|
`prKey` (`owner/repo#123`), `status`, `round`, `processKey` (the **engine process
|
|
41
51
|
instance key** — the bridge to the engine REST API, §5), `openEscalation`,
|
|
42
52
|
`activeWorker`/`leaseUntil` (is an agent actually working the round, or is the job
|
|
@@ -152,24 +162,36 @@ A loop escalates only when an agent returns `needs_input`/`blocked`, or a safety
|
|
|
152
162
|
fires (round cap, a review that never arrives, a merge conflict, an unfixable CI
|
|
153
163
|
failure). The parked process waits for a human answer.
|
|
154
164
|
|
|
155
|
-
Find the open escalations, then answer them
|
|
165
|
+
Find the open escalations, then answer them. **Prefer the `listEscalations` tool**
|
|
166
|
+
(the projected read tool that lists every open escalation with the `userTaskKey` and form —
|
|
167
|
+
tracked by sibling #666; on a deployment that predates it, use the `/status` / task-inbox
|
|
168
|
+
curl fallback below —
|
|
169
|
+
you need to answer it); curl is the no-MCP fallback.
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
# Every open escalation waiting for a human, across all four kinds — each with its
|
|
173
|
+
# userTaskKey, prKey, kind (elementId), question/findings/task, and formVariables:
|
|
174
|
+
tool: listEscalations
|
|
175
|
+
```
|
|
156
176
|
|
|
157
177
|
```bash
|
|
158
|
-
#
|
|
178
|
+
# No-MCP fallback: read the open escalations off /status …
|
|
159
179
|
curl -sS __BASE__/status | jq '.prs[] | select(.openEscalation != null)
|
|
160
180
|
| { prKey, status, round, openEscalation }'
|
|
161
181
|
```
|
|
162
182
|
|
|
163
183
|
The four decision-required escalation kinds — **PR review-loop**, **implementation
|
|
164
184
|
(feature) task**, **plan-review**, and **trial-merge** — are now native BPMN
|
|
165
|
-
`userTask`s bearing a linked `.form`, all answered the same way through the
|
|
166
|
-
|
|
185
|
+
`userTask`s bearing a linked `.form`, all answered the same way through the
|
|
186
|
+
`completeUserTask` tool (a human uses the same **task inbox** surface underneath).
|
|
187
|
+
There is no bespoke per-kind webhook or answer page any more.
|
|
167
188
|
|
|
168
|
-
**List the open escalation tasks.**
|
|
169
|
-
`question` / `findings` / `task`)
|
|
189
|
+
**List the open escalation tasks.** `listEscalations` returns each open task with its
|
|
190
|
+
context (`prKey` / `question` / `findings` / `task`) and its kind in `elementId` /
|
|
191
|
+
`kind`, so you do **not** need to reach for the raw Tasks inbox. No-MCP fallback only:
|
|
170
192
|
|
|
171
193
|
```bash
|
|
172
|
-
#
|
|
194
|
+
# No-MCP fallback — the un-projected engine task inbox (prefer `listEscalations`):
|
|
173
195
|
curl -sS __BASE__/../../tasks/api/tasks | jq '.[] | { userTaskKey, elementId, variables }'
|
|
174
196
|
|
|
175
197
|
# Filter to one kind (e.g. plan-review decisions) by elementId:
|
|
@@ -187,34 +209,52 @@ answer (assignee/candidate-group and age surface on each task once assignment la
|
|
|
187
209
|
> node then runs against the unmet dependency. See §9.2 before clearing one.
|
|
188
210
|
|
|
189
211
|
**Answer a task** by completing it with the typed variables its form expects — the
|
|
190
|
-
completion resumes the parked process
|
|
212
|
+
completion resumes the parked process. **Prefer the `completeUserTask` tool** (or
|
|
213
|
+
`agentCompleteEscalation` when you answer as an agent assignee, ADR 0046); the
|
|
214
|
+
`curl … /actions/complete-user-task` app-owned door is the no-MCP fallback.
|
|
191
215
|
|
|
192
|
-
```
|
|
216
|
+
```text
|
|
193
217
|
# PR review-loop (elementId `wait-answer`, pr-escalation form):
|
|
194
|
-
|
|
195
|
-
|
|
218
|
+
tool: completeUserTask { "userTaskKey": "<key>",
|
|
219
|
+
"variables": { "answer": "Cap retries at 5 and proceed." } }
|
|
196
220
|
|
|
197
221
|
# Implementation (feature) task (elementId `feature-escalation`):
|
|
198
222
|
# { "resolution": "answer", "answer": "…" } to resume, or { "resolution": "abandon" }
|
|
199
|
-
|
|
200
|
-
|
|
223
|
+
tool: completeUserTask { "userTaskKey": "<key>",
|
|
224
|
+
"variables": { "resolution": "answer", "answer": "Use v2." } }
|
|
201
225
|
|
|
202
226
|
# Plan-review (elementId `plan-review-decision`):
|
|
203
227
|
# { "directive": "revise", "notes": "…" } (fresh review budget) or { "directive": "proceed" }
|
|
204
|
-
|
|
205
|
-
|
|
228
|
+
tool: completeUserTask { "userTaskKey": "<key>",
|
|
229
|
+
"variables": { "directive": "revise", "notes": "Make issue-7 the seam." } }
|
|
206
230
|
|
|
207
231
|
# Trial-merge (elementId `trial-merge-decision`):
|
|
208
232
|
# { "action": "proceed" | "rebase" | "abandon", "notes"?: "…" }
|
|
209
|
-
|
|
210
|
-
|
|
233
|
+
tool: completeUserTask { "userTaskKey": "<key>",
|
|
234
|
+
"variables": { "action": "rebase", "notes": "Re-run after the fix." } }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# No-MCP fallback — the app-owned complete-user-task door (prefer the tool).
|
|
239
|
+
# `variables` must carry the shape THIS task's escalation kind expects (see the per-kind
|
|
240
|
+
# tool forms above): `{ answer }` here is the PR review-loop form — a feature task wants
|
|
241
|
+
# `{ resolution, answer }`, plan-review `{ directive, notes }`, trial-merge `{ action, notes? }`.
|
|
242
|
+
curl -sS -X POST __BASE__/actions/complete-user-task -H 'content-type: application/json' \
|
|
243
|
+
-d '{ "userTaskKey": "<key>", "variables": { "answer": "Cap retries at 5 and proceed." } }'
|
|
211
244
|
```
|
|
212
245
|
|
|
213
246
|
**Answer a PR escalation** (both the review-loop `wait-answer` and the merge-loop
|
|
214
247
|
`wait-merge-answer` — both are now native user tasks answered the same way, #256).
|
|
215
|
-
Use the PR key's parked user task and submit the `pr-escalation` form's `{ answer }
|
|
248
|
+
Use the PR key's parked user task and submit the `pr-escalation` form's `{ answer }`
|
|
249
|
+
through the `completeUserTask` tool; curl is the no-MCP fallback:
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
tool: completeUserTask { "userTaskKey": "<key>",
|
|
253
|
+
"variables": { "answer": "Yes — cap the retries at 5 and proceed." } }
|
|
254
|
+
```
|
|
216
255
|
|
|
217
256
|
```bash
|
|
257
|
+
# No-MCP fallback:
|
|
218
258
|
curl -sS -X POST __BASE__/actions/complete-user-task \
|
|
219
259
|
-H 'content-type: application/json' \
|
|
220
260
|
-d '{
|
|
@@ -223,9 +263,11 @@ curl -sS -X POST __BASE__/actions/complete-user-task \
|
|
|
223
263
|
}'
|
|
224
264
|
```
|
|
225
265
|
|
|
226
|
-
The `userTaskKey` comes from `GET /status
|
|
227
|
-
canonical answer door for every escalation kind
|
|
228
|
-
|
|
266
|
+
The `userTaskKey` comes from `listEscalations` (or `GET /status`/the Tasks inbox). This
|
|
267
|
+
is the ONE canonical answer door (`completeUserTask`) for every escalation kind — with
|
|
268
|
+
`agentCompleteEscalation` the agent-assignee variant that records identity for
|
|
269
|
+
attribution (ADR 0046); the merge loop no longer uses a durable `escalation-answered`
|
|
270
|
+
message catch.
|
|
229
271
|
|
|
230
272
|
If `NANO_PR_WEBHOOK_SECRET` is set on the deployment, add `-H "x-hook-secret: <secret>"`.
|
|
231
273
|
|
|
@@ -266,10 +308,35 @@ The app stores each PR/plan's engine **process instance key** in its `process_ke
|
|
|
266
308
|
column and surfaces it as `processKey` in `/status`. That key is the join between the
|
|
267
309
|
app's business view and the engine's execution view.
|
|
268
310
|
|
|
269
|
-
**Find the instance for a PR:** take `processKey` from `/status
|
|
270
|
-
engine
|
|
311
|
+
**Find the instance for a PR:** take `processKey` from `listActivePrs` (`/status`), then
|
|
312
|
+
query engine truth. **Prefer the framework `urban_debug_*` engine-debug tools** (owned by
|
|
313
|
+
the nano-ide urban runtime — see the crosswalk in §10); the `curl __ENGINE__/…` engine
|
|
314
|
+
REST calls are the no-MCP fallback:
|
|
315
|
+
|
|
316
|
+
```text
|
|
317
|
+
PK = <processKey from listActivePrs>
|
|
318
|
+
|
|
319
|
+
# The instance itself (state, the BPMN process it is running, start time):
|
|
320
|
+
tool: urban_debug_search_process_instances { "filter": { "processInstanceKey": PK } }
|
|
321
|
+
|
|
322
|
+
# Where is it parked? — active jobs on the instance (a CREATED senior:pr-review job
|
|
323
|
+
# with a `worker` set means an agent has leased the round; none means it is queued).
|
|
324
|
+
# `urban_debug_search_jobs`/`_search_variables` are projected only where the framework
|
|
325
|
+
# offers them (sibling nano-ide epic); else drop to the curl door (see §10 crosswalk):
|
|
326
|
+
tool: urban_debug_search_jobs { "filter": { "processInstanceKey": PK, "state": "CREATED" } } # where projected
|
|
327
|
+
|
|
328
|
+
# Its variables (the aggregate/job payload the engine holds):
|
|
329
|
+
tool: urban_debug_search_variables { "filter": { "processInstanceKey": PK } } # where projected
|
|
330
|
+
|
|
331
|
+
# Is it dead-in-the-water on a technical fault? — active incidents:
|
|
332
|
+
tool: urban_debug_search_incidents { "filter": { "processInstanceKey": PK, "state": "ACTIVE" } }
|
|
333
|
+
|
|
334
|
+
# Which BPMN element/wait-state is it sitting on?
|
|
335
|
+
tool: urban_debug_search_element_instance_wait_states { "filter": { "processInstanceKey": PK } }
|
|
336
|
+
```
|
|
271
337
|
|
|
272
338
|
```bash
|
|
339
|
+
# No-MCP fallback — the raw engine Camunda-8 v2 REST API:
|
|
273
340
|
PK=<processKey-from-status>
|
|
274
341
|
|
|
275
342
|
# The instance itself (state, the BPMN process it is running, start time):
|
|
@@ -277,8 +344,7 @@ curl -sS -X POST __ENGINE__/process-instances/search \
|
|
|
277
344
|
-H 'content-type: application/json' \
|
|
278
345
|
-d "{ \"filter\": { \"processInstanceKey\": \"$PK\" } }" | jq
|
|
279
346
|
|
|
280
|
-
# Where is it parked? — active jobs on the instance
|
|
281
|
-
# with a `worker` set means an agent has leased the round; none means it is queued):
|
|
347
|
+
# Where is it parked? — active jobs on the instance:
|
|
282
348
|
curl -sS -X POST __ENGINE__/jobs/search \
|
|
283
349
|
-H 'content-type: application/json' \
|
|
284
350
|
-d "{ \"filter\": { \"processInstanceKey\": \"$PK\", \"state\": \"CREATED\" } }" | jq
|
|
@@ -294,15 +360,23 @@ curl -sS -X POST __ENGINE__/element-instances/search \
|
|
|
294
360
|
-d "{ \"filter\": { \"processInstanceKey\": \"$PK\" } }" | jq
|
|
295
361
|
```
|
|
296
362
|
|
|
363
|
+
> The `urban_debug_search_jobs`, `urban_debug_search_variables` and
|
|
364
|
+
> `urban_debug_get_process_definition_xml` reads are owned by the sibling nano-ide urban
|
|
365
|
+
> epic; where a deployment's framework has not yet projected them, use the curl fallback
|
|
366
|
+
> for that row. The three always-present engine reads are
|
|
367
|
+
> `urban_debug_search_process_instances`, `urban_debug_search_element_instance_wait_states`
|
|
368
|
+
> and `urban_debug_search_incidents`.
|
|
369
|
+
|
|
297
370
|
The app already mirrors an ACTIVE incident onto the PR row (`incident`/incident
|
|
298
371
|
message), so a PR that shows an incident in the UI is parked on an engine fault —
|
|
299
|
-
inspect it with `incidents/search` above. If the
|
|
300
|
-
deployment's engine base is `__ENGINE__` (set via
|
|
301
|
-
`CAMUNDA_REST_ADDRESS`).
|
|
372
|
+
inspect it with `urban_debug_search_incidents` (or `incidents/search`) above. If the
|
|
373
|
+
engine is not at the default, the deployment's engine base is `__ENGINE__` (set via
|
|
374
|
+
`NANOBPMN_BASE_URL` or `CAMUNDA_REST_ADDRESS`).
|
|
302
375
|
|
|
303
376
|
**Relate an instance back to a PR:** if you have a `processKey` but not the PR, match
|
|
304
|
-
it against
|
|
305
|
-
longer in `/status`; its instance has already completed or been
|
|
377
|
+
it against `listActivePrs`/`/status` (`.prs[] | select(.processKey == "<PK>")`). A
|
|
378
|
+
terminal PR is no longer in `/status`; its instance has already completed or been
|
|
379
|
+
cancelled.
|
|
306
380
|
|
|
307
381
|
---
|
|
308
382
|
|
|
@@ -311,9 +385,15 @@ longer in `/status`; its instance has already completed or been cancelled.
|
|
|
311
385
|
The behaviour is defined by durable BPMN processes and model-authored agent prompts —
|
|
312
386
|
both live in the source repo, not in the job payload.
|
|
313
387
|
|
|
314
|
-
- **Processes:**
|
|
315
|
-
|
|
316
|
-
|
|
388
|
+
- **Processes:** to inspect the model an instance is **actually running**, prefer the
|
|
389
|
+
framework tool **`urban_debug_get_process_definition_xml`** (owned by the sibling
|
|
390
|
+
nano-ide urban epic — see §10), which returns the deployed BPMN XML for a process
|
|
391
|
+
definition; that is engine truth and cannot drift from what is live. The repo copies
|
|
392
|
+
`resources/processes/*.bpmn` — `convergence-loop.bpmn` (review), `merge-loop.bpmn`
|
|
393
|
+
(merge/CI-fix/rebase), `plan-fanout.bpmn` (planning), `retro.bpmn` — are the
|
|
394
|
+
**source of truth in git** for routing, read them from the checkout when you want the
|
|
395
|
+
authored form (or have no MCP). They can lag a running deployment, so confirm against
|
|
396
|
+
the deployed XML when *why did this instance route here* matters. To understand *why* an
|
|
317
397
|
instance went where it did, read the gateway conditions (FEEL expressions on the
|
|
318
398
|
sequence flows) for the element it is parked on (§5).
|
|
319
399
|
- **Prompts (agent base instructions):** `resources/prompts/*.md` — `review-round.md`,
|
|
@@ -369,11 +449,24 @@ Work through this order:
|
|
|
369
449
|
`NANO_PR_REVIEW_WAIT_TIMEOUT` (default `PT20M`); the poller also re-nudges the
|
|
370
450
|
reviewer periodically. If the reviewer bot is not provisioned on the repo, no
|
|
371
451
|
review will ever land — that is a repo-config problem, not an app bug.
|
|
372
|
-
5. **Cancel + resubmit** as a last resort. Cancel
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
452
|
+
5. **Cancel + resubmit** as a last resort. Cancel through the **app-owned** door —
|
|
453
|
+
**prefer the `cancelInstance` tool** (projected by sibling #667; the same
|
|
454
|
+
record-consistent path as the UI's per-row Cancel, `POST /app/actions/cancel
|
|
455
|
+
{ "processInstanceKey": "<PK>" }`), which cancels
|
|
456
|
+
the engine instance **and** marks the PR `abandoned` so it drops out of
|
|
457
|
+
`listActivePrs`. Then re-submit the PR (§1) to start a fresh loop. **Do not** cancel a
|
|
458
|
+
raw engine instance out from under the app with the engine-level
|
|
459
|
+
`urban_debug_cancel_instance` tool (or a direct engine REST cancel) — that cancels the
|
|
460
|
+
token but leaves the PR row inconsistent. Always go through `cancelInstance` /
|
|
461
|
+
`POST /app/actions/cancel` so the app's record state stays consistent. Note the door
|
|
462
|
+
sits at `/app/actions/cancel`, **outside** the `__BASE__` control-API base (ADR 0059) —
|
|
463
|
+
until #667 projects `cancelInstance`, that curl door is the only cancel path. No-MCP
|
|
464
|
+
fallback:
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
curl -sS -X POST __BASE__/../actions/cancel -H 'content-type: application/json' \
|
|
468
|
+
-d '{ "processInstanceKey": "<PK>" }'
|
|
469
|
+
```
|
|
377
470
|
|
|
378
471
|
---
|
|
379
472
|
|
|
@@ -767,3 +860,66 @@ Semantics:
|
|
|
767
860
|
`stage:"merged"` once its PR merges. So `wait[epic]` targeting a feature/epic **root issue**
|
|
768
861
|
observes that thread's aggregate frontier and releases on `stage:"merged" && active:false` either
|
|
769
862
|
way — see `getDeliveryGraphVocabulary` (the `epic` probe entry) for the structured contract.
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
## 10. Tool ↔ curl crosswalk (MCP-first, curl is the no-MCP fallback)
|
|
867
|
+
|
|
868
|
+
When you drive this instance over MCP, **call the projected tool** — every recipe above
|
|
869
|
+
leads with it. `curl __BASE__/…`/`curl __ENGINE__/…` is only the **no-MCP fallback** for an
|
|
870
|
+
agent that has no tools (see the runbook, `docs/mcp-runbook.md` §2/§3, and [§5
|
|
871
|
+
Fallback](mcp-runbook.md#5-fallback)). This table maps every guide action to its projected
|
|
872
|
+
tool name and its no-MCP fallback underneath (usually a curl door; a few rows fall back to reading a repo file instead).
|
|
873
|
+
|
|
874
|
+
App-owned operations (projected from this app's `openapi.yaml`; the `__BASE__` control API).
|
|
875
|
+
Rows tagged *(projected by sibling #NNN)* name a tool that lands with that sibling task —
|
|
876
|
+
until it does, only the curl door in the third column exists on the deployment:
|
|
877
|
+
|
|
878
|
+
| Guide action | Projected tool | No-MCP fallback |
|
|
879
|
+
|---|---|---|
|
|
880
|
+
| Which code is live (§0) | `getVersion` | `curl __BASE__/version` |
|
|
881
|
+
| Every PR in flight (§0/§5) | `listActivePrs` | `curl __BASE__/status` |
|
|
882
|
+
| List open escalations (§3) | `listEscalations` *(projected by sibling #666; curl door until then)* | `curl __BASE__/../../tasks/api/tasks` (the answerable list — carries each `userTaskKey`); `/status` filtered with the jq recipe just below this table is only a quick *indicator* (it surfaces `openEscalation` as a question string, **no `userTaskKey`**, so you can't answer from it) |
|
|
883
|
+
| Answer an escalation (§3) | `completeUserTask` (agent-assignee: `agentCompleteEscalation`) | `curl -X POST __BASE__/actions/complete-user-task` |
|
|
884
|
+
| Cancel an instance (record-consistent) (§7) | `cancelInstance` *(projected by sibling #667; curl door until then)* | `curl -X POST __BASE__/../actions/cancel` *(the `/app/actions/cancel` door, outside `__BASE__`)* |
|
|
885
|
+
| Publish a BPMN message (§7) | `postMessage` | `curl -X POST __BASE__/actions/message` |
|
|
886
|
+
| The operator guide itself | `getAgentInstructions` (full) / `getAgentGuide(section?)` (addressable) | `curl __BASE__/agent` / `curl __BASE__/agent/guide` |
|
|
887
|
+
|
|
888
|
+
The `listEscalations` no-MCP fallback that filters `/status` with `jq` lives here, not inside the
|
|
889
|
+
table cell above: a literal `|` can't be written in a GFM table cell without escaping it as `\|`,
|
|
890
|
+
and this guide is served **raw** (byte-for-byte, `__BASE__`/`__ENGINE__` aside) to no-MCP agents, so
|
|
891
|
+
the escaped form would be copied verbatim and the shell would treat `\|` as a literal argument
|
|
892
|
+
rather than a pipe. It's only a **quick indicator** — `/status` surfaces `openEscalation` as a
|
|
893
|
+
question string with **no `userTaskKey`**, so to actually *answer* one you still need the tasks
|
|
894
|
+
inbox (`curl __BASE__/../../tasks/api/tasks`) or the `listEscalations` tool for its `userTaskKey`
|
|
895
|
+
(§3). It selects the same `{ prKey, status, round, openEscalation }` fields as the §3 fallback for
|
|
896
|
+
consistency. Copy it from here:
|
|
897
|
+
|
|
898
|
+
```sh
|
|
899
|
+
curl __BASE__/status | jq '.prs[] | select(.openEscalation != null)
|
|
900
|
+
| { prKey, status, round, openEscalation }'
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
Framework engine-debug tools (owned by the nano-ide urban runtime, not this app; they front
|
|
905
|
+
the engine's Camunda-8 v2 REST API at `__ENGINE__`). The first three are always projected;
|
|
906
|
+
`urban_debug_search_jobs` / `urban_debug_search_variables` /
|
|
907
|
+
`urban_debug_get_process_definition_xml` are owned by the sibling nano-ide urban epic — use
|
|
908
|
+
them by name where the framework projects them, else drop to the curl door:
|
|
909
|
+
|
|
910
|
+
| Guide action | Projected tool | No-MCP fallback |
|
|
911
|
+
|---|---|---|
|
|
912
|
+
| Search process instances (§5) | `urban_debug_search_process_instances` | `curl -X POST __ENGINE__/process-instances/search` |
|
|
913
|
+
| Inspect element/wait states (§5) | `urban_debug_search_element_instance_wait_states` | `curl -X POST __ENGINE__/element-instances/search` |
|
|
914
|
+
| Active incidents (§5) | `urban_debug_search_incidents` | `curl -X POST __ENGINE__/incidents/search` |
|
|
915
|
+
| Jobs on an instance (§5) | `urban_debug_search_jobs` | `curl -X POST __ENGINE__/jobs/search` |
|
|
916
|
+
| Instance variables (§5) | `urban_debug_search_variables` | `curl -X POST __ENGINE__/variables/search` |
|
|
917
|
+
| Deployed BPMN model XML (§6) | `urban_debug_get_process_definition_xml` | read the source copy `resources/processes/*.bpmn` from the checkout |
|
|
918
|
+
| Engine-level cancel — **avoid** (§7) | `urban_debug_cancel_instance` | *(use the app-owned `cancelInstance` instead — engine cancel leaves the PR row inconsistent)* |
|
|
919
|
+
|
|
920
|
+
**Rule of thumb (agent-guide §5 fallback discipline):** if a row has a projected tool, use
|
|
921
|
+
it; reach for its curl door only when you are genuinely not on MCP (or the tool's sibling
|
|
922
|
+
task has not yet landed — see the *(projected by sibling #NNN)* tags above). The app-owned
|
|
923
|
+
cancel door (`cancelInstance`, #667) is always preferred over the engine-level
|
|
924
|
+
`urban_debug_cancel_instance`,
|
|
925
|
+
because only the app door transitions the PR/plan record to `abandoned`.
|
package/docs/mcp-runbook.md
CHANGED
|
@@ -91,7 +91,14 @@ family). Ask:
|
|
|
91
91
|
|
|
92
92
|
> *"Using workforce-local, show what's in flight and any open escalations."*
|
|
93
93
|
|
|
94
|
-
The agent should call the status operation tool, not curl.
|
|
94
|
+
The agent should call the status operation tool (`listActivePrs`), not curl. The operator
|
|
95
|
+
guide's **tool↔curl crosswalk** (agent-guide §10, via `getAgentGuide(section="tool-crosswalk")`)
|
|
96
|
+
maps every guide action to its projected tool name and the curl door underneath — so a
|
|
97
|
+
tool-aware agent leads with the tool (`listActivePrs`, `getVersion`, `completeUserTask`,
|
|
98
|
+
the `urban_debug_*` engine reads — plus `listEscalations` and `cancelInstance` *where the
|
|
99
|
+
deployment projects them*; those two are sibling tasks #666/#667 and are not on a
|
|
100
|
+
deployment that predates them, so fall back to the curl door there) and treats curl as
|
|
101
|
+
the no-MCP fallback only.
|
|
95
102
|
|
|
96
103
|
## 3. Debug a wedged instance
|
|
97
104
|
|
|
@@ -99,10 +106,19 @@ The agent should call the status operation tool, not curl.
|
|
|
99
106
|
> instance, compare engine truth against the app's projections, and tell me where
|
|
100
107
|
> it's stuck."*
|
|
101
108
|
|
|
102
|
-
The agent has: instance search, wait states,
|
|
109
|
+
The agent has: instance search, wait states, incidents (engine truth) — plus
|
|
110
|
+
`variables` where the framework projects the `urban_debug_search_variables` read
|
|
111
|
+
(otherwise via the engine curl fallback) — plus
|
|
103
112
|
the `urban_*` projection reads (app belief) + the operator guide (the
|
|
104
113
|
`getAgentInstructions` tool) for the convergence-loop-specific meaning of each wedge
|
|
105
|
-
shape. A wedge is frequently exactly a disagreement between the two planes.
|
|
114
|
+
shape. A wedge is frequently exactly a disagreement between the two planes. The guide's
|
|
115
|
+
**tool↔curl crosswalk** (agent-guide §10) names each engine-truth tool
|
|
116
|
+
(`urban_debug_search_process_instances` / `urban_debug_search_element_instance_wait_states`
|
|
117
|
+
/ `urban_debug_search_incidents`, and where projected `urban_debug_search_jobs` /
|
|
118
|
+
`urban_debug_search_variables` / `urban_debug_get_process_definition_xml`) so the agent
|
|
119
|
+
reaches for the tool, not the raw `__ENGINE__` curl — and steers cancels to the app-owned
|
|
120
|
+
`cancelInstance` (projected by sibling #667; until it lands, the `/app/actions/cancel`
|
|
121
|
+
door the UI's Cancel uses), never the record-desyncing engine-level `urban_debug_cancel_instance`.
|
|
106
122
|
|
|
107
123
|
## 4. Guard posture
|
|
108
124
|
|
|
@@ -166,7 +182,8 @@ limit. Over MCP, prefer the **addressable** companion tool `getAgentGuide(sectio
|
|
|
166
182
|
|
|
167
183
|
- **No argument** → a compact **table of contents**: every stable section id
|
|
168
184
|
(`orient`, `submit-pr`, `submit-epic`, `escalations`, `lifecycle`, `debug`,
|
|
169
|
-
`debug-models`, `unstick`, `raise-issue`, `delivery-graphs`) with a
|
|
185
|
+
`debug-models`, `unstick`, `raise-issue`, `delivery-graphs`, `tool-crosswalk`) with a
|
|
186
|
+
one-line summary.
|
|
170
187
|
- **`section=<id>`** → **only** that section's markdown, small enough to fit a typical
|
|
171
188
|
limit. An unknown id is rejected with `issues[{path,message}]` listing the valid ids.
|
|
172
189
|
|
package/openapi.yaml
CHANGED
|
@@ -3628,7 +3628,7 @@ paths:
|
|
|
3628
3628
|
example: delivery-graphs
|
|
3629
3629
|
description: OPTIONAL stable section id (from the table of contents `getAgentGuide` returns
|
|
3630
3630
|
with no argument), e.g. `orient`, `submit-pr`, `submit-epic`, `escalations`, `lifecycle`,
|
|
3631
|
-
`debug`, `debug-models`, `unstick`, `raise-issue`, `delivery-graphs`. Omit it to get the
|
|
3631
|
+
`debug`, `debug-models`, `unstick`, `raise-issue`, `delivery-graphs`, `tool-crosswalk`. Omit it to get the
|
|
3632
3632
|
table of contents. An unknown id yields a 400 listing the valid ids.
|
|
3633
3633
|
responses:
|
|
3634
3634
|
"200":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/nano-workforce",
|
|
3
|
-
"version": "0.170.
|
|
3
|
+
"version": "0.170.1",
|
|
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",
|
package/pages/overview.page.json
CHANGED
|
@@ -82,9 +82,9 @@
|
|
|
82
82
|
"variant": "ghost",
|
|
83
83
|
"modal": {
|
|
84
84
|
"title": "Point your agent at Nano Workforce",
|
|
85
|
-
"description": "Copy this prompt and paste it into your coding agent (Copilot, Claude, etc.). It connects the agent to this instance's MCP server \u2014 the workforce's operations become native tools (including the live operator guide as `getAgentInstructions`), so it can drive and debug your workforce; agents with no MCP client fall back to fetching the operator skill. Note: `/app/mcp` is served on the same HTTP surface as the rest of the app \u2014 reachable on loopback by default, and from a remote instance (merlin, an ngrok tunnel) only when the app is bound wide (`network.bind`) or fronted by a reverse proxy, per the MCP runbook.",
|
|
85
|
+
"description": "Copy this prompt and paste it into your coding agent (Copilot, Claude, etc.). It connects the agent to this instance's MCP server \u2014 the workforce's operations become native tools (including the live operator guide as `getAgentInstructions`, or its addressable companion `getAgentGuide(section?)` \u2014 preferred over the ~43KB blob to avoid tool-result overrun), so it can drive and debug your workforce; agents with no MCP client fall back to fetching the operator skill. Note: `/app/mcp` is served on the same HTTP surface as the rest of the app \u2014 reachable on loopback by default, and from a remote instance (merlin, an ngrok tunnel) only when the app is bound wide (`network.bind`) or fronted by a reverse proxy, per the MCP runbook.",
|
|
86
86
|
"copyLabel": "Copy prompt",
|
|
87
|
-
"copyText": "Add this running Nano Workforce instance as an MCP server, then help me drive and debug my workforce \u2014 a durable orchestration app that drives pull requests to review convergence against an automated reviewer, merges them, and can take a whole issue and plan \u2192 implement \u2192 converge it across a fleet of coding agents.\n\nRegister ONE MCP server entry per instance (streamable-HTTP transport, URL {{appBase}}app/mcp). Naming the instance makes targeting the wrong one structurally impossible \u2014 its tools are namespaced under that name:\n\n copilot mcp add --transport http workforce-local {{appBase}}app/mcp\n\nGive each instance its own entry (e.g. `workforce-merlin` for a LAN/remote node). In config form (~/.copilot/mcp-config.json or repo-scoped .mcp.json):\n\n {\n \"mcpServers\": {\n \"workforce-local\": {\n \"type\": \"http\",\n \"url\": \"{{appBase}}app/mcp\",\n \"tools\": [\"*\"]\n }\n }\n }\n\nIf this instance is secured with a shared secret (NANO_PR_WEBHOOK_SECRET), pass it on the MCP connection \u2014 reads AND mutations both require it:\n\n copilot mcp add --transport http workforce-local {{appBase}}app/mcp \\\n --header \"x-hook-secret: $NANO_PR_WEBHOOK_SECRET\"\n\nIn config form, add a `headers` entry alongside `url` on that server (per the MCP runbook) \u2014 the config path has no header flag, so omitting this yields 401s:\n\n \"headers\": { \"x-hook-secret\": \"$NANO_PR_WEBHOOK_SECRET\" }\n\nA Basic-Auth-fronted instance (behind a reverse proxy) additionally needs `Authorization: Basic \u2026` on the connection.\n\nMCP servers register at host startup \u2014 add the entry, THEN start a new session so its tools load. The `workforce-*` tools then appear (the full set of operations projected from this instance's OpenAPI contract, including the live operator guide itself as the `getAgentInstructions` read tool
|
|
87
|
+
"copyText": "Add this running Nano Workforce instance as an MCP server, then help me drive and debug my workforce \u2014 a durable orchestration app that drives pull requests to review convergence against an automated reviewer, merges them, and can take a whole issue and plan \u2192 implement \u2192 converge it across a fleet of coding agents.\n\nRegister ONE MCP server entry per instance (streamable-HTTP transport, URL {{appBase}}app/mcp). Naming the instance makes targeting the wrong one structurally impossible \u2014 its tools are namespaced under that name:\n\n copilot mcp add --transport http workforce-local {{appBase}}app/mcp\n\nGive each instance its own entry (e.g. `workforce-merlin` for a LAN/remote node). In config form (~/.copilot/mcp-config.json or repo-scoped .mcp.json):\n\n {\n \"mcpServers\": {\n \"workforce-local\": {\n \"type\": \"http\",\n \"url\": \"{{appBase}}app/mcp\",\n \"tools\": [\"*\"]\n }\n }\n }\n\nIf this instance is secured with a shared secret (NANO_PR_WEBHOOK_SECRET), pass it on the MCP connection \u2014 reads AND mutations both require it:\n\n copilot mcp add --transport http workforce-local {{appBase}}app/mcp \\\n --header \"x-hook-secret: $NANO_PR_WEBHOOK_SECRET\"\n\nIn config form, add a `headers` entry alongside `url` on that server (per the MCP runbook) \u2014 the config path has no header flag, so omitting this yields 401s:\n\n \"headers\": { \"x-hook-secret\": \"$NANO_PR_WEBHOOK_SECRET\" }\n\nA Basic-Auth-fronted instance (behind a reverse proxy) additionally needs `Authorization: Basic \u2026` on the connection.\n\nMCP servers register at host startup \u2014 add the entry, THEN start a new session so its tools load. The `workforce-*` tools then appear (the full set of operations projected from this instance's OpenAPI contract, including the live operator guide itself as the `getAgentInstructions` read tool \u2014 or its addressable companion `getAgentGuide(section?)`, which the MCP runbook recommends over the ~43KB blob to avoid tool-result overrun \u2014 plus the `urban_debug_*` family for inspecting a wedged instance's process instances, wait states, and incidents).\n\nThe instance's operations are now native tools. Ask, naming the instance: \"Using workforce-local, show what's in flight and any open escalations.\" It should call the status tool, not curl. Operator-only doors (the delivery-graph stage/dispatch/dismiss lifecycle \u2014 the human click IS the approval) are deliberately not tools.\n\nNo MCP client? The curl path is unchanged \u2014 fetch and follow the live guide (the response is JSON with a `skill` markdown field), adding `-H \"x-hook-secret: <secret>\"` if this instance is secured:\n\n curl -sS {{appBase}}app/api/agent/skill\n\nThat skill bootstraps you to this instance's live operator guide at {{appBase}}app/api/agent (the same guide MCP exposes as `getAgentInstructions`, or section-addressably as `getAgentGuide`). If you find a bug or a stuck process, the guide explains how to raise an issue or a PR against nanobpm/nano-workforce."
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
},
|