@nanobpm/nano-workforce 0.210.1 → 0.211.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/app/service.ts CHANGED
@@ -1464,7 +1464,10 @@ export function isOrphanedPr(p: {
1464
1464
  // Provenance only explains the CURRENT owner's termination: a reason bound to a different
1465
1465
  // (predecessor) instance never masks an orphaned successor.
1466
1466
  !hasOwnTerminationReason(p) &&
1467
- p.merged_at == null
1467
+ p.merged_at == null &&
1468
+ // #959: a landing recorded only in the merge audit derives `merged` (base still `merging`,
1469
+ // `merged_at` NULL) — that PR landed, it is not orphaned.
1470
+ p.derived_status !== "merged"
1468
1471
  );
1469
1472
  }
1470
1473
 
@@ -2053,7 +2056,7 @@ async function classifyWaveTarget(
2053
2056
  token: string,
2054
2057
  ): Promise<"cleared" | "closed" | "pending"> {
2055
2058
  const tracked = await prsTracking(data).get(prKey);
2056
- if (tracked && tracked.status === "merged") return "cleared"; // worker-owned terminal, passes through the derive edge unchanged
2059
+ if (tracked && (tracked.status === "merged" || tracked.derived_status === "merged")) return "cleared"; // worker-owned terminal, passes through the derive edge unchanged
2057
2060
  if (tracked && tracked.derived_status === ABANDONED_STATUS) return "cleared"; // ADR-0065 derive-only terminal → non-blocking, no re-reconcile needed
2058
2061
  const parsed = parsePr(prKey);
2059
2062
  if (!parsed) return "cleared"; // unparseable ref can't be checked → never wedge the barrier
@@ -2139,7 +2142,12 @@ export async function mergeLaneDecisionForPr(data: DataLayer, prKey: string): Pr
2139
2142
  // cancelled/terminated (derive-only terminal → `derived_status === "abandoned"`; the base row is
2140
2143
  // still frozen at its transient). Reading only base `status` here missed a cancelled member and
2141
2144
  // stalled/misrouted the lane.
2142
- if (lanePr && (lanePr.status === "merged" || lanePr.derived_status === "abandoned")) {
2145
+ // #959: an audit-only landing (merge recorded, `pr.mark-merged` never ran) derives `merged` while
2146
+ // the base row is still `merging` — it is just as complete as a base `merged` row.
2147
+ if (
2148
+ lanePr &&
2149
+ (lanePr.status === "merged" || lanePr.derived_status === "merged" || lanePr.derived_status === "abandoned")
2150
+ ) {
2143
2151
  completedPrKeys.add(lanePr.pr_key);
2144
2152
  }
2145
2153
  }
@@ -319,3 +319,47 @@ test("startPlan re-admits a derive-only-abandoned epic (base 'dispatched', deriv
319
319
  assertEquals(stores.plan_tasks.rows.length, 0);
320
320
  });
321
321
  });
322
+
323
+ // #959: an audit-only landing (merge recorded in `merges`, `pr.mark-merged` never ran) derives
324
+ // `merged` while the base row is still `merging`. It must count as lane-complete and never surface as
325
+ // `orphaned` in /status.
326
+ test("#959 mergeLaneDecisionForPr treats an audit-only landed (derived merged) predecessor as complete", async () => {
327
+ const PLAN_KEY = "o/r#100";
328
+ const stores: Stores = {
329
+ plan_tasks: {
330
+ rows: [
331
+ { id: 1, plan_key: PLAN_KEY, task_id: "a", pr_key: "o/r#1" },
332
+ { id: 2, plan_key: PLAN_KEY, task_id: "b", pr_key: "o/r#2" },
333
+ ],
334
+ key: "id",
335
+ },
336
+ plan_merge_exclusions: {
337
+ rows: [{ id: 1, plan_key: PLAN_KEY, task_a: "a", task_b: "b", files: JSON.stringify(["shared.ts"]), source: "file-overlap" }],
338
+ key: "id",
339
+ },
340
+ plan_task_deps: { rows: [], key: "plan_key" },
341
+ pull_requests: {
342
+ rows: [
343
+ { pr_key: "o/r#1", repo: "o/r", number: 1, status: "merging", derived_status: "merged", merged_at: null },
344
+ { pr_key: "o/r#2", repo: "o/r", number: 2, status: "converging", derived_status: "converging" },
345
+ ],
346
+ key: "pr_key",
347
+ },
348
+ };
349
+ const decision = await mergeLaneDecisionForPr(memData(stores), "o/r#2");
350
+ assertEquals(decision?.isHeld, false);
351
+ });
352
+
353
+ test("#959 activePrs does not surface an audit-only landed (derived merged) PR as orphaned", async () => {
354
+ const stores: Stores = {
355
+ pull_requests: {
356
+ rows: [
357
+ { pr_key: "o/r#1", repo: "o/r", number: 1, url: "u1", status: "merging", derived_status: "merged", merged_at: null, current_round: 1, updated_at: "2024-01-01" },
358
+ ],
359
+ key: "pr_key",
360
+ },
361
+ escalations: { rows: [], key: "id" },
362
+ };
363
+ const active = await activePrs(memData(stores));
364
+ assertEquals(active.some((p) => p.status === "orphaned"), false);
365
+ });
package/build-info.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.210.1",
3
- "gitSha": "e2c4e966b566ce8b4bdc19e84bceb130909a981c",
4
- "gitTag": "v0.210.1",
5
- "builtAt": "2026-11-12T12:00:58.232Z"
2
+ "version": "0.211.0",
3
+ "gitSha": "4ba5c8c61e6d5bfceaff3883c1719795797eb9a4",
4
+ "gitTag": "v0.211.0",
5
+ "builtAt": "2026-11-14T19:46:33.652Z"
6
6
  }
