@nanobpm/nano-workforce 0.176.0 → 0.177.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.
@@ -3,11 +3,14 @@
3
3
  // worker list — family, host, current jobs, liveness — grouped by leaf token, sourced from the H1
4
4
  // presence registry (#144). Read-only projection; it NEVER gates control flow (advisory-only, ADR 0056).
5
5
  //
6
- // H6 (#149) closes the loop: the correlation registry (`app/agentic/correlation.ts`) supplies the
7
- // `jobKeysFor` resolver the presence snapshot exposes as a seam, so each worker's current jobKeys light
8
- // up; each worker's drill `stream` is repointed at its jobKey-scoped relay stream (`job:<jobKey>`); and
9
- // the report carries the `correlations` the process-instance / plan context for every current job —
10
- // so the cockpit lines a worker's terminal up with "that process instance / this plan".
6
+ // H6/#713 closes the loop with an EXPLICIT claim registry (`app/agentic/claim-registry.ts`): it is the
7
+ // AUTHORITATIVE source the presence snapshot's `jobKeysFor` seam resolves against, so each worker's
8
+ // current jobKeys light up from `claim` frames not inferred from the relay terminal and appear even
9
+ // with ZERO transcript. Each worker's drill `stream` is repointed at its claimed jobKey-scoped relay
10
+ // stream (`job:<jobKey>`), keyed by the CLAIM (explicit instance+jobKey), not by a connection. The
11
+ // relay correlation registry is DEMOTED to drill-in context only: it still supplies the `correlations`
12
+ // — the process-instance / plan context for a job's terminal — so the cockpit lines a worker's terminal
13
+ // up with "that process instance / this plan", but it is no longer the visibility source.
11
14
  //
12
15
  // This is the supply half of the visibility plane only. The demand×supply matrix, missing-agent-type
13
16
  // reds, and diversity-SLO lights are DE-SCOPED to the enrolment epic #152 — this report carries no
@@ -16,7 +19,8 @@
16
19
  // The optional shared-secret guard stays HERE (the runtime does not enforce OpenAPI `security`): when
17
20
  // NANO_PR_WEBHOOK_SECRET is set, callers must present it via the x-hook-secret header. Unset → open.
18
21
 
19
- import { type CorrelationRegistry, currentCorrelation, type JobCorrelation } from "../app/agentic/correlation.ts";
22
+ import { type ClaimRegistry, currentClaimRegistry } from "../app/agentic/claim-registry.ts";
23
+ import { currentCorrelation, type JobCorrelation } from "../app/agentic/correlation.ts";
20
24
  import { currentPresenceRegistry, type SupplyWorker } from "../app/agentic/families/presence.family.ts";
21
25
  import { envVar } from "../app/version.ts";
22
26
  import type { AgenticJobCorrelation, AgenticSupplyReport, AgenticSupplyWorker } from "../nano-generated/api-io.d.ts";
@@ -27,13 +31,14 @@ import { defineOperation } from "../nano-generated/operations.ts";
27
31
  const SECRET = envVar("NANO_PR_WEBHOOK_SECRET") ?? "";
28
32
 
29
33
  // Project a presence-registry row to the wire worker. The drill `stream` defaults to the worker
