@nanobpm/nano-workforce 0.36.0 → 0.38.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.
Files changed (75) hide show
  1. package/.github/workflows/ci.yml +7 -17
  2. package/AGENTS.md +8 -7
  3. package/CHANGELOG.md +14 -0
  4. package/README.md +6 -27
  5. package/SPEC.md +15 -13
  6. package/app/abandon.test.ts +14 -19
  7. package/app/abandon.ts +2 -2
  8. package/app/baseGuard.test.ts +7 -6
  9. package/app/blackboard.test.ts +25 -35
  10. package/app/blackboard.ts +2 -2
  11. package/app/ensure-pr.test.ts +10 -12
  12. package/app/github.test.ts +9 -8
  13. package/app/github.ts +1 -21
  14. package/app/instance-tracking.test.ts +8 -6
  15. package/app/mergeExclusion.test.ts +11 -20
  16. package/app/mergeProtocol.test.ts +14 -13
  17. package/app/mergeRebaseArm.test.ts +8 -6
  18. package/app/mergeTrain.test.ts +10 -9
  19. package/app/persist-escalation.test.ts +10 -31
  20. package/app/persist-round.test.ts +7 -20
  21. package/app/plan.test.ts +19 -42
  22. package/app/record-plan-review.test.ts +8 -7
  23. package/app/retro.test.ts +24 -48
  24. package/app/reviewWait.test.ts +12 -11
  25. package/app/rounds.test.ts +13 -12
  26. package/app/service.test.ts +14 -27
  27. package/app/service.ts +1 -1
  28. package/app/taskDelta.test.ts +8 -17
  29. package/app/trialMerge.test.ts +4 -3
  30. package/app/version.ts +6 -21
  31. package/app/waves.test.ts +17 -16
  32. package/biome.json +0 -2
  33. package/main.ts +3 -3
  34. package/nano.app.json +2 -25
  35. package/openapi.yaml +649 -0
  36. package/operations/answerFeatureEscalation.test.ts +100 -0
  37. package/operations/answerFeatureEscalation.ts +49 -0
  38. package/operations/appendBlackboard.ts +61 -0
  39. package/{actions → operations}/blackboard.test.ts +28 -30
  40. package/{actions/abandon.test.ts → operations/checkAbandon.test.ts} +12 -26
  41. package/{actions/abandon.ts → operations/checkAbandon.ts} +8 -7
  42. package/operations/getVersion.test.ts +8 -12
  43. package/operations/getVersion.ts +2 -3
  44. package/operations/listActivePrs.test.ts +8 -14
  45. package/operations/listActivePrs.ts +3 -4
  46. package/operations/postMessage.ts +1 -1
  47. package/operations/readBlackboard.ts +29 -0
  48. package/operations/startAndMessage.test.ts +11 -15
  49. package/operations/startConvergenceLoop.ts +17 -11
  50. package/operations/startPlanFanout.ts +13 -7
  51. package/package.json +9 -7
  52. package/pages/epic.page.json +1 -1
  53. package/pages/home.page.json +1 -1
  54. package/scripts/check-agent-prompts.test.ts +15 -11
  55. package/scripts/layout-bpmn.ts +6 -28
  56. package/scripts/pages-contract.test.ts +14 -13
  57. package/scripts/purge-db.ts +1 -1
  58. package/scripts/upgrade-from-pack.ts +1 -1
  59. package/test/assert.ts +74 -0
  60. package/tsconfig.json +4 -1
  61. package/workers/persist-task-escalation/worker.ts +1 -1
  62. package/workers/record-plan-review/worker.test.ts +6 -9
  63. package/workers/record-results/worker.test.ts +5 -8
  64. package/workers/record-trial-merge/worker.test.ts +7 -18
  65. package/workers/record-wave/worker.test.ts +13 -20
  66. package/workers/retro-gather/worker.test.ts +4 -17
  67. package/workers/retro-record/worker.test.ts +8 -27
  68. package/workers/select-wave/worker.test.ts +5 -8
  69. package/actions/blackboard.ts +0 -77
  70. package/actions/feature-answer-hook.ts +0 -45
  71. package/actions/plan-hook.ts +0 -20
  72. package/actions/webhook-submit.ts +0 -22
  73. package/deno.json +0 -24
  74. package/deno.lock +0 -1777
  75. package/openapi.json +0 -248
