@nanobpm/nano-workforce 0.46.2 → 0.48.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 CHANGED
@@ -1,3 +1,17 @@
1
+ # [0.48.0](https://github.com/nanobpm/nano-workforce/compare/v0.47.0...v0.48.0) (2026-08-12)
2
+
3
+
4
+ ### Features
5
+
6
+ * **pages:** render PR incident as a red badge on the home grid ([#132](https://github.com/nanobpm/nano-workforce/issues/132)) ([7a58015](https://github.com/nanobpm/nano-workforce/commit/7a58015676b8d0ca6ec7b28c7943504b5f3dfb0e)), closes [#94](https://github.com/nanobpm/nano-workforce/issues/94)
7
+
8
+ # [0.47.0](https://github.com/nanobpm/nano-workforce/compare/v0.46.2...v0.47.0) (2026-08-12)
9
+
10
+
11
+ ### Features
12
+
13
+ * **ui:** Agent Instructions button (copy-paste agent prompt) ([#126](https://github.com/nanobpm/nano-workforce/issues/126)) ([4c022ab](https://github.com/nanobpm/nano-workforce/commit/4c022abcbaa90501cacc27ac0b9c8d77b3f81df1)), closes [nanobpm/nano-ide#196](https://github.com/nanobpm/nano-ide/issues/196)
14
+
1
15
  ## [0.46.2](https://github.com/nanobpm/nano-workforce/compare/v0.46.1...v0.46.2) (2026-08-12)
2
16
 
3
17
 
@@ -1,10 +1,14 @@
1
- // End-to-end pilot for @nanobpm/urban-testkit (nano-ide issue #157, slice S3).
1
+ // End-to-end pilot for @nanobpm/urban-testkit (nano-ide issue #157, slices S3 + S4).
2
2
  //
3
3
  // Boots this whole Urban app in-process against the WASM engine and a virtual clock via
4
4
  // `bootTestApp`, then drives its real ADR-0059 OpenAPI operations by `operationId` — the same
5
5
  // spec-driven `/app/api/*` surface a browser, a CI relay, or Swagger hit in production. No socket
6
6
  // is opened, no wall-clock is waited on, and no GitHub network is touched.
7
7
  //
8
+ // It also adopts the S4 coverage-exhaustive gate: booted with `{ coverage: true }`, it gates a
9
+ // curated `pilot:operations` surface (the operations this pilot claims to own) and prints an
10
+ // informational whole-app coverage snapshot for the operations/workers it does not yet drive.
11
+ //
8
12
  // Network isolation: the app's GitHub transport (app/github.ts) is forced to `token` mode with no
9
13
  // token, so every best-effort GitHub read short-circuits to `null`/idle instead of reaching out.
10
14
  // That keeps the pilot hermetic and deterministic in CI.
@@ -60,6 +64,14 @@ const HARNESS_ENV = {
60
64
  NANO_APP_DB_URL: `file:${join(DB_DIR, "app.db")}`,
61
65
  } as const;
62
66
 
67
+ // The operations this pilot claims to own — the slice of the app's OpenAPI surface it drives end
68
+ // to end. The coverage gate below is scoped to exactly these: if you add one here without a test
69
+ // that drives it, `assertFullCoverage` goes red. (The app declares 10 operations + 16 workers in
70
+ // total; this pilot is deliberately a representative slice, not a whole-app sweep, so it does NOT
71
+ // gate the auto-declared "operations"/"workers" surfaces — it reports them instead, see below.)
72
+ const PILOT_OPERATIONS = ["appendBlackboard", "readBlackboard", "startConvergenceLoop"] as const;
73
+ const PILOT_SURFACE = "pilot:operations";
74
+
63
75
  describe("nano-workforce e2e (urban-testkit pilot)", () => {
64
76
  let app: TestApp;
65
77
 
@@ -68,9 +80,13 @@ describe("nano-workforce e2e (urban-testkit pilot)", () => {
68
80
  savedEnv.set(k, process.env[k]);
69
81
  process.env[k] = v;
70
82
  }
71
- app = await bootTestApp(APP_ROOT, { env: HARNESS_ENV });
83
+ // Enable the S4 coverage gate: `app.coverage` is pre-declared with this app's "operations"
84
+ // (from openapi.yaml) and "workers" (from nano.app.json) surfaces, and records each hit
85
+ // automatically as the pilot drives operations / the engine runs jobs.
86
+ app = await bootTestApp(APP_ROOT, { env: HARNESS_ENV, coverage: true });
72
87
  // This app declares an `api` binding, so the spec-driven driver must be present.
73
88
  assert.ok(app.api, "app.api driver should be defined (nano.app.json declares an `api` binding)");
89
+ assert.ok(app.coverage, "app.coverage should be defined (booted with { coverage: true })");
74
90
  });
75
91
 
76
92
  after(async () => {
@@ -172,4 +188,35 @@ describe("nano-workforce e2e (urban-testkit pilot)", () => {
172
188
  const reconciled = await prs.findOne({ pr_key: prKey });
173
189
  assert.equal(reconciled?.status, "abandoned", "reconciler abandoned the terminated PR's row");
174
190
  });
191
+
192
+ test("coverage gate: every operation the pilot claims to own was exercised", () => {
193
+ const coverage = app.coverage;
194
+ assert.ok(coverage);
195
+
196
+ // The auto-declared "operations" surface has recorded (across the tests above) exactly the
197
+ // operations this pilot drove. Mirror the pilot-owned ones into a curated surface and gate
198
+ // THAT — so the pilot fails the moment a `PILOT_OPERATIONS` entry lacks a driving test, without
199
+ // demanding whole-app coverage (this is a representative slice, not a full sweep).
200
+ const report = coverage.report();
201
+ const opsExercised = new Set(
202
+ report.surfaces.find((s) => s.surface === "operations")?.exercised ?? [],
203
+ );
204
+ coverage.declareSurface(PILOT_SURFACE, PILOT_OPERATIONS);
205
+ for (const id of PILOT_OPERATIONS) {
206
+ if (opsExercised.has(id)) coverage.record(PILOT_SURFACE, id);
207
+ }
208
+ // Fails, naming any un-driven pilot operation, if PILOT_OPERATIONS grows without a test.
209
+ coverage.assertFullCoverage({ surfaces: [PILOT_SURFACE] });
210
+
211
+ // Informational (non-failing) whole-app snapshot: how much of the app's total surface this
212
+ // pilot exercises. Surfaces the remaining operations/workers as a roadmap for widening the
213
+ // pilot, without turning the deliberate slice into a red build.
214
+ for (const surface of report.surfaces) {
215
+ const { exercised, declared, missing } = surface;
216
+ console.log(
217
+ `[coverage] ${surface.surface}: ${exercised.length}/${declared.length} exercised` +
218
+ (missing.length ? ` — not yet driven: ${missing.join(", ")}` : ""),
219
+ );
220
+ }
221
+ });
175
222
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanobpm/nano-workforce",
3
- "version": "0.46.2",
3
+ "version": "0.48.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",
@@ -46,11 +46,11 @@
46
46
  "lint:fix": "biome check --write app operations workers pages components scripts e2e main.ts"
47
47
  },
48
48
  "dependencies": {
49
- "@nanobpm/urban": "^0.42.0"
49
+ "@nanobpm/urban": "^0.43.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@biomejs/biome": "^2.4.11",
53
- "@nanobpm/urban-testkit": "^0.3.0",
53
+ "@nanobpm/urban-testkit": "^0.4.0",
54
54
  "@semantic-release/changelog": "^6.0.3",
55
55
  "@semantic-release/git": "^10.0.1",
56
56
  "@semantic-release/npm": "^13.1.5",
@@ -27,6 +27,20 @@
27
27
  "variant": "sub"
28
28
  }
29
29
  },
30
+ {
31
+ "type": "button",
32
+ "id": "agent-instructions",
33
+ "props": {
34
+ "label": "🤖 Agent Instructions",
35
+ "variant": "ghost",
36
+ "modal": {
37
+ "title": "Point your agent at Nano Workforce",
38
+ "description": "Copy this prompt and paste it into your coding agent (Copilot, Claude, etc.). It tells the agent to read this workforce's live operator guide, then help you drive and debug it.",
39
+ "copyLabel": "Copy prompt",
40
+ "copyText": "You are helping me operate a running Nano Workforce instance — 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\nFirst, fetch and read its live operator guide. It tells you how to submit PRs and epics for convergence (including whether to go all the way to merge or stop at review consensus), how to find engine instances and relate them to PRs via the Nano/Camunda-8 REST API, how to inspect the models and prompts, and how to help me untangle escalations:\n\n curl -sS {{appBase}}app/api/agent\n\nIf this instance is secured with a shared secret (NANO_PR_WEBHOOK_SECRET), add its value as an x-hook-secret header, otherwise the request returns 401:\n\n curl -sS -H \"x-hook-secret: <secret>\" {{appBase}}app/api/agent\n\nThen follow that guide to help me drive and debug this workforce. If you find a bug or a stuck process, the guide explains how to raise an issue or a PR against nanobpm/nano-workforce."
41
+ }
42
+ }
43
+ },
30
44
  {
31
45
  "type": "actionForm",
32
46
  "id": "submit",
@@ -73,7 +87,7 @@
73
87
  "columns": [
74
88
  { "field": "pr_key", "header": "PR", "linkField": "url" },
75
89
  { "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
76
- { "field": "incident_message", "header": "Incident" },
90
+ { "field": "incident_message", "header": "Incident", "badge": { "tone": "danger", "label": "1" } },
77
91
  { "field": "current_round", "header": "Round" },
78
92
  { "field": "active_worker", "header": "Agent" },
79
93
  { "field": "updated_at", "header": "Updated" }