30
- // instance (H5) but is repointed at the worker's jobKey-scoped relay stream (`job:<jobKey>`) when the
31
- // correlation registry knows a current job for it (H6) — so drilling in opens the LIVE job's terminal.
32
- function toWorker(w: SupplyWorker, correlation: CorrelationRegistry | undefined): AgenticSupplyWorker {
34
+ // instance (H5) but is repointed at the worker's claimed jobKey-scoped relay stream (`job:<jobKey>`)
35
+ // when the claim registry knows a current claim for it (#713) — keyed by the CLAIM, not by the
36
+ // connection so drilling in opens the LIVE job's terminal even before any transcript lands.
37
+ function toWorker(w: SupplyWorker, claims: ClaimRegistry | undefined): AgenticSupplyWorker {
33
38
  const out: AgenticSupplyWorker = {
34
39
  instance: w.instance,
35
40
  identity: w.identity,
36
- stream: correlation?.primaryStreamFor(w.instance) ?? w.instance,
41
+ stream: claims?.primaryStreamFor(w.instance) ?? w.instance,
37
42
  jobKeys: [...w.jobKeys],
38
43
  live: w.live,
39
44
  staleMs: w.staleMs,
@@ -67,15 +72,19 @@ export default defineOperation("getAgenticSupply", async ({ req }, app) => {
67
72
  return { status: 200, body: empty };
68
73
  }
69
74
 
70
- // Thread the H6 correlation registry (if mounted) as the presence snapshot's jobKeysFor resolver so a
71
- // worker's current jobKeys populate; absent jobKeys stay empty (advisory, never an error).
75
+ // #713: the CLAIM registry (if mounted) is the authoritative `jobKeysFor` source the presence
76
+ // snapshot resolves against — a worker's current jobKeys come from explicit `claim` frames, not the
77
+ // relay terminal, so they populate with zero transcript. Absent → jobKeys stay empty (advisory).
78
+ const claims = currentClaimRegistry();
79
+ // The correlation registry is demoted to drill-in context only: it still carries the per-job
80
+ // process-instance / plan context surfaced in `correlations`, but no longer feeds visibility.
72
81
  const correlation = currentCorrelation();
73
- const snapshot = registry.snapshot(correlation ? { jobKeysFor: (instance) => correlation.jobKeysFor(instance) } : {});
82
+ const snapshot = registry.snapshot(claims ? { jobKeysFor: (instance) => claims.jobKeysFor(instance) } : {});
74
83
  const report: AgenticSupplyReport = {
75
84
  count: snapshot.count,
76
85
  generatedAt: new Date().toISOString(),
77
- workers: snapshot.workers.map((w) => toWorker(w, correlation)),
78
- leaves: snapshot.leaves.map((leaf) => ({ token: leaf.token, workers: leaf.workers.map((w) => toWorker(w, correlation)) })),
86
+ workers: snapshot.workers.map((w) => toWorker(w, claims)),
87
+ leaves: snapshot.leaves.map((leaf) => ({ token: leaf.token, workers: leaf.workers.map((w) => toWorker(w, claims)) })),
79
88
  correlations: correlation ? correlation.snapshot().correlations.map(toCorrelation) : [],
80
89
  };
81
90
  return { status: 200, body: report };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanobpm/nano-workforce",
3
- "version": "0.176.0",
3
+ "version": "0.177.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",
@@ -62,7 +62,7 @@
62
62
  "lint:fix": "biome check --write app operations workers pages components scripts e2e main.ts"
63
63
  },
64
64
  "dependencies": {
65
- "@nanobpm/agentic": "^0.10.0",
65
+ "@nanobpm/agentic": "^0.11.0",
66
66
  "@nanobpm/urban": "^0.90.0",
67
67
  "bpmn-auto-layout": "^2.0.0-alpha.2"
68
68
  },
@@ -154,6 +154,7 @@ test("E2E: the whole visibility plane wires up — presence, correlation, supply
154
154
  assert(names.includes("presence"), "H1 presence family is discovered by the H0 seam");
155
155
  assert(names.includes("relay"), "H3 relay family is discovered by the H0 seam");
156
156
  assert(names.includes("correlation"), "H6 correlation family is discovered by the H0 seam");
157
+ assert(names.includes("claim"), "#713 claim family is discovered by the H0 seam");
157
158
 
158
159
  // ── H1: a worker connects and REGISTERs; a live presence row appears with its family/host. ──
159
160
  const worker = conn("wk-conn-1", "leafA");
@@ -162,7 +163,14 @@ test("E2E: the whole visibility plane wires up — presence, correlation, supply
162
163
  worker.feed({ lane: "control", family: "register", seq: 1, payload: { instance: "wk-a", capability: { family: "opus", host: "boxA" } } });
163
164
  await flush();
164
165
 
165
- // ── H6: the orchestrator links the worker's active jobKey to its process instance / plan. ──
166
+ // ── #713: the worker CLAIMs its active jobKey the authoritative visibility source (explicit
167
+ // instance, no relay/connection inference). This is what lights up jobKeys, even before any
168
+ // transcript lands. ──
169
+ worker.feed({ lane: "control", family: "claim", seq: 2, payload: { instance: "wk-a", jobKey: JOB } });
170
+ await flush();
171
+
172
+ // ── H6: the orchestrator links the worker's active jobKey to its process instance / plan (drill-in
173
+ // context only, since #713 — no longer the visibility source). ──
166
174
  const correlation = currentCorrelation();
167
175
  assert(correlation !== undefined, "the correlation family installed the singleton");
168
176
  correlation.link("wk-a", JOB, { processInstanceKey: "4612", bpmnProcessId: "plan-fanout", elementId: "implement-task", planKey: "nanobpm/nano-workforce#142" });
@@ -179,8 +187,8 @@ test("E2E: the whole visibility plane wires up — presence, correlation, supply
179
187
  assertEquals(w.instance, "wk-a");
180
188
  assertEquals(w.family, "opus");
181
189
  assertEquals(w.host, "boxA");
182
- assertEquals(w.jobKeys, [JOB], "H1×H6: the correlation registry feeds the presence jobKeys seam");
183
- assertEquals(w.stream, STREAM, "H6: the drill stream repoints at the live job's stream");
190
+ assertEquals(w.jobKeys, [JOB], "#713: the claim registry feeds the presence jobKeys seam");
191
+ assertEquals(w.stream, STREAM, "#713: the drill stream repoints at the claimed job's stream");
184
192
  assertEquals(res.body.correlations.length, 1);
185
193
  const c = res.body.correlations[0];
186
194
  assertEquals(c.jobKey, JOB);