package/openapi.json DELETED
@@ -1,248 +0,0 @@
1
- {
2
- "openapi": "3.0.3",
3
- "info": {
4
- "title": "Nano Workforce control API",
5
- "version": "1.0.0",
6
- "description": "The externally-facing control surface an operator, an automation harness, or an LLM uses to observe and steer PR-convergence and planning runs. Contract-first (ADR 0058): the toolkit derives typed request/response contracts + runtime validators from this document and each `operationId` is implemented by a delegate module in `operations/`. Mounted under base `/app/api`, kept off the framework-reserved `/app` page-runtime namespace (which owns `/app/runtime.js`, `/app/pages/*`, `/app/data/*`). The `/hooks/*` webhook endpoints stay on `actions[]` because they live outside the `/app` namespace."
7
- },
8
- "components": {
9
- "securitySchemes": {
10
- "hookSecret": {
11
- "type": "apiKey",
12
- "in": "header",
13
- "name": "x-hook-secret",
14
- "description": "Optional shared secret. Enforced by the delegate (NOT the runtime) only when NANO_PR_WEBHOOK_SECRET is set; unset means the endpoint is open. Declared here for documentation."
15
- }
16
- },
17
- "schemas": {
18
- "ErrorBody": {
19
- "type": "object",
20
- "required": ["error"],
21
- "properties": { "error": { "type": "string" } }
22
- },
23
- "ActivePr": {
24
- "type": "object",
25
- "description": "A tracked PR that is not in a terminal (converged/abandoned) state.",
26
- "required": [
27
- "prKey", "repo", "number", "url", "title", "status", "round",
28
- "processKey", "waitingSince", "openEscalation", "updatedAt",
29
- "activeWorker", "leaseUntil"
30
- ],
31
- "properties": {
32
- "prKey": { "type": "string" },
33
- "repo": { "type": "string" },
34
- "number": { "type": "integer" },
35
- "url": { "type": "string" },
36
- "title": { "type": ["string", "null"] },
37
- "status": { "type": "string" },
38
- "round": { "type": "integer" },
39
- "processKey": { "type": ["string", "null"], "description": "The engine process instance key; also the keyField the pages processExplorer link uses." },
40
- "waitingSince": { "type": ["string", "null"] },
41
- "openEscalation": { "type": ["string", "null"] },
42
- "updatedAt": { "type": "string" },
43
- "activeWorker": { "type": ["string", "null"] },
44
- "leaseUntil": { "type": ["string", "null"] }
45
- }
46
- },
47
- "ActivePrList": {
48
- "type": "object",
49
- "required": ["count", "prs"],
50
- "properties": {
51
- "count": { "type": "integer" },
52
- "prs": { "type": "array", "items": { "$ref": "#/components/schemas/ActivePr" } }
53
- }
54
- },
55
- "VersionInfo": {
56
- "type": "object",
57
- "description": "The running app's identity (which code is actually live).",
58
- "required": [
59
- "name", "version", "urbanVersion", "gitSha", "gitBranch",
60
- "runtime", "pid", "startedAt", "uptimeSeconds"
61
- ],
62
- "properties": {
63
- "name": { "type": "string" },
64
- "version": { "type": ["string", "null"] },
65
- "urbanVersion": { "type": ["string", "null"] },
66
- "gitSha": { "type": ["string", "null"] },
67
- "gitBranch": { "type": ["string", "null"] },
68
- "runtime": { "type": "string" },
69
- "pid": { "type": ["integer", "null"] },
70
- "startedAt": { "type": "string" },
71
- "uptimeSeconds": { "type": "integer" }
72
- }
73
- },
74
- "SubmitResult": {
75
- "type": "object",
76
- "required": ["prKey"],
77
- "properties": {
78
- "prKey": { "type": "string" },
79
- "processKey": { "type": ["string", "null"], "description": "The started convergence-loop instance key (null if the engine did not return one)." },
80
- "alreadyRunning": { "type": "boolean", "description": "True when a non-terminal convergence loop for this PR already exists; the aggregate was refreshed and no new instance was started." }
81
- }
82
- },
83
- "StartPlanResult": {
84
- "type": "object",
85
- "required": ["planKey"],
86
- "properties": {
87
- "planKey": { "type": "string" },
88
- "processKey": { "type": ["string", "null"] },
89
- "alreadyRunning": { "type": "boolean", "description": "True when a non-terminal plan for this issue already exists; no new instance was started." }
90
- }
91
- },
92
- "StartVariables": {
93
- "type": "object",
94
- "description": "Process-start variables. `pr`/`url` (convergence) or `issue`/`url` (planning) name the target; extra keys are forwarded to the engine.",
95
- "additionalProperties": true,
96
- "properties": {
97
- "pr": { "type": "string", "description": "PR reference: owner/repo#123 or a PR URL." },
98
- "issue": { "type": "string", "description": "Issue reference: owner/repo#123 or an issue URL." },
99
- "url": { "type": "string", "description": "Alias for pr/issue when a bare URL is supplied." },
100
- "dependsOn": { "type": "array", "items": { "type": "string" } },
101
- "maxRounds": { "type": "integer", "minimum": 1 }
102
- }
103
- },
104
- "MessageResult": {
105
- "type": "object",
106
- "description": "The result of publishing a message / answering an escalation. Shape varies by message name; `ok` is always present.",
107
- "additionalProperties": true,
108
- "required": ["ok"],
109
- "properties": { "ok": { "type": "boolean" } }
110
- }
111
- }
112
- },
113
- "paths": {
114
- "/status": {
115
- "get": {
116
- "operationId": "listActivePrs",
117
- "summary": "List every tracked PR currently in flight (not converged/abandoned), newest-updated first.",
118
- "security": [{ "hookSecret": [] }, {}],
119
- "responses": {
120
- "200": {
121
- "description": "The active PRs.",
122
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ActivePrList" } } }
123
- },
124
- "401": {
125
- "description": "Missing/invalid shared secret (only when NANO_PR_WEBHOOK_SECRET is set).",
126
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorBody" } } }
127
- }
128
- }
129
- }
130
- },
131
- "/version": {
132
- "get": {
133
- "operationId": "getVersion",
134
- "summary": "The running app's identity (app/urban versions, git sha/branch, runtime, pid, uptime).",
135
- "security": [{ "hookSecret": [] }, {}],
136
- "responses": {
137
- "200": {
138
- "description": "The version/identity payload.",
139
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VersionInfo" } } }
140
- },
141
- "401": {
142
- "description": "Missing/invalid shared secret (only when NANO_PR_WEBHOOK_SECRET is set).",
143
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorBody" } } }
144
- }
145
- }
146
- }
147
- },
148
- "/actions/start/convergence-loop": {
149
- "post": {
150
- "operationId": "startConvergenceLoop",
151
- "summary": "Register/refresh a PR aggregate (idempotent on prKey) and start its convergence loop.",
152
- "requestBody": {
153
- "required": true,
154
- "content": {
155
- "application/json": {
156
- "schema": {
157
- "type": "object",
158
- "additionalProperties": true,
159
- "required": ["variables"],
160
- "properties": { "variables": { "$ref": "#/components/schemas/StartVariables" } }
161
- }
162
- }
163
- }
164
- },
165
- "responses": {
166
- "202": {
167
- "description": "The loop was started (or refreshed).",
168
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubmitResult" } } }
169
- },
170
- "400": {
171
- "description": "The PR reference could not be parsed.",
172
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorBody" } } }
173
- }
174
- }
175
- }
176
- },
177
- "/actions/start/plan-fanout": {
178
- "post": {
179
- "operationId": "startPlanFanout",
180
- "summary": "Register/refresh a plan aggregate (idempotent on planKey) and start the planning fan-out.",
181
- "requestBody": {
182
- "required": true,
183
- "content": {
184
- "application/json": {
185
- "schema": {
186
- "type": "object",
187
- "additionalProperties": true,
188
- "required": ["variables"],
189
- "properties": { "variables": { "$ref": "#/components/schemas/StartVariables" } }
190
- }
191
- }
192
- }
193
- },
194
- "responses": {
195
- "202": {
196
- "description": "The plan fan-out was started (or was already running).",
197
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StartPlanResult" } } }
198
- },
199
- "400": {
200
- "description": "The issue reference could not be parsed.",
201
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorBody" } } }
202
- }
203
- }
204
- }
205
- },
206
- "/actions/message": {
207
- "post": {
208
- "operationId": "postMessage",
209
- "summary": "Publish a message / answer an escalation. For escalation-answered and feature-escalation-answered names, runs the corresponding answer flow; otherwise a plain publishMessage.",
210
- "requestBody": {
211
- "required": true,
212
- "content": {
213
- "application/json": {
214
- "schema": {
215
- "type": "object",
216
- "additionalProperties": true,
217
- "required": ["name"],
218
- "properties": {
219
- "name": { "type": "string", "description": "The message name (correlates a waiting event)." },
220
- "correlationKey": { "type": "string" },
221
- "variables": {
222
- "type": "object",
223
- "additionalProperties": true,
224
- "properties": { "answer": { "type": "string" } }
225
- }
226
- }
227
- }
228
- }
229
- }
230
- },
231
- "responses": {
232
- "200": {
233
- "description": "The message was published (or the escalation answered).",
234
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MessageResult" } } }
235
- },
236
- "400": {
237
- "description": "A required field was missing/blank.",
238
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorBody" } } }
239
- },
240
- "404": {
241
- "description": "No matching open escalation / parked token to answer.",
242
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MessageResult" } } }
243
- }
244
- }
245
- }
246
- }
247
- }
248
- }