@nanobpm/nano-workforce 0.37.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.
- package/CHANGELOG.md +7 -0
- package/README.md +5 -6
- package/SPEC.md +15 -13
- package/app/abandon.test.ts +4 -4
- package/app/abandon.ts +2 -2
- package/app/blackboard.test.ts +3 -3
- package/app/blackboard.ts +2 -2
- package/app/persist-escalation.test.ts +1 -1
- package/app/persist-round.test.ts +1 -1
- package/app/service.ts +1 -1
- package/biome.json +0 -2
- package/main.ts +3 -3
- package/nano.app.json +2 -25
- package/openapi.yaml +649 -0
- package/operations/answerFeatureEscalation.test.ts +100 -0
- package/operations/answerFeatureEscalation.ts +49 -0
- package/operations/appendBlackboard.ts +61 -0
- package/{actions → operations}/blackboard.test.ts +16 -10
- package/{actions/abandon.test.ts → operations/checkAbandon.test.ts} +5 -11
- package/{actions/abandon.ts → operations/checkAbandon.ts} +8 -7
- package/operations/listActivePrs.ts +1 -1
- package/operations/postMessage.ts +1 -1
- package/operations/readBlackboard.ts +29 -0
- package/operations/startAndMessage.test.ts +5 -3
- package/operations/startConvergenceLoop.ts +17 -11
- package/operations/startPlanFanout.ts +13 -7
- package/package.json +4 -4
- package/pages/epic.page.json +1 -1
- package/pages/home.page.json +1 -1
- package/workers/persist-task-escalation/worker.ts +1 -1
- package/actions/blackboard.ts +0 -77
- package/actions/feature-answer-hook.ts +0 -45
- package/actions/plan-hook.ts +0 -20
- package/actions/webhook-submit.ts +0 -22
- package/openapi.json +0 -248
package/openapi.yaml
ADDED
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
# Nano Workforce control API — OpenAPI (authoring source, ADR 0059).
|
|
2
|
+
#
|
|
3
|
+
# This YAML is the single source of truth for the app HTTP surface. The Urban toolkit
|
|
4
|
+
# derives typed request/response contracts + runtime validators from it (urban gen), and
|
|
5
|
+
# each operationId is implemented by a delegate in operations/<operationId>.ts. Mounted
|
|
6
|
+
# under /app/api (off the framework-reserved /app page-runtime namespace). Swagger UI is
|
|
7
|
+
# served at /app/api-docs. Prefer editing this file over the generated artifacts.
|
|
8
|
+
openapi: 3.0.3
|
|
9
|
+
info:
|
|
10
|
+
title: Nano Workforce control API
|
|
11
|
+
version: 1.0.0
|
|
12
|
+
description: "The externally-facing control surface an operator, an automation harness, or an LLM
|
|
13
|
+
uses to observe and steer PR-convergence and planning runs. Contract-first (ADR 0058/0059): the
|
|
14
|
+
toolkit derives typed request/response contracts + runtime validators from this document and
|
|
15
|
+
each `operationId` is implemented by a delegate module in `operations/`. Mounted under base
|
|
16
|
+
`/app/api`, kept off the framework-reserved `/app` page-runtime namespace (which owns
|
|
17
|
+
`/app/runtime.js`, `/app/pages/*`, `/app/data/*`). The webhook endpoints are ordinary operations
|
|
18
|
+
too (ADR 0059 — the `actions[]` array is retired): their `/hooks/*` paths in this document are
|
|
19
|
+
relative to the `/app/api` base, so they are served at `/app/api/hooks/*`. Shared-secret and
|
|
20
|
+
capability-token side-channels enforce their credential in the delegate."
|
|
21
|
+
components:
|
|
22
|
+
securitySchemes:
|
|
23
|
+
hookSecret:
|
|
24
|
+
type: apiKey
|
|
25
|
+
in: header
|
|
26
|
+
name: x-hook-secret
|
|
27
|
+
description: Optional shared secret. Enforced by the delegate (NOT the runtime) only when
|
|
28
|
+
NANO_PR_WEBHOOK_SECRET is set; unset means the endpoint is open. Declared here for
|
|
29
|
+
documentation.
|
|
30
|
+
schemas:
|
|
31
|
+
ErrorBody:
|
|
32
|
+
type: object
|
|
33
|
+
required:
|
|
34
|
+
- error
|
|
35
|
+
properties:
|
|
36
|
+
error:
|
|
37
|
+
type: string
|
|
38
|
+
ActivePr:
|
|
39
|
+
type: object
|
|
40
|
+
description: A tracked PR that is not in a terminal (converged/abandoned) state.
|
|
41
|
+
required:
|
|
42
|
+
- prKey
|
|
43
|
+
- repo
|
|
44
|
+
- number
|
|
45
|
+
- url
|
|
46
|
+
- title
|
|
47
|
+
- status
|
|
48
|
+
- round
|
|
49
|
+
- processKey
|
|
50
|
+
- waitingSince
|
|
51
|
+
- openEscalation
|
|
52
|
+
- updatedAt
|
|
53
|
+
- activeWorker
|
|
54
|
+
- leaseUntil
|
|
55
|
+
properties:
|
|
56
|
+
prKey:
|
|
57
|
+
type: string
|
|
58
|
+
repo:
|
|
59
|
+
type: string
|
|
60
|
+
number:
|
|
61
|
+
type: integer
|
|
62
|
+
url:
|
|
63
|
+
type: string
|
|
64
|
+
title:
|
|
65
|
+
type:
|
|
66
|
+
- string
|
|
67
|
+
- "null"
|
|
68
|
+
status:
|
|
69
|
+
type: string
|
|
70
|
+
round:
|
|
71
|
+
type: integer
|
|
72
|
+
processKey:
|
|
73
|
+
type:
|
|
74
|
+
- string
|
|
75
|
+
- "null"
|
|
76
|
+
description: The engine process instance key; also the keyField the pages processExplorer link uses.
|
|
77
|
+
waitingSince:
|
|
78
|
+
type:
|
|
79
|
+
- string
|
|
80
|
+
- "null"
|
|
81
|
+
openEscalation:
|
|
82
|
+
type:
|
|
83
|
+
- string
|
|
84
|
+
- "null"
|
|
85
|
+
updatedAt:
|
|
86
|
+
type: string
|
|
87
|
+
activeWorker:
|
|
88
|
+
type:
|
|
89
|
+
- string
|
|
90
|
+
- "null"
|
|
91
|
+
leaseUntil:
|
|
92
|
+
type:
|
|
93
|
+
- string
|
|
94
|
+
- "null"
|
|
95
|
+
ActivePrList:
|
|
96
|
+
type: object
|
|
97
|
+
required:
|
|
98
|
+
- count
|
|
99
|
+
- prs
|
|
100
|
+
properties:
|
|
101
|
+
count:
|
|
102
|
+
type: integer
|
|
103
|
+
prs:
|
|
104
|
+
type: array
|
|
105
|
+
items:
|
|
106
|
+
$ref: "#/components/schemas/ActivePr"
|
|
107
|
+
VersionInfo:
|
|
108
|
+
type: object
|
|
109
|
+
description: The running app's identity (which code is actually live).
|
|
110
|
+
required:
|
|
111
|
+
- name
|
|
112
|
+
- version
|
|
113
|
+
- urbanVersion
|
|
114
|
+
- gitSha
|
|
115
|
+
- gitBranch
|
|
116
|
+
- runtime
|
|
117
|
+
- pid
|
|
118
|
+
- startedAt
|
|
119
|
+
- uptimeSeconds
|
|
120
|
+
properties:
|
|
121
|
+
name:
|
|
122
|
+
type: string
|
|
123
|
+
version:
|
|
124
|
+
type:
|
|
125
|
+
- string
|
|
126
|
+
- "null"
|
|
127
|
+
urbanVersion:
|
|
128
|
+
type:
|
|
129
|
+
- string
|
|
130
|
+
- "null"
|
|
131
|
+
gitSha:
|
|
132
|
+
type:
|
|
133
|
+
- string
|
|
134
|
+
- "null"
|
|
135
|
+
gitBranch:
|
|
136
|
+
type:
|
|
137
|
+
- string
|
|
138
|
+
- "null"
|
|
139
|
+
runtime:
|
|
140
|
+
type: string
|
|
141
|
+
pid:
|
|
142
|
+
type:
|
|
143
|
+
- integer
|
|
144
|
+
- "null"
|
|
145
|
+
startedAt:
|
|
146
|
+
type: string
|
|
147
|
+
uptimeSeconds:
|
|
148
|
+
type: integer
|
|
149
|
+
SubmitResult:
|
|
150
|
+
type: object
|
|
151
|
+
required:
|
|
152
|
+
- prKey
|
|
153
|
+
properties:
|
|
154
|
+
prKey:
|
|
155
|
+
type: string
|
|
156
|
+
processKey:
|
|
157
|
+
type:
|
|
158
|
+
- string
|
|
159
|
+
- "null"
|
|
160
|
+
description: The started convergence-loop instance key (null if the engine did not return one).
|
|
161
|
+
alreadyRunning:
|
|
162
|
+
type: boolean
|
|
163
|
+
description: True when a non-terminal convergence loop for this PR already exists; the aggregate was
|
|
164
|
+
refreshed and no new instance was started.
|
|
165
|
+
StartPlanResult:
|
|
166
|
+
type: object
|
|
167
|
+
required:
|
|
168
|
+
- planKey
|
|
169
|
+
properties:
|
|
170
|
+
planKey:
|
|
171
|
+
type: string
|
|
172
|
+
processKey:
|
|
173
|
+
type:
|
|
174
|
+
- string
|
|
175
|
+
- "null"
|
|
176
|
+
alreadyRunning:
|
|
177
|
+
type: boolean
|
|
178
|
+
description: True when a non-terminal plan for this issue already exists; no new instance was started.
|
|
179
|
+
StartVariables:
|
|
180
|
+
type: object
|
|
181
|
+
description: The start request body. `pr`/`url` (convergence) or `issue`/`url` (planning) name the
|
|
182
|
+
target. Only the fields below are read by the delegate; any extra keys are accepted but
|
|
183
|
+
ignored (they are NOT forwarded to the engine as process variables).
|
|
184
|
+
additionalProperties: true
|
|
185
|
+
properties:
|
|
186
|
+
pr:
|
|
187
|
+
type: string
|
|
188
|
+
description: "PR reference: owner/repo#123 or a PR URL."
|
|
189
|
+
issue:
|
|
190
|
+
type: string
|
|
191
|
+
description: "Issue reference: owner/repo#123 or an issue URL."
|
|
192
|
+
url:
|
|
193
|
+
type: string
|
|
194
|
+
description: Alias for pr/issue when a bare URL is supplied.
|
|
195
|
+
dependsOn:
|
|
196
|
+
type: array
|
|
197
|
+
items:
|
|
198
|
+
type: string
|
|
199
|
+
maxRounds:
|
|
200
|
+
type: integer
|
|
201
|
+
minimum: 1
|
|
202
|
+
description: Values above 100 are accepted and clamped to 100 by the delegate.
|
|
203
|
+
MessageResult:
|
|
204
|
+
type: object
|
|
205
|
+
description: The result of publishing a message / answering an escalation. Shape varies by message
|
|
206
|
+
name; `ok` is always present.
|
|
207
|
+
additionalProperties: true
|
|
208
|
+
required:
|
|
209
|
+
- ok
|
|
210
|
+
properties:
|
|
211
|
+
ok:
|
|
212
|
+
type: boolean
|
|
213
|
+
FeatureAnswerRequest:
|
|
214
|
+
type: object
|
|
215
|
+
description: "Answer an implementation-phase task escalation (issue #25). Supply either `corrKey`,
|
|
216
|
+
or both `plan` and `task`; `answer` is always required."
|
|
217
|
+
required:
|
|
218
|
+
- answer
|
|
219
|
+
properties:
|
|
220
|
+
corrKey:
|
|
221
|
+
type: string
|
|
222
|
+
description: The task correlation key, `<plan_key>:<task_id>` (e.g. owner/repo#12:task-3).
|
|
223
|
+
plan:
|
|
224
|
+
type: string
|
|
225
|
+
description: Plan reference (owner/repo#N); combined with `task` to derive the corrKey.
|
|
226
|
+
task:
|
|
227
|
+
type: string
|
|
228
|
+
description: Task id within the plan; combined with `plan` to derive the corrKey.
|
|
229
|
+
answer:
|
|
230
|
+
type: string
|
|
231
|
+
minLength: 1
|
|
232
|
+
description: The operator's answer that resumes the parked implementation agent.
|
|
233
|
+
BlackboardEntry:
|
|
234
|
+
type: object
|
|
235
|
+
required:
|
|
236
|
+
- id
|
|
237
|
+
- author_task
|
|
238
|
+
- kind
|
|
239
|
+
- files
|
|
240
|
+
- body
|
|
241
|
+
- wave
|
|
242
|
+
- created_at
|
|
243
|
+
properties:
|
|
244
|
+
id:
|
|
245
|
+
type: integer
|
|
246
|
+
author_task:
|
|
247
|
+
type: string
|
|
248
|
+
kind:
|
|
249
|
+
type: string
|
|
250
|
+
files:
|
|
251
|
+
type: array
|
|
252
|
+
items:
|
|
253
|
+
type: string
|
|
254
|
+
body:
|
|
255
|
+
type: string
|
|
256
|
+
wave:
|
|
257
|
+
type:
|
|
258
|
+
- integer
|
|
259
|
+
- "null"
|
|
260
|
+
created_at:
|
|
261
|
+
type: string
|
|
262
|
+
BlackboardPage:
|
|
263
|
+
type: object
|
|
264
|
+
required:
|
|
265
|
+
- planKey
|
|
266
|
+
- entries
|
|
267
|
+
- cursor
|
|
268
|
+
properties:
|
|
269
|
+
planKey:
|
|
270
|
+
type: string
|
|
271
|
+
entries:
|
|
272
|
+
type: array
|
|
273
|
+
items:
|
|
274
|
+
$ref: "#/components/schemas/BlackboardEntry"
|
|
275
|
+
cursor:
|
|
276
|
+
type:
|
|
277
|
+
- integer
|
|
278
|
+
- "null"
|
|
279
|
+
description: The plan's current head id; pass it back as `since` on the next poll (Tier 2).
|
|
280
|
+
BlackboardAppendRequest:
|
|
281
|
+
type: object
|
|
282
|
+
description: Append one blackboard entry. Idempotent on (plan, dedupe_key).
|
|
283
|
+
required:
|
|
284
|
+
- body
|
|
285
|
+
properties:
|
|
286
|
+
author_task:
|
|
287
|
+
type: string
|
|
288
|
+
description: The authoring task id; defaults to "system" when omitted/blank.
|
|
289
|
+
kind:
|
|
290
|
+
type: string
|
|
291
|
+
description: Entry kind (e.g. note, file-claim); normalized by the delegate.
|
|
292
|
+
files:
|
|
293
|
+
type: array
|
|
294
|
+
items:
|
|
295
|
+
type: string
|
|
296
|
+
description: Files this entry concerns (used by advisory file-claim conflict detection).
|
|
297
|
+
body:
|
|
298
|
+
type: string
|
|
299
|
+
minLength: 1
|
|
300
|
+
description: The note text (required, non-blank).
|
|
301
|
+
wave:
|
|
302
|
+
type: integer
|
|
303
|
+
description: Optional plan wave the entry belongs to.
|
|
304
|
+
dedupe_key:
|
|
305
|
+
type: string
|
|
306
|
+
description: "Idempotency key: a retry with the same (plan, dedupe_key) returns the existing entry."
|
|
307
|
+
BlackboardAppendResult:
|
|
308
|
+
type: object
|
|
309
|
+
required:
|
|
310
|
+
- id
|
|
311
|
+
- inserted
|
|
312
|
+
- conflicts
|
|
313
|
+
properties:
|
|
314
|
+
id:
|
|
315
|
+
type: integer
|
|
316
|
+
inserted:
|
|
317
|
+
type: boolean
|
|
318
|
+
description: False when a prior entry with the same dedupe_key already existed (idempotent retry).
|
|
319
|
+
conflicts:
|
|
320
|
+
type: array
|
|
321
|
+
description: Prior sibling file-claims on the same file(s) (advisory first-writer-wins; never a lock).
|
|
322
|
+
items:
|
|
323
|
+
type: object
|
|
324
|
+
required:
|
|
325
|
+
- file
|
|
326
|
+
- author_task
|
|
327
|
+
- id
|
|
328
|
+
- body
|
|
329
|
+
- created_at
|
|
330
|
+
properties:
|
|
331
|
+
file:
|
|
332
|
+
type: string
|
|
333
|
+
author_task:
|
|
334
|
+
type: string
|
|
335
|
+
id:
|
|
336
|
+
type: integer
|
|
337
|
+
body:
|
|
338
|
+
type: string
|
|
339
|
+
created_at:
|
|
340
|
+
type: string
|
|
341
|
+
AbandonStatus:
|
|
342
|
+
type: object
|
|
343
|
+
required:
|
|
344
|
+
- prKey
|
|
345
|
+
- status
|
|
346
|
+
- abandoned
|
|
347
|
+
properties:
|
|
348
|
+
prKey:
|
|
349
|
+
type: string
|
|
350
|
+
status:
|
|
351
|
+
type: string
|
|
352
|
+
abandoned:
|
|
353
|
+
type: boolean
|
|
354
|
+
description: Derived from pull_requests.status; true ⇒ the run was cancelled and the agent must stop.
|
|
355
|
+
paths:
|
|
356
|
+
/status:
|
|
357
|
+
get:
|
|
358
|
+
operationId: listActivePrs
|
|
359
|
+
summary: List every tracked PR currently in flight (not converged/abandoned), newest-updated first.
|
|
360
|
+
security:
|
|
361
|
+
- hookSecret: []
|
|
362
|
+
- {}
|
|
363
|
+
responses:
|
|
364
|
+
"200":
|
|
365
|
+
description: The active PRs.
|
|
366
|
+
content:
|
|
367
|
+
application/json:
|
|
368
|
+
schema:
|
|
369
|
+
$ref: "#/components/schemas/ActivePrList"
|
|
370
|
+
"401":
|
|
371
|
+
description: Missing/invalid shared secret (only when NANO_PR_WEBHOOK_SECRET is set).
|
|
372
|
+
content:
|
|
373
|
+
application/json:
|
|
374
|
+
schema:
|
|
375
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
376
|
+
/version:
|
|
377
|
+
get:
|
|
378
|
+
operationId: getVersion
|
|
379
|
+
summary: The running app's identity (app/urban versions, git sha/branch, runtime, pid, uptime).
|
|
380
|
+
security:
|
|
381
|
+
- hookSecret: []
|
|
382
|
+
- {}
|
|
383
|
+
responses:
|
|
384
|
+
"200":
|
|
385
|
+
description: The version/identity payload.
|
|
386
|
+
content:
|
|
387
|
+
application/json:
|
|
388
|
+
schema:
|
|
389
|
+
$ref: "#/components/schemas/VersionInfo"
|
|
390
|
+
"401":
|
|
391
|
+
description: Missing/invalid shared secret (only when NANO_PR_WEBHOOK_SECRET is set).
|
|
392
|
+
content:
|
|
393
|
+
application/json:
|
|
394
|
+
schema:
|
|
395
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
396
|
+
/actions/start/convergence-loop:
|
|
397
|
+
post:
|
|
398
|
+
operationId: startConvergenceLoop
|
|
399
|
+
summary: Register/refresh a PR aggregate (idempotent on prKey) and start its convergence loop.
|
|
400
|
+
requestBody:
|
|
401
|
+
required: true
|
|
402
|
+
content:
|
|
403
|
+
application/json:
|
|
404
|
+
schema:
|
|
405
|
+
$ref: "#/components/schemas/StartVariables"
|
|
406
|
+
responses:
|
|
407
|
+
"202":
|
|
408
|
+
description: The loop was started (or refreshed).
|
|
409
|
+
content:
|
|
410
|
+
application/json:
|
|
411
|
+
schema:
|
|
412
|
+
$ref: "#/components/schemas/SubmitResult"
|
|
413
|
+
"400":
|
|
414
|
+
description: The PR reference could not be parsed.
|
|
415
|
+
content:
|
|
416
|
+
application/json:
|
|
417
|
+
schema:
|
|
418
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
419
|
+
/actions/start/plan-fanout:
|
|
420
|
+
post:
|
|
421
|
+
operationId: startPlanFanout
|
|
422
|
+
summary: Register/refresh a plan aggregate (idempotent on planKey) and start the planning fan-out.
|
|
423
|
+
requestBody:
|
|
424
|
+
required: true
|
|
425
|
+
content:
|
|
426
|
+
application/json:
|
|
427
|
+
schema:
|
|
428
|
+
$ref: "#/components/schemas/StartVariables"
|
|
429
|
+
responses:
|
|
430
|
+
"202":
|
|
431
|
+
description: The plan fan-out was started (or was already running).
|
|
432
|
+
content:
|
|
433
|
+
application/json:
|
|
434
|
+
schema:
|
|
435
|
+
$ref: "#/components/schemas/StartPlanResult"
|
|
436
|
+
"400":
|
|
437
|
+
description: The issue reference could not be parsed.
|
|
438
|
+
content:
|
|
439
|
+
application/json:
|
|
440
|
+
schema:
|
|
441
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
442
|
+
/actions/message:
|
|
443
|
+
post:
|
|
444
|
+
operationId: postMessage
|
|
445
|
+
summary: Publish a message / answer an escalation. For escalation-answered and
|
|
446
|
+
feature-escalation-answered names, runs the corresponding answer flow; otherwise a plain
|
|
447
|
+
publishMessage.
|
|
448
|
+
requestBody:
|
|
449
|
+
required: true
|
|
450
|
+
content:
|
|
451
|
+
application/json:
|
|
452
|
+
schema:
|
|
453
|
+
type: object
|
|
454
|
+
additionalProperties: true
|
|
455
|
+
required:
|
|
456
|
+
- name
|
|
457
|
+
properties:
|
|
458
|
+
name:
|
|
459
|
+
type: string
|
|
460
|
+
minLength: 1
|
|
461
|
+
description: The message name (correlates a waiting event).
|
|
462
|
+
correlationKey:
|
|
463
|
+
type: string
|
|
464
|
+
variables:
|
|
465
|
+
type: object
|
|
466
|
+
additionalProperties: true
|
|
467
|
+
properties:
|
|
468
|
+
answer:
|
|
469
|
+
type: string
|
|
470
|
+
responses:
|
|
471
|
+
"200":
|
|
472
|
+
description: The message was published (or the escalation answered).
|
|
473
|
+
content:
|
|
474
|
+
application/json:
|
|
475
|
+
schema:
|
|
476
|
+
$ref: "#/components/schemas/MessageResult"
|
|
477
|
+
"400":
|
|
478
|
+
description: A required field was missing/blank.
|
|
479
|
+
content:
|
|
480
|
+
application/json:
|
|
481
|
+
schema:
|
|
482
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
483
|
+
"404":
|
|
484
|
+
description: No matching open escalation / parked token to answer.
|
|
485
|
+
content:
|
|
486
|
+
application/json:
|
|
487
|
+
schema:
|
|
488
|
+
$ref: "#/components/schemas/MessageResult"
|
|
489
|
+
/hooks/feature-answer:
|
|
490
|
+
post:
|
|
491
|
+
operationId: answerFeatureEscalation
|
|
492
|
+
summary: "Answer an implementation-phase task escalation out of band (issue #25). Optional
|
|
493
|
+
shared-secret guard (x-hook-secret), enforced only when NANO_PR_WEBHOOK_SECRET is set."
|
|
494
|
+
security:
|
|
495
|
+
- hookSecret: []
|
|
496
|
+
- {}
|
|
497
|
+
requestBody:
|
|
498
|
+
required: true
|
|
499
|
+
content:
|
|
500
|
+
application/json:
|
|
501
|
+
schema:
|
|
502
|
+
$ref: "#/components/schemas/FeatureAnswerRequest"
|
|
503
|
+
responses:
|
|
504
|
+
"200":
|
|
505
|
+
description: The escalation was answered and the parked agent resumed.
|
|
506
|
+
content:
|
|
507
|
+
application/json:
|
|
508
|
+
schema:
|
|
509
|
+
$ref: "#/components/schemas/MessageResult"
|
|
510
|
+
"400":
|
|
511
|
+
description: A required field was missing (answer, or a resolvable correlation key).
|
|
512
|
+
content:
|
|
513
|
+
application/json:
|
|
514
|
+
schema:
|
|
515
|
+
$ref: "#/components/schemas/MessageResult"
|
|
516
|
+
"401":
|
|
517
|
+
description: Missing/invalid shared secret (only when NANO_PR_WEBHOOK_SECRET is set).
|
|
518
|
+
content:
|
|
519
|
+
application/json:
|
|
520
|
+
schema:
|
|
521
|
+
$ref: "#/components/schemas/MessageResult"
|
|
522
|
+
"404":
|
|
523
|
+
description: No matching open task escalation for the correlation key.
|
|
524
|
+
content:
|
|
525
|
+
application/json:
|
|
526
|
+
schema:
|
|
527
|
+
$ref: "#/components/schemas/MessageResult"
|
|
528
|
+
/hooks/blackboard:
|
|
529
|
+
get:
|
|
530
|
+
operationId: readBlackboard
|
|
531
|
+
summary: Read a plan's blackboard, scoped by the capability token (Tier 1). Optional ?since=<id>
|
|
532
|
+
returns only newer entries.
|
|
533
|
+
parameters:
|
|
534
|
+
- name: token
|
|
535
|
+
in: query
|
|
536
|
+
required: false
|
|
537
|
+
schema:
|
|
538
|
+
type: string
|
|
539
|
+
description: "Per-plan capability token (issues #51 / #49) — the token IS the credential and
|
|
540
|
+
scopes the read to exactly one plan. Required in practice, but declared optional here
|
|
541
|
+
because it may instead be sent as the `x-blackboard-token` header; the delegate enforces
|
|
542
|
+
it (400 when absent from both, 404 when unknown). Not an OpenAPI `security` scheme: it is
|
|
543
|
+
a per-plan DB-issued capability, not a fixed shared secret the runtime could verify."
|
|
544
|
+
- name: since
|
|
545
|
+
in: query
|
|
546
|
+
required: false
|
|
547
|
+
schema:
|
|
548
|
+
type: integer
|
|
549
|
+
description: Return only entries with id > since (incremental poll).
|
|
550
|
+
responses:
|
|
551
|
+
"200":
|
|
552
|
+
description: The blackboard page for this plan.
|
|
553
|
+
content:
|
|
554
|
+
application/json:
|
|
555
|
+
schema:
|
|
556
|
+
$ref: "#/components/schemas/BlackboardPage"
|
|
557
|
+
"400":
|
|
558
|
+
description: Missing blackboard token.
|
|
559
|
+
content:
|
|
560
|
+
application/json:
|
|
561
|
+
schema:
|
|
562
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
563
|
+
"404":
|
|
564
|
+
description: Unknown blackboard token (never reveals which plans exist).
|
|
565
|
+
content:
|
|
566
|
+
application/json:
|
|
567
|
+
schema:
|
|
568
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
569
|
+
post:
|
|
570
|
+
operationId: appendBlackboard
|
|
571
|
+
summary: Append one entry to a plan's blackboard, scoped by the capability token (Tier 1).
|
|
572
|
+
Idempotent on (plan, dedupe_key).
|
|
573
|
+
parameters:
|
|
574
|
+
- name: token
|
|
575
|
+
in: query
|
|
576
|
+
required: false
|
|
577
|
+
schema:
|
|
578
|
+
type: string
|
|
579
|
+
description: "Per-plan capability token (issues #51 / #49) — the token IS the credential and
|
|
580
|
+
scopes the write to exactly one plan. Required in practice, but declared optional here
|
|
581
|
+
because it may instead be sent as the `x-blackboard-token` header; the delegate enforces
|
|
582
|
+
it (400 when absent from both, 404 when unknown). Not an OpenAPI `security` scheme: it is
|
|
583
|
+
a per-plan DB-issued capability, not a fixed shared secret the runtime could verify."
|
|
584
|
+
requestBody:
|
|
585
|
+
required: true
|
|
586
|
+
content:
|
|
587
|
+
application/json:
|
|
588
|
+
schema:
|
|
589
|
+
$ref: "#/components/schemas/BlackboardAppendRequest"
|
|
590
|
+
responses:
|
|
591
|
+
"200":
|
|
592
|
+
description: "An idempotent retry: the entry already existed (inserted=false)."
|
|
593
|
+
content:
|
|
594
|
+
application/json:
|
|
595
|
+
schema:
|
|
596
|
+
$ref: "#/components/schemas/BlackboardAppendResult"
|
|
597
|
+
"201":
|
|
598
|
+
description: The entry was appended.
|
|
599
|
+
content:
|
|
600
|
+
application/json:
|
|
601
|
+
schema:
|
|
602
|
+
$ref: "#/components/schemas/BlackboardAppendResult"
|
|
603
|
+
"400":
|
|
604
|
+
description: Missing blackboard token or blank body.
|
|
605
|
+
content:
|
|
606
|
+
application/json:
|
|
607
|
+
schema:
|
|
608
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
609
|
+
"404":
|
|
610
|
+
description: Unknown blackboard token (never reveals which plans exist).
|
|
611
|
+
content:
|
|
612
|
+
application/json:
|
|
613
|
+
schema:
|
|
614
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
615
|
+
/hooks/abandon:
|
|
616
|
+
get:
|
|
617
|
+
operationId: checkAbandon
|
|
618
|
+
summary: "Cooperative abandon check (issue #76): tell a running agent whether its run was cancelled.
|
|
619
|
+
Scoped by the per-PR capability token."
|
|
620
|
+
parameters:
|
|
621
|
+
- name: token
|
|
622
|
+
in: query
|
|
623
|
+
required: false
|
|
624
|
+
schema:
|
|
625
|
+
type: string
|
|
626
|
+
description: "Per-PR capability token (issue #76) — the token IS the credential and scopes the
|
|
627
|
+
read to exactly one PR. Required in practice, but declared optional here because it may
|
|
628
|
+
instead be sent as the `x-abandon-token` header; the delegate enforces it (400 when absent
|
|
629
|
+
from both, 404 when unknown). Not an OpenAPI `security` scheme: it is a per-PR DB-issued
|
|
630
|
+
capability, not a fixed shared secret the runtime could verify."
|
|
631
|
+
responses:
|
|
632
|
+
"200":
|
|
633
|
+
description: The PR's abandon status.
|
|
634
|
+
content:
|
|
635
|
+
application/json:
|
|
636
|
+
schema:
|
|
637
|
+
$ref: "#/components/schemas/AbandonStatus"
|
|
638
|
+
"400":
|
|
639
|
+
description: Missing abandon token.
|
|
640
|
+
content:
|
|
641
|
+
application/json:
|
|
642
|
+
schema:
|
|
643
|
+
$ref: "#/components/schemas/ErrorBody"
|
|
644
|
+
"404":
|
|
645
|
+
description: Unknown abandon token (never reveals which PRs exist).
|
|
646
|
+
content:
|
|
647
|
+
application/json:
|
|
648
|
+
schema:
|
|
649
|
+
$ref: "#/components/schemas/ErrorBody"
|