@@ -0,0 +1,40 @@
1
+ -- Backfill for issue #959: merged PRs whose run was terminated afterwards showed as "abandoned".
2
+ --
3
+ -- ROOT CAUSE. `pull_requests__tracking.derived_status` mapped ANY terminated run to `abandoned`
4
+ -- without consulting the PR's merge state. Of the 24 top-level runs terminated in November, 23
5
+ -- belong to PRs that MERGED on GitHub — an operator cancelled a redundant post-merge loop, an
6
+ -- out-of-band merge (Mergify / a manual click) was reconciled, or a restart/upgrade cleaned up. Every
7
+ -- one read "abandoned", which made the status untrustworthy and skewed retro/conformance stats
8
+ -- (`LANDED_PR_STATUSES` excludes `abandoned`, so merged PRs didn't count as landed).
9
+ --
10
+ -- THE FIX is the merge-aware derivation (app/prTrackingReadModel.ts): a landed PR now reads `merged`
11
+ -- regardless of how its instance ended, keyed off the base row's `merged_at`. That derivation is
12
+ -- merge-aware ONLY when `merged_at` is set — but several out-of-band/reconciled merges terminated the
13
+ -- loop BEFORE `pr.mark-merged` (or the review-wait retire) could stamp `merged_at`, leaving the base
14
+ -- row frozen at its in-flight transient (`converging`/`waiting_merge`/…) with `merged_at` NULL. For
15
+ -- those rows the derivation still has no merge signal, so they would keep reading `abandoned`.
16
+ --
17
+ -- This migration re-derives the existing rows so the 23 show as `merged`: any `pull_requests` row
18
+ -- that has a `merged` outcome in its `merges` audit trail but whose base `status` is not already
19
+ -- `merged` (and that has no `merged_at` yet) is stamped `status='merged'` + `merged_at`. The audit
20
+ -- trail (`merges.outcome='merged'`) is the canonical record that the PR landed — written by
21
+ -- `pr.mark-merged`, the merge worker's already-merged short-circuit, and the out-of-band retire path.
22
+ -- With the base row settled to `merged`, the derivation reads `merged` whether or not the instance
23
+ -- was later terminated (the terminated edge is gated on `merged_at IS NULL`).
24
+ --
25
+ -- Idempotent and additive-only: it touches only rows with a recorded `merged` audit outcome that are
26
+ -- not already settled `merged`, and never overwrites an existing `merged_at`. `merged_at` is stamped
27
+ -- from the earliest `merged` audit row (`MIN(m.at)`), falling back to the current timestamp when that
28
+ -- is somehow NULL. Safe to re-run.
29
+ UPDATE pull_requests
30
+ SET status = 'merged',
31
+ merged_at = COALESCE(
32
+ merged_at,
33
+ (SELECT MIN(m.at) FROM merges m WHERE m.pr_key = pull_requests.pr_key AND m.outcome = 'merged'),
34
+ datetime('now')
35
+ ),
36
+ updated_at = datetime('now')
37
+ WHERE status <> 'merged'
38
+ AND EXISTS (
39
+ SELECT 1 FROM merges m WHERE m.pr_key = pull_requests.pr_key AND m.outcome = 'merged'
40
+ );
package/main.ts CHANGED
@@ -23,10 +23,16 @@ import { type AgenticChannelHandle, mountAgenticChannel } from "./app/agentic/ch
23
23
  import { makeElementInstanceResolver } from "./app/agentic/element-instance.ts";
24
24
  import { mountConsoleRedirect, resolveConsoleOrigin } from "./app/consoleRedirect.ts";
25
25
  import { announceEngine, resolveEngineAddress } from "./app/enginePreflight.ts";
26
+ import { registerPullRequestTrackingReadModel } from "./app/prTrackingReadModel.ts";
26
27
  import { runEngineReconcile } from "./app/reconcile.ts";
27
28
  import { MAX_ROUNDS, pollOnce } from "./app/service.ts";
28
29
  import { envVar } from "./app/version.ts";
29
30
 
31
+ // Issue #959: register the app-owned, merge-aware `pull_requests__tracking` read model BEFORE the
32
+ // runtime mounts workers/instance-tracking, so the framework provisions THIS derivation (a landed PR
33
+ // reads `merged`, never `abandoned`, however its loop ended) instead of the merge-blind default.
34
+ registerPullRequestTrackingReadModel();
35
+
30
36
  const PORT = Number(process.env.PR_REVIEW_PORT ?? 3000);
31
37
  const POLL_MS = Number(process.env.NANO_PR_POLL_MS ?? 60_000);
32
38
  const GITHUB_TOKEN = process.env.GITHUB_TOKEN ?? "";
package/openapi.yaml CHANGED
@@ -4312,6 +4312,8 @@ paths:
4312
4312
  /status:
4313
4313
  get:
4314
4314
  operationId: listActivePrs
4315
+ x-cli:
4316
+ command: [status]
4315
4317
  summary: List every tracked PR currently in flight (not converged/abandoned), newest-updated first.
4316
4318
  security:
4317
4319
  - hookSecret: []
@@ -4332,6 +4334,8 @@ paths:
4332
4334
  /actions/cancel:
4333
4335
  post:
4334
4336
  operationId: cancelInstance
4337
+ x-cli:
4338
+ command: [cancel]
4335
4339
  summary: "Cancel a wedged run the app-owned, record-consistent way: terminate the engine instance AND flip its PR/plan record to `abandoned` (issue #667). The correct unstick — go through the app so its record state stays consistent, rather than engine-level `urban_debug_cancel_instance`, which cancels out from under the app and leaves the PR row inconsistent."
4336
4340
  security:
4337
4341
  - hookSecret: []
@@ -4391,6 +4395,9 @@ paths:
4391
4395
  /actions/cancel-pr:
4392
4396
  post:
4393
4397
  operationId: cancelPrRun
4398
+ x-cli:
4399
+ command: [cancel-pr]
4400
+ audience: operator
4394
4401
  # x-mcp exclusion: the page-only twin of `cancelInstance` (the per-row Cancel button). Agents use
4395
4402
  # the guarded `/actions/cancel` door.
4396
4403
  x-mcp:
@@ -4436,6 +4443,9 @@ paths:
4436
4443
  /actions/incident/retry:
4437
4444
  post:
4438
4445
  operationId: retryIncident
4446
+ x-cli:
4447
+ command: [incidents, retry]
4448
+ audience: operator
4439
4449
  # x-mcp exclusion (ADR 0067 §2 / nano-ide#488): operator-only remediation door. Retrying an
4440
4450
  # incident-parked run is the human unstick action on the Convergence/Home grids (the incident
4441
4451
  # row's "Retry incident" button); agents diagnose through the read surface and escalate, they
@@ -4491,6 +4501,8 @@ paths:
4491
4501
  /auto-resolutions:
4492
4502
  get:
4493
4503
  operationId: listAutoResolutions
4504
+ x-cli:
4505
+ command: [auto-resolutions, list]
4494
4506
  summary: List the escalation steward's auto-resolution records (reviewable), newest first.
4495
4507
  description: >-
4496
4508
  The durable, reviewable record of every escalation the opt-in steward (issue #889) resolved
@@ -4537,6 +4549,8 @@ paths:
4537
4549
  /auto-resolutions/review:
4538
4550
  post:
4539
4551
  operationId: reviewAutoResolution
4552
+ x-cli:
4553
+ command: [auto-resolutions, review]
4540
4554
  summary: Mark an auto-resolution record dismissed (reviewed, no action) or intervened (operator stepped in).
4541
4555
  description: >-
4542
4556
  The Auto-resolutions view's review affordance (issue #889). `dismiss` marks the record
@@ -4594,6 +4608,8 @@ paths:
4594
4608
  /incidents:
4595
4609
  get:
4596
4610
  operationId: listIncidents
4611
+ x-cli:
4612
+ command: [incidents, list]
4597
4613
  summary: List every tracked run parked on an ACTIVE engine incident, oldest first.
4598
4614
  description: >-
4599
4615
  Sibling of `/escalations` for technical liveness faults (issue #931). Read-only projection
@@ -4619,6 +4635,8 @@ paths:
4619
4635
  /escalations:
4620
4636
  get:
4621
4637
  operationId: listEscalations
4638
+ x-cli:
4639
+ command: [escalations, list]
4622
4640
  summary: List every OPEN escalation awaiting a human/agent decision, with the completable userTaskKey.
4623
4641
  description: >-
4624
4642
  Discovery for the escalation-answer path (epic #664, issue #666): across every surfaced
@@ -4649,6 +4667,8 @@ paths:
4649
4667
  /lineage:
4650
4668
  get:
4651
4669
  operationId: getLineage
4670
+ x-cli:
4671
+ command: [lineage]
4652
4672
  summary: "Intent to progress lineage projection (issue #245): every request stitched into one arc (request, implementation, PRs, convergence, merge, outcome), active frontier first. Pass root to fetch a single origin's thread."
4653
4673
  security:
4654
4674
  - hookSecret: []
@@ -4676,6 +4696,8 @@ paths:
4676
4696
  /prs/history:
4677
4697
  get:
4678
4698
  operationId: getPrHistory
4699
+ x-cli:
4700
+ command: [prs, history]
4679
4701
  summary: "A PR's escalation + round history (issue #668): per-round status transitions and outcome, plus each escalation's kind, question/answer, and timestamps — the read that retires the DB-over-ssh fallback. Identify the PR by prKey, or by processKey. Reads the same rounds/escalations tables the Convergence page surfaces."
4680
4702
  security:
4681
4703
  - hookSecret: []
@@ -4715,6 +4737,8 @@ paths:
4715
4737
  /agentic/supply:
4716
4738
  get:
4717
4739
  operationId: getAgenticSupply
4740
+ x-cli:
4741
+ command: [agentic, supply]
4718
4742
  summary: The SUPPLY-ONLY agentic visibility report — the live worker list (family, host, current jobs,
4719
4743
  liveness) grouped by leaf, sourced from the H1 presence registry. Feeds the H5 cockpit page.
4720
4744
  security:
@@ -4736,6 +4760,8 @@ paths:
4736
4760
  /agentic/vocab:
4737
4761
  get:
4738
4762
  operationId: getAgenticVocab
4763
+ x-cli:
4764
+ command: [agentic, vocab]
4739
4765
  summary: The crew vocabulary artifact (enrolment epic 152 slice N1) — the ONE capability→token map a
4740
4766
  worker resolves its SERVE set against. Returns the networks/roles tree plus the flattened
4741
4767
  per-token enrolment requirements and the artifact version. Read-only; advisory.
@@ -4758,6 +4784,8 @@ paths:
4758
4784
  /agentic/enrol:
4759
4785
  post:
4760
4786
  operationId: enrolAgenticWorker
4787
+ x-cli:
4788
+ command: [agentic, enrol]
4761
4789
  summary: Resolve a worker's declared enrolment capability to its SERVE token set (ADR 0059 revised
4762
4790
  REGISTER→SERVE, per-worker). Returns the SERVE leaf tokens, the vocab version resolved against,
4763
4791
  and the liveness lease TTL. Pure/deterministic — idempotent per (app, worker).
@@ -4827,6 +4855,8 @@ paths:
4827
4855
  /agentic/registry:
4828
4856
  get:
4829
4857
  operationId: getAgenticRegistry
4858
+ x-cli:
4859
+ command: [agentic, registry]
4830
4860
  summary: The demand×supply report (enrolment epic 152 slice N1) — deployed demand (the models'
4831
4861
  taskDefinition leaves) diffed against live supply (the presence registry resolved through the
4832
4862
  crew vocab), per network, with missing-agent-type reds and the diversity SLO. Read-only; advisory.
@@ -4849,6 +4879,8 @@ paths:
4849
4879
  /agentic/transcripts:
4850
4880
  get:
4851
4881
  operationId: listAgenticTranscripts
4882
+ x-cli:
4883
+ command: [agentic, transcripts, list]
4852
4884
  summary: List captured agent sessions (H3/#146) — the durable transcripts an ephemeral agent flushed
4853
4885
  on job completion, readable AFTER it exited. Optional filters by jobKey / process instance / plan /
4854
4886
  time. Also serves the proxy-safe single-stream READ form `?stream=` (#744). Advisory read-only;
@@ -4954,6 +4986,12 @@ paths:
4954
4986
  /agentic/transcripts/{stream}:
4955
4987
  get:
4956
4988
  operationId: getAgenticTranscript
4989
+ # x-cli exclusion (#744): the legacy PATH form 404s behind a proxy that decodes %2F in a path
4990
+ # segment for slash-bearing worker-instance ids (`34:<instance>/<jobKey>` — covered by
4991
+ # app/agentic/transcript-url.test.ts). The proxy-safe single-stream read is the query form,
4992
+ # `agentic transcripts list --stream <stream>`; do not project this route into the CLI.
4993
+ x-cli:
4994
+ exclude: true
4957
4995
  summary: Fetch a stored transcript's bytes (H3/#146), range/offset-based so the cockpit terminal
4958
4996
  replays it through the same resume-from-offset renderer it uses for a live stream. PATH form
4959
4997
  of the single-stream read (back-compat; a slash-bearing id 404s behind a decoding proxy —
@@ -5004,6 +5042,8 @@ paths:
5004
5042
  /agentic/agent-instances:
5005
5043
  get:
5006
5044
  operationId: listAgentInstances
5045
+ x-cli:
5046
+ command: [agentic, instances, list]
5007
5047
  summary: >-
5008
5048
  List engine-native AgentInstances (issue #745/#747, umbrella #746) — the durable agent-task
5009
5049
  runs the worker harness minted against the `<zeebe:agentDefinition agentType="external"/>` marker,
@@ -5061,6 +5101,9 @@ paths:
5061
5101
  /agentic/agent-instances/{agentInstanceKey}/history:
5062
5102
  get:
5063
5103
  operationId: getAgentInstanceHistory
5104
+ x-cli:
5105
+ command: [agentic, instances, history]
5106
+ positional: [agentInstanceKey]
5064
5107
  summary: >-
5065
5108
  Fetch one AgentInstance's durable conversation history (turns + per-turn metrics) from
5066
5109
  engine `searchAgentInstanceHistory` (issue #745/#747), in conversational order, with the owning
@@ -5120,6 +5163,9 @@ paths:
5120
5163
  /reconcile:
5121
5164
  post:
5122
5165
  operationId: reconcileEngineState
5166
+ x-cli:
5167
+ command: [admin, reconcile]
5168
+ audience: operator
5123
5169
  summary: Reconcile engine-backed inflight projections after an engine reset/rewind (issue #622).
5124
5170
  description: >-
5125
5171
  The explicit operator command for the app-side reconciliation surface. Probes the engine's
@@ -5154,6 +5200,8 @@ paths:
5154
5200
  /version:
5155
5201
  get:
5156
5202
  operationId: getVersion
5203
+ x-cli:
5204
+ command: [version]
5157
5205
  summary: The running app's identity (app/urban versions, git sha/branch, runtime, pid, uptime).
5158
5206
  security:
5159
5207
  - hookSecret: []
@@ -5174,6 +5222,8 @@ paths:
5174
5222
  /agent:
5175
5223
  get:
5176
5224
  operationId: getAgentInstructions
5225
+ x-cli:
5226
+ command: [guide]
5177
5227
  summary: The agent operator guide (markdown) — how to submit PRs/epics, answer escalations, and
5178
5228
  debug this instance. Point a coding agent at this URL to drive and debug the workforce.
5179
5229
  security:
@@ -5195,6 +5245,8 @@ paths:
5195
5245
  /agent/skill:
5196
5246
  get:
5197
5247
  operationId: getAgentSkill
5248
+ x-cli:
5249
+ command: [guide, skill]
5198
5250
  summary: The portable operator skill (markdown SKILL.md) an agent runtime loads on demand — a
5199
5251
  thin bootstrap that resolves which instance to drive, then fetches the live operator guide
5200
5252
  (GET /agent). Surfaced from the Overview tab's "Agent Instructions" prompt.
@@ -5217,6 +5269,8 @@ paths:
5217
5269
  /agent/guide:
5218
5270
  get:
5219
5271
  operationId: getAgentGuide
5272
+ x-cli:
5273
+ command: [guide, full]
5220
5274
  summary: The operator guide, ADDRESSABLE — fetch one section instead of the whole ~43KB blob.
5221
5275
  Read-only, pure, idempotent. Call with NO `section` to get a compact table of contents (every
5222
5276
  stable section id + a one-line summary); call with `section` set to a TOC id (e.g.
@@ -5289,6 +5343,9 @@ paths:
5289
5343
  /actions/start/convergence-loop:
5290
5344
  post:
5291
5345
  operationId: startConvergenceLoop
5346
+ x-cli:
5347
+ command: [converge]
5348
+ positional: [pr]
5292
5349
  summary: Register/refresh a PR aggregate (idempotent on prKey) and start its convergence loop.
5293
5350
  requestBody:
5294
5351
  required: true
@@ -5376,6 +5433,9 @@ paths:
5376
5433
  /actions/start/plan-fanout:
5377
5434
  post:
5378
5435
  operationId: startPlanFanout
5436
+ x-cli:
5437
+ command: [start, epic]
5438
+ positional: [issue]
5379
5439
  summary: Register/refresh a plan aggregate (idempotent on planKey) and start the planning fan-out.
5380
5440
  requestBody:
5381
5441
  required: true
@@ -5451,6 +5511,8 @@ paths:
5451
5511
  /actions/start/epic-set:
5452
5512
  post:
5453
5513
  operationId: startEpicSet
5514
+ x-cli:
5515
+ command: [start, epic-set]
5454
5516
  summary: Admit a SET of epics plus their inter-epic dependency edges in one all-or-nothing call (issue #292).
5455
5517
  description: >-
5456
5518
  The set/batch admission door (slice S2). Validates the WHOLE submission before persisting
@@ -5581,6 +5643,9 @@ paths:
5581
5643
  /actions/start/sequence-issues:
5582
5644
  post:
5583
5645
  operationId: sequenceIssues
5646
+ x-cli:
5647
+ command: [sequence]
5648
+ positional: [issues]
5584
5649
  summary: Generate + STAGE the canonical "implement issue → converge → merge" delivery graph from a high-level intent (never dispatches). (epic #605 / S4)
5585
5650
  description: >-
5586
5651
  An INTENT-SHAPED door (epic nano-workforce#605, S4). ADR 0005's delivery graph is a closed
@@ -5716,6 +5781,8 @@ paths:
5716
5781
  /actions/compile-delivery-graph:
5717
5782
  post:
5718
5783
  operationId: compileDeliveryGraph
5784
+ x-cli:
5785
+ command: [graph, compile]
5719
5786
  summary: Validate + compile a delivery graph and STAGE it for operator review (never dispatches). (ADR 0005 Decision 7 / #460)
5720
5787
  description: >-
5721
5788
  The agent-facing delivery-graph door — the END of the agent's surface (ADR 0005 Decision 7,
@@ -6192,6 +6259,8 @@ paths:
6192
6259
  /actions/delivery-graph/preview:
6193
6260
  post:
6194
6261
  operationId: previewDeliveryGraph
6262
+ x-cli:
6263
+ command: [graph, preview]
6195
6264
  summary: UI JSON-paste PURE PREVIEW — parse a pasted delivery-graph JSON string and compile it, without staging. (ADR 0005 Decision 7 / #460 / #516)
6196
6265
  description: >-
6197
6266
  The human-facing UI JSON-paste PURE PREVIEW ingress. The Delivery Graphs page's "Preview"
@@ -6246,6 +6315,9 @@ paths:
6246
6315
  /actions/delivery-graph/stage:
6247
6316
  post:
6248
6317
  operationId: stageDeliveryGraph
6318
+ x-cli:
6319
+ command: [graph, stage]
6320
+ audience: operator
6249
6321
  # x-mcp exclusion (ADR 0067 §2 / nano-ide#488): operator-only cockpit door, kept OFF the
6250
6322
  # runtime-projected MCP tool surface. The staged-proposal lifecycle (stage -> dispatch ->
6251
6323
  # dismiss) is the human approval path (ADR 0005 Decision 7 — the operator's click IS the
@@ -6285,6 +6357,9 @@ paths:
6285
6357
  /actions/delivery-graph/dispatch:
6286
6358
  post:
6287
6359
  operationId: dispatchDeliveryGraph
6360
+ x-cli:
6361
+ command: [graph, dispatch]
6362
+ audience: operator
6288
6363
  # x-mcp exclusion (ADR 0067 §2 / nano-ide#488): the canonical operator-only door. Dispatch
6289
6364
  # approval IS a human clicking Dispatch in the cockpit (ADR 0005 Decision 7) — there is no
6290
6365
  # replayable token and no agent-facing dispatch handle, so it is never a projected MCP tool.
@@ -6335,6 +6410,9 @@ paths:
6335
6410
  /actions/delivery-graph/dismiss:
6336
6411
  post:
6337
6412
  operationId: dismissProposal
6413
+ x-cli:
6414
+ command: [graph, dismiss]
6415
+ audience: operator
6338
6416
  # x-mcp exclusion (ADR 0067 §2 / nano-ide#488): operator-only cockpit door — discarding a
6339
6417
  # staged proposal as noise is a human decision in the same approval class as dispatch, so it
6340
6418
  # stays off the projected MCP tool surface.
@@ -6373,6 +6451,8 @@ paths:
6373
6451
  /actions/delivery-graph/proposal-bpmn:
6374
6452
  post:
6375
6453
  operationId: previewProposalBpmn
6454
+ x-cli:
6455
+ command: [graph, proposal-bpmn]
6376
6456
  summary: Recompile a staged delivery-graph proposal's BPMN (with DI) for read-only preview — no deploy, no dispatch.
6377
6457
  description: >-
6378
6458
  Return the compiled BPMN 2.0 XML (including diagram interchange) of a staged delivery-graph
@@ -6413,6 +6493,8 @@ paths:
6413
6493
  /delivery-graph/staged:
6414
6494
  get:
6415
6495
  operationId: listStagedProposals
6496
+ x-cli:
6497
+ command: [graph, staged]
6416
6498
  summary: List the LIVE staged delivery-graph proposals awaiting dispatch (issue #511), newest first.
6417
6499
  description: >-
6418
6500
  The read behind the staged-proposals App-View AND the MCP `listStagedProposals` tool: every
@@ -6453,6 +6535,8 @@ paths:
6453
6535
  /delivery-graph/vocabulary:
6454
6536
  get:
6455
6537
  operationId: getDeliveryGraphVocabulary
6538
+ x-cli:
6539
+ command: [graph, vocabulary]
6456
6540
  summary: The closed delivery-graph vocabulary + wait-probe semantics as structured JSON (ADR 0005).
6457
6541
  description: >-
6458
6542
  Read tool (projected onto the MCP surface like `getAgentInstructions`). Returns the CLOSED
@@ -6709,6 +6793,8 @@ paths:
6709
6793
  /actions/delivery-graph/library/save:
6710
6794
  post:
6711
6795
  operationId: saveToLibrary
6796
+ x-cli:
6797
+ command: [graph, library, save]
6712
6798
  summary: Save a delivery graph to the reusable library — from a raw graph JSON or an existing proposal digest (issue #522, #519 S3).
6713
6799
  description: >-
6714
6800
  Persist a delivery graph to the reusable LIBRARY (issue #522, epic #519 S3) — the durable base
@@ -6764,6 +6850,8 @@ paths:
6764
6850
  /actions/delivery-graph/library/import:
6765
6851
  post:
6766
6852
  operationId: importToLibrary
6853
+ x-cli:
6854
+ command: [graph, library, import]
6767
6855
  summary: Import a delivery graph FROM A FILE into the reusable library (issue #524, #519 S5).
6768
6856
  description: >-
6769
6857
  Import a delivery graph into the LIBRARY from a filesystem file (issue #524, epic #519 S5). The
@@ -6823,6 +6911,8 @@ paths:
6823
6911
  /delivery-graph/library:
6824
6912
  get:
6825
6913
  operationId: listLibrary
6914
+ x-cli:
6915
+ command: [graph, library, list]
6826
6916
  summary: List the saved reusable delivery-graph library entries (issue #522, #519 S3), newest first.
6827
6917
  description: >-
6828
6918
  The read behind the Library App-View (S4/#523): every saved library entry, newest first, with
@@ -6849,6 +6939,9 @@ paths:
6849
6939
  /delivery-graph/library/{id}:
6850
6940
  get:
6851
6941
  operationId: getLibraryEntry
6942
+ x-cli:
6943
+ command: [graph, library, get]
6944
+ positional: [id]
6852
6945
  summary: Fetch one saved library entry by id (issue #522, #519 S3).
6853
6946
  description: >-
6854
6947
  Return one saved library entry (issue #522) by its `id`, including its full `graph` JSON. An
@@ -6884,6 +6977,9 @@ paths:
6884
6977
  $ref: "#/components/schemas/ErrorBody"
6885
6978
  delete:
6886
6979
  operationId: deleteLibraryEntry
6980
+ x-cli:
6981
+ command: [graph, library, delete]
6982
+ positional: [id]
6887
6983
  summary: Delete one saved library entry by id (issue #522, #519 S3).
6888
6984
  description: >-
6889
6985
  Delete one saved library entry (issue #522) by its `id`. Idempotent — deleting an id that names
@@ -6915,6 +7011,9 @@ paths:
6915
7011
  /actions/start/feature:
6916
7012
  post:
6917
7013
  operationId: startFeature
7014
+ x-cli:
7015
+ command: [start, feature]
7016
+ positional: [issue]
6918
7017
  summary: Register/refresh a feature-run aggregate (idempotent on featureKey) and start the single-issue run.
6919
7018
  requestBody:
6920
7019
  required: true
@@ -7259,6 +7358,9 @@ paths:
7259
7358
  /actions/message:
7260
7359
  post:
7261
7360
  operationId: postMessage
7361
+ x-cli:
7362
+ command: [admin, message]
7363
+ audience: operator
7262
7364
  summary: "Publish a BPMN message (optionally correlated) into the engine. Every escalation kind
7263
7365
  (task, plan-review, trial-merge, PR review-loop, PR merge-loop) is a native user task answered
7264
7366
  via the Tasks inbox / POST /actions/complete-user-task, not this route."
@@ -7307,6 +7409,9 @@ paths:
7307
7409
  /actions/complete-user-task:
7308
7410
  post:
7309
7411
  operationId: completeUserTask
7412
+ x-cli:
7413
+ command: [escalations, answer]
7414
+ positional: [userTaskKey]
7310
7415
  summary: "Complete an open native user-task escalation from the nwf Tasks inbox (issue #236).
7311
7416
  Submits the parked task's typed `.form` variables (e.g. a plan-review `{ directive, notes }`, a
7312
7417
  trial-merge `{ action, notes }`, a PR `{ answer }`, a feature escalation `{ resolution, answer }`,
@@ -7360,6 +7465,8 @@ paths:
7360
7465
  /actions/acknowledge-done:
7361
7466
  post:
7362
7467
  operationId: acknowledgeDone
7468
+ x-cli:
7469
+ command: [ack, done]
7363
7470
  summary: "Tick off a TERMINAL feature run (issue #254 §5). Stamps `acknowledged_at` on the run so
7364
7471
  the gateway recomputes its `list_bucket` to 'history', dropping the finished run out of the
7365
7472
  primary Active list into History. The Done twin of the blocked acknowledgement, but a terminal run is
@@ -7407,6 +7514,8 @@ paths:
7407
7514
  /actions/acknowledge-epic:
7408
7515
  post:
7409
7516
  operationId: acknowledgeEpic
7517
+ x-cli:
7518
+ command: [ack, epic]
7410
7519
  summary: "Dismiss a RESOLVED epic (issue #298). Stamps `acknowledged_at` on the `plans` row so
7411
7520
  the gateway recomputes its `list_bucket` to 'history' (and `ack_open` to 0), dropping the
7412
7521
  resolved epic out of the Active epic list into History. The epic twin of acknowledge-done, but
@@ -7458,6 +7567,8 @@ paths:
7458
7567
  /actions/acknowledge-pr:
7459
7568
  post:
7460
7569
  operationId: acknowledgePr
7570
+ x-cli:
7571
+ command: [ack, pr]
7461
7572
  summary: "Dismiss a TERMINAL pull request (issue #641). Stamps `acknowledged_at` on the
7462
7573
  `pull_requests` row so the `pull_requests_read_model` VIEW recomputes its `list_bucket` to
7463
7574
  'history' (and `ack_open` to 0), dropping the finished PR out of the Active convergence list
@@ -7507,6 +7618,8 @@ paths:
7507
7618
  /actions/acknowledge-delivery-graph:
7508
7619
  post:
7509
7620
  operationId: acknowledgeDeliveryGraph
7621
+ x-cli:
7622
+ command: [ack, graph]
7510
7623
  summary: "Dismiss a TERMINAL delivery-graph run (issue #641). Stamps `acknowledged_at` on the
7511
7624
  `delivery_graph_runs` row so the `delivery_graph_read_model` VIEW recomputes its `list_bucket`
7512
7625
  to 'history' (and `ack_open` to 0), dropping the finished run out of the Active delivery-graph
@@ -7556,6 +7669,8 @@ paths:
7556
7669
  /hooks/agent-complete:
7557
7670
  post:
7558
7671
  operationId: agentCompleteEscalation
7672
+ x-cli:
7673
+ exclude: true
7559
7674
  summary: "Complete an escalation user task as an agent assignee (ADR 0046). Reuses the same
7560
7675
  `.form` contract + engine resume path a human uses; records the agent identity for
7561
7676
  attribution. Optional shared-secret guard (x-hook-secret), enforced only when
@@ -7618,6 +7733,8 @@ paths:
7618
7733
  /hooks/revert-completion:
7619
7734
  post:
7620
7735
  operationId: revertEscalationCompletion
7736
+ x-cli:
7737
+ exclude: true
7621
7738
  summary: "Revert/override an agent escalation completion (ADR 0046 reversibility). A human marks
7622
7739
  a reversible agent completion reverted so the agent's answer is no longer authoritative.
7623
7740
  Optional shared-secret guard (x-hook-secret), enforced only when NANO_PR_WEBHOOK_SECRET is
@@ -7677,6 +7794,8 @@ paths:
7677
7794
  /hooks/blackboard:
7678
7795
  get:
7679
7796
  operationId: readBlackboard
7797
+ x-cli:
7798
+ exclude: true
7680
7799
  summary: Read a plan's blackboard, scoped by the capability token (Tier 1). Optional ?since=<id>
7681
7800
  returns only newer entries.
7682
7801
  parameters:
@@ -7717,6 +7836,8 @@ paths:
7717
7836
  $ref: "#/components/schemas/ErrorBody"
7718
7837
  post:
7719
7838
  operationId: appendBlackboard
7839
+ x-cli:
7840
+ exclude: true
7720
7841
  summary: Append one entry to a plan's blackboard, scoped by the capability token (Tier 1).
7721
7842
  Idempotent on (plan, dedupe_key).
7722
7843
  parameters:
@@ -7791,6 +7912,8 @@ paths:
7791
7912
  /hooks/abandon:
7792
7913
  get:
7793
7914
  operationId: checkAbandon
7915
+ x-cli:
7916
+ exclude: true
7794
7917
  summary: "Cooperative abandon check (issue #76): tell a running agent whether its run was cancelled.
7795
7918
  Scoped by the per-PR capability token."
7796
7919
  parameters:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanobpm/nano-workforce",
3
- "version": "0.210.1",
3
+ "version": "0.211.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",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@nanobpm/agentic": "^0.16.0",
69
- "@nanobpm/urban": "^0.98.0",
69
+ "@nanobpm/urban": "^0.99.0",
70
70
  "bpmn-auto-layout": "^2.0.0-alpha.2"
71
71
  },
72
72
  "devDependencies": {