@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/CHANGELOG.md +12 -0
- package/app/abandon.test.ts +31 -0
- package/app/abandon.ts +8 -2
- package/app/cliExtension.test.ts +40 -0
- package/app/conformance.test.ts +17 -0
- package/app/loopTermination.test.ts +4 -0
- package/app/migration139.test.ts +115 -0
- package/app/prTrackingReadModel.test.ts +175 -0
- package/app/prTrackingReadModel.ts +152 -0
- package/app/retro.test.ts +11 -0
- package/app/service.ts +11 -3
- package/app/terminalReaderBehaviour.test.ts +44 -0
- package/build-info.json +4 -4
- package/db/migrations/139_pull_requests_backfill_merged_not_abandoned.sql +40 -0
- package/main.ts +6 -0
- package/openapi.yaml +123 -0
- package/package.json +2 -2
- package/test/trackingViews.ts +16 -12
- package/workers/reconverge/worker.test.ts +137 -8
- package/workers/reconverge/worker.ts +53 -3
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
|
-
|
|
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.
|
|
3
|
-
"gitSha": "
|
|
4
|
-
"gitTag": "v0.
|
|
5
|
-
"builtAt": "2026-11-
|
|
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.
|
|
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.
|
|
69
|
+
"@nanobpm/urban": "^0.99.0",
|
|
70
70
|
"bpmn-auto-layout": "^2.0.0-alpha.2"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|