@nanobpm/nano-workforce 0.72.0 → 0.73.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/AGENTS.md +28 -1
- package/CHANGELOG.md +14 -0
- package/README.md +9 -8
- package/SPEC.md +22 -17
- package/app/agentGuide.ts +4 -3
- package/app/feature.ts +3 -3
- package/app/plan.ts +5 -3
- package/app/service.ts +2 -1
- package/app/taskDelta.ts +1 -1
- package/{resources → docs}/agent-guide.md +19 -10
- package/e2e/agent-answerable.e2e.ts +2 -1
- package/e2e/feature-run.e2e.ts +5 -4
- package/e2e/support/engine-client.ts +17 -0
- package/nano.app.json +0 -9
- package/package.json +3 -3
- package/pages/tasks.page.json +228 -172
- package/scripts/check-agent-prompts.test.ts +38 -5
- package/scripts/check-agent-prompts.ts +69 -29
- package/workers/record-dependency/worker.ts +1 -1
- package/workers/record-feature/worker.ts +1 -1
- package/workers/record-wave/worker.ts +1 -1
- /package/{prompts → resources/prompts}/feature.md +0 -0
- /package/{prompts → resources/prompts}/fix-ci.md +0 -0
- /package/{prompts → resources/prompts}/plan-review.md +0 -0
- /package/{prompts → resources/prompts}/plan.md +0 -0
- /package/{prompts → resources/prompts}/rebase.md +0 -0
- /package/{prompts → resources/prompts}/retro.md +0 -0
- /package/{prompts → resources/prompts}/review-round.md +0 -0
- /package/{prompts → resources/prompts}/trial-merge.md +0 -0
package/pages/tasks.page.json
CHANGED
|
@@ -7,28 +7,48 @@
|
|
|
7
7
|
"id": "nav",
|
|
8
8
|
"props": {
|
|
9
9
|
"variant": "bar",
|
|
10
|
-
"sticky": true,
|
|
11
10
|
"title": "Nano Workforce",
|
|
12
11
|
"items": [
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{
|
|
18
|
-
|
|
12
|
+
{
|
|
13
|
+
"label": "Overview",
|
|
14
|
+
"page": "overview"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"label": "Convergence",
|
|
18
|
+
"page": "home"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"label": "Epics",
|
|
22
|
+
"page": "epic"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"label": "Feature",
|
|
26
|
+
"page": "feature"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"label": "Tasks",
|
|
30
|
+
"page": "tasks"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"label": "Cockpit",
|
|
34
|
+
"page": "cockpit"
|
|
35
|
+
}
|
|
19
36
|
]
|
|
20
37
|
}
|
|
21
38
|
},
|
|
22
39
|
{
|
|
23
40
|
"type": "text",
|
|
24
41
|
"id": "title",
|
|
25
|
-
"props": {
|
|
42
|
+
"props": {
|
|
43
|
+
"text": "Tasks",
|
|
44
|
+
"variant": "heading"
|
|
45
|
+
}
|
|
26
46
|
},
|
|
27
47
|
{
|
|
28
48
|
"type": "text",
|
|
29
49
|
"id": "subtitle",
|
|
30
50
|
"props": {
|
|
31
|
-
"text": "Open native user-task escalations awaiting a human decision
|
|
51
|
+
"text": "Open native user-task escalations awaiting a human decision — across feature runs, epics, and PR review loops. Each row parks a process on your decision; submit one and the process resumes on the same canonical path Urban's task inbox uses. Rows disappear once completed (here, via the inbox, or out-of-band).",
|
|
32
52
|
"variant": "sub"
|
|
33
53
|
}
|
|
34
54
|
},
|
|
@@ -37,57 +57,56 @@
|
|
|
37
57
|
"id": "feature-escalations",
|
|
38
58
|
"props": {
|
|
39
59
|
"title": "Feature escalations",
|
|
40
|
-
"collapsible": true,
|
|
41
|
-
"defaultCollapsed": false,
|
|
42
|
-
"showCount": true,
|
|
43
|
-
"rowKey": "user_task_key",
|
|
44
|
-
"refreshMs": 5000,
|
|
45
60
|
"data": {
|
|
46
61
|
"kind": "datasource",
|
|
47
62
|
"source": "app",
|
|
48
63
|
"table": "user_tasks",
|
|
49
|
-
"
|
|
50
|
-
|
|
64
|
+
"filter": [
|
|
65
|
+
{
|
|
66
|
+
"field": "element_id",
|
|
67
|
+
"in": [
|
|
68
|
+
"feature-escalation"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"orderBy": {
|
|
73
|
+
"field": "updated_at",
|
|
74
|
+
"dir": "desc"
|
|
75
|
+
}
|
|
51
76
|
},
|
|
52
77
|
"columns": [
|
|
53
|
-
{ "field": "subject_key", "header": "Feature", "link": { "kind": "page", "page": "feature", "keyField": "subject_key" } },
|
|
54
|
-
{ "field": "question", "header": "Question" },
|
|
55
|
-
{ "field": "subject_url", "header": "Issue", "linkField": "subject_url" },
|
|
56
|
-
{ "field": "process_key", "header": "Process", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
57
|
-
{ "field": "updated_at", "header": "Updated" }
|
|
58
|
-
],
|
|
59
|
-
"rowActions": [
|
|
60
78
|
{
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
79
|
+
"field": "subject_key",
|
|
80
|
+
"header": "Feature"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"field": "subject_url",
|
|
84
|
+
"header": "Issue"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"field": "process_key",
|
|
88
|
+
"header": "Process",
|
|
89
|
+
"link": {
|
|
90
|
+
"kind": "processExplorer",
|
|
91
|
+
"keyField": "process_key"
|
|
67
92
|
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"field": "updated_at",
|
|
96
|
+
"header": "Updated"
|
|
68
97
|
}
|
|
69
98
|
],
|
|
99
|
+
"rowKey": "user_task_key",
|
|
70
100
|
"detail": {
|
|
71
101
|
"linkField": "subject_url",
|
|
72
|
-
"fields": [
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"promptField": "question",
|
|
77
|
-
"inputKey": "answer",
|
|
78
|
-
"inputLabel": "Guidance for the implementation agent",
|
|
79
|
-
"submitLabel": "Answer & re-dispatch",
|
|
80
|
-
"action": {
|
|
81
|
-
"path": "/app/api/actions/answer-escalation",
|
|
82
|
-
"successLabel": "Answered \u2014 the agent will resume",
|
|
83
|
-
"body": {
|
|
84
|
-
"userTaskKey": "{{row.user_task_key}}",
|
|
85
|
-
"resolution": "answer",
|
|
86
|
-
"answer": "{{form.answer}}"
|
|
87
|
-
}
|
|
102
|
+
"fields": [
|
|
103
|
+
{
|
|
104
|
+
"field": "question",
|
|
105
|
+
"label": "Escalation question"
|
|
88
106
|
}
|
|
89
|
-
|
|
90
|
-
}
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
"refreshMs": 5000
|
|
91
110
|
}
|
|
92
111
|
},
|
|
93
112
|
{
|
|
@@ -95,56 +114,60 @@
|
|
|
95
114
|
"id": "plan-reviews",
|
|
96
115
|
"props": {
|
|
97
116
|
"title": "Plan-review decisions",
|
|
98
|
-
"collapsible": true,
|
|
99
|
-
"defaultCollapsed": false,
|
|
100
|
-
"showCount": true,
|
|
101
|
-
"rowKey": "user_task_key",
|
|
102
|
-
"refreshMs": 5000,
|
|
103
117
|
"data": {
|
|
104
118
|
"kind": "datasource",
|
|
105
119
|
"source": "app",
|
|
106
120
|
"table": "user_tasks",
|
|
107
|
-
"
|
|
108
|
-
|
|
121
|
+
"filter": [
|
|
122
|
+
{
|
|
123
|
+
"field": "element_id",
|
|
124
|
+
"in": [
|
|
125
|
+
"plan-review-decision"
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"orderBy": {
|
|
130
|
+
"field": "updated_at",
|
|
131
|
+
"dir": "desc"
|
|
132
|
+
}
|
|
109
133
|
},
|
|
110
134
|
"columns": [
|
|
111
|
-
{ "field": "subject_key", "header": "Epic", "link": { "kind": "page", "page": "epic-detail", "keyField": "subject_key" } },
|
|
112
|
-
{ "field": "question", "header": "Findings" },
|
|
113
|
-
{ "field": "subject_url", "header": "Issue", "linkField": "subject_url" },
|
|
114
|
-
{ "field": "process_key", "header": "Process", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
115
|
-
{ "field": "updated_at", "header": "Updated" }
|
|
116
|
-
],
|
|
117
|
-
"rowActions": [
|
|
118
135
|
{
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
"field": "subject_key",
|
|
137
|
+
"header": "Epic"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"field": "question",
|
|
141
|
+
"header": "Findings"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"field": "subject_url",
|
|
145
|
+
"header": "Issue"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"field": "process_key",
|
|
149
|
+
"header": "Process",
|
|
150
|
+
"link": {
|
|
151
|
+
"kind": "processExplorer",
|
|
152
|
+
"keyField": "process_key"
|
|
125
153
|
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"field": "updated_at",
|
|
157
|
+
"header": "Updated"
|
|
126
158
|
}
|
|
127
159
|
],
|
|
160
|
+
"rowKey": "user_task_key",
|
|
128
161
|
"detail": {
|
|
129
162
|
"linkField": "subject_url",
|
|
130
|
-
"fields": [
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"promptField": "question",
|
|
135
|
-
"inputKey": "notes",
|
|
136
|
-
"inputLabel": "Revision guidance for the planner",
|
|
137
|
-
"submitLabel": "Revise \u2014 re-plan",
|
|
138
|
-
"action": {
|
|
139
|
-
"path": "/app/api/actions/complete-user-task",
|
|
140
|
-
"successLabel": "Sent back \u2014 the planner will revise",
|
|
141
|
-
"body": {
|
|
142
|
-
"userTaskKey": "{{row.user_task_key}}",
|
|
143
|
-
"variables": { "directive": "revise", "notes": "{{form.notes}}" }
|
|
144
|
-
}
|
|
163
|
+
"fields": [
|
|
164
|
+
{
|
|
165
|
+
"field": "question",
|
|
166
|
+
"label": "Review findings"
|
|
145
167
|
}
|
|
146
|
-
|
|
147
|
-
}
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
"refreshMs": 5000
|
|
148
171
|
}
|
|
149
172
|
},
|
|
150
173
|
{
|
|
@@ -152,58 +175,60 @@
|
|
|
152
175
|
"id": "trial-merges",
|
|
153
176
|
"props": {
|
|
154
177
|
"title": "Trial-merge decisions",
|
|
155
|
-
"collapsible": true,
|
|
156
|
-
"defaultCollapsed": false,
|
|
157
|
-
"showCount": true,
|
|
158
|
-
"rowKey": "user_task_key",
|
|
159
|
-
"refreshMs": 5000,
|
|
160
178
|
"data": {
|
|
161
179
|
"kind": "datasource",
|
|
162
180
|
"source": "app",
|
|
163
181
|
"table": "user_tasks",
|
|
164
|
-
"
|
|
165
|
-
|
|
182
|
+
"filter": [
|
|
183
|
+
{
|
|
184
|
+
"field": "element_id",
|
|
185
|
+
"in": [
|
|
186
|
+
"trial-merge-decision"
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
],
|
|
190
|
+
"orderBy": {
|
|
191
|
+
"field": "updated_at",
|
|
192
|
+
"dir": "desc"
|
|
193
|
+
}
|
|
166
194
|
},
|
|
167
195
|
"columns": [
|
|
168
|
-
{ "field": "subject_key", "header": "Epic", "link": { "kind": "page", "page": "epic-detail", "keyField": "subject_key" } },
|
|
169
|
-
{ "field": "question", "header": "Trial merge" },
|
|
170
|
-
{ "field": "subject_url", "header": "Issue", "linkField": "subject_url" },
|
|
171
|
-
{ "field": "process_key", "header": "Process", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
172
|
-
{ "field": "updated_at", "header": "Updated" }
|
|
173
|
-
],
|
|
174
|
-
"rowActions": [
|
|
175
196
|
{
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"showWhenField": "user_task_key",
|
|
179
|
-
"action": {
|
|
180
|
-
"path": "/app/api/actions/complete-user-task",
|
|
181
|
-
"body": { "userTaskKey": "{{row.user_task_key}}", "variables": { "action": "proceed" } }
|
|
182
|
-
}
|
|
197
|
+
"field": "subject_key",
|
|
198
|
+
"header": "Epic"
|
|
183
199
|
},
|
|
184
200
|
{
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
"showWhenField": "user_task_key",
|
|
188
|
-
"action": {
|
|
189
|
-
"path": "/app/api/actions/complete-user-task",
|
|
190
|
-
"body": { "userTaskKey": "{{row.user_task_key}}", "variables": { "action": "rebase" } }
|
|
191
|
-
}
|
|
201
|
+
"field": "question",
|
|
202
|
+
"header": "Trial merge"
|
|
192
203
|
},
|
|
193
204
|
{
|
|
194
|
-
"
|
|
195
|
-
"
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
205
|
+
"field": "subject_url",
|
|
206
|
+
"header": "Issue"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"field": "process_key",
|
|
210
|
+
"header": "Process",
|
|
211
|
+
"link": {
|
|
212
|
+
"kind": "processExplorer",
|
|
213
|
+
"keyField": "process_key"
|
|
200
214
|
}
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"field": "updated_at",
|
|
218
|
+
"header": "Updated"
|
|
201
219
|
}
|
|
202
220
|
],
|
|
221
|
+
"rowKey": "user_task_key",
|
|
203
222
|
"detail": {
|
|
204
223
|
"linkField": "subject_url",
|
|
205
|
-
"fields": [
|
|
206
|
-
|
|
224
|
+
"fields": [
|
|
225
|
+
{
|
|
226
|
+
"field": "question",
|
|
227
|
+
"label": "Trial merge"
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
"refreshMs": 5000
|
|
207
232
|
}
|
|
208
233
|
},
|
|
209
234
|
{
|
|
@@ -211,45 +236,56 @@
|
|
|
211
236
|
"id": "pr-reviews",
|
|
212
237
|
"props": {
|
|
213
238
|
"title": "PR review escalations",
|
|
214
|
-
"collapsible": true,
|
|
215
|
-
"defaultCollapsed": false,
|
|
216
|
-
"showCount": true,
|
|
217
|
-
"rowKey": "user_task_key",
|
|
218
|
-
"refreshMs": 5000,
|
|
219
239
|
"data": {
|
|
220
240
|
"kind": "datasource",
|
|
221
241
|
"source": "app",
|
|
222
242
|
"table": "user_tasks",
|
|
223
|
-
"
|
|
224
|
-
|
|
243
|
+
"filter": [
|
|
244
|
+
{
|
|
245
|
+
"field": "element_id",
|
|
246
|
+
"in": [
|
|
247
|
+
"wait-answer"
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
],
|
|
251
|
+
"orderBy": {
|
|
252
|
+
"field": "updated_at",
|
|
253
|
+
"dir": "desc"
|
|
254
|
+
}
|
|
225
255
|
},
|
|
226
256
|
"columns": [
|
|
227
|
-
{
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
{
|
|
257
|
+
{
|
|
258
|
+
"field": "subject_key",
|
|
259
|
+
"header": "PR"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"field": "subject_url",
|
|
263
|
+
"header": "PR link"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"field": "process_key",
|
|
267
|
+
"header": "Process",
|
|
268
|
+
"link": {
|
|
269
|
+
"kind": "processExplorer",
|
|
270
|
+
"keyField": "process_key"
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"field": "updated_at",
|
|
275
|
+
"header": "Updated"
|
|
276
|
+
}
|
|
232
277
|
],
|
|
278
|
+
"rowKey": "user_task_key",
|
|
233
279
|
"detail": {
|
|
234
280
|
"linkField": "subject_url",
|
|
235
|
-
"fields": [
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
"promptField": "question",
|
|
240
|
-
"inputKey": "answer",
|
|
241
|
-
"inputLabel": "Your answer (resumes the review loop, handed to the next round)",
|
|
242
|
-
"submitLabel": "Answer \u2014 resume review",
|
|
243
|
-
"action": {
|
|
244
|
-
"path": "/app/api/actions/complete-user-task",
|
|
245
|
-
"successLabel": "Answered \u2014 the review loop will resume",
|
|
246
|
-
"body": {
|
|
247
|
-
"userTaskKey": "{{row.user_task_key}}",
|
|
248
|
-
"variables": { "answer": "{{form.answer}}" }
|
|
249
|
-
}
|
|
281
|
+
"fields": [
|
|
282
|
+
{
|
|
283
|
+
"field": "question",
|
|
284
|
+
"label": "Escalation question"
|
|
250
285
|
}
|
|
251
|
-
|
|
252
|
-
}
|
|
286
|
+
]
|
|
287
|
+
},
|
|
288
|
+
"refreshMs": 5000
|
|
253
289
|
}
|
|
254
290
|
},
|
|
255
291
|
{
|
|
@@ -257,41 +293,61 @@
|
|
|
257
293
|
"id": "blocked-runs",
|
|
258
294
|
"props": {
|
|
259
295
|
"title": "Blocked feature runs",
|
|
260
|
-
"collapsible": true,
|
|
261
|
-
"defaultCollapsed": false,
|
|
262
|
-
"showCount": true,
|
|
263
|
-
"rowKey": "user_task_key",
|
|
264
|
-
"refreshMs": 5000,
|
|
265
296
|
"data": {
|
|
266
297
|
"kind": "datasource",
|
|
267
298
|
"source": "app",
|
|
268
299
|
"table": "user_tasks",
|
|
269
|
-
"
|
|
270
|
-
|
|
300
|
+
"filter": [
|
|
301
|
+
{
|
|
302
|
+
"field": "element_id",
|
|
303
|
+
"in": [
|
|
304
|
+
"feature-blocked"
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
],
|
|
308
|
+
"orderBy": {
|
|
309
|
+
"field": "updated_at",
|
|
310
|
+
"dir": "desc"
|
|
311
|
+
}
|
|
271
312
|
},
|
|
272
313
|
"columns": [
|
|
273
|
-
{ "field": "subject_key", "header": "Feature", "link": { "kind": "page", "page": "feature", "keyField": "subject_key" } },
|
|
274
|
-
{ "field": "question", "header": "Disposition" },
|
|
275
|
-
{ "field": "subject_url", "header": "Issue", "linkField": "subject_url" },
|
|
276
|
-
{ "field": "process_key", "header": "Process", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
277
|
-
{ "field": "updated_at", "header": "Updated" }
|
|
278
|
-
],
|
|
279
|
-
"rowActions": [
|
|
280
314
|
{
|
|
281
|
-
"
|
|
282
|
-
"
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
315
|
+
"field": "subject_key",
|
|
316
|
+
"header": "Feature"
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"field": "question",
|
|
320
|
+
"header": "Disposition"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"field": "subject_url",
|
|
324
|
+
"header": "Issue"
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"field": "process_key",
|
|
328
|
+
"header": "Process",
|
|
329
|
+
"link": {
|
|
330
|
+
"kind": "processExplorer",
|
|
331
|
+
"keyField": "process_key"
|
|
287
332
|
}
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"field": "updated_at",
|
|
336
|
+
"header": "Updated"
|
|
288
337
|
}
|
|
289
338
|
],
|
|
339
|
+
"rowKey": "user_task_key",
|
|
290
340
|
"detail": {
|
|
291
341
|
"linkField": "subject_url",
|
|
292
|
-
"fields": [
|
|
293
|
-
|
|
342
|
+
"fields": [
|
|
343
|
+
{
|
|
344
|
+
"field": "question",
|
|
345
|
+
"label": "Disposition"
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
},
|
|
349
|
+
"refreshMs": 5000
|
|
294
350
|
}
|
|
295
351
|
}
|
|
296
352
|
]
|
|
297
|
-
}
|
|
353
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// Red/green coverage for the agent-prompt deploy guard (scripts/check-agent-prompts.ts).
|
|
2
2
|
//
|
|
3
|
-
// Since #169 each agent's base prompt is a linked *resource*: `prompts/<token>.md` is
|
|
4
|
-
//
|
|
5
|
-
// `<zeebe:linkedResource resourceId="<token>.md" bindingType="latest" linkName="prompt"/>`.
|
|
3
|
+
// Since #169 each agent's base prompt is a linked *resource*: `resources/prompts/<token>.md` is
|
|
4
|
+
// deployed as a generic resource and each service task links it with
|
|
5
|
+
// `<zeebe:linkedResource resourceId="<token>.md" bindingType="latest" linkName="prompt"/>`. Under
|
|
6
|
+
// ADR 0062 the app declares no `models`, so the guard discovers deployables by the `resources/`
|
|
7
|
+
// convention walk; a manifest with explicit `models` globs is still honoured as an override. The
|
|
6
8
|
// engine silently OMITS an unresolvable link — a typo'd or undeployed `resourceId` yields a blank
|
|
7
9
|
// base prompt at runtime (the prompt-less-agent root of the empty "(no question provided)"
|
|
8
10
|
// escalations, Magikcraft/nano-bpm #597/#599). These cases assert the guard fails on each broken
|
|
@@ -14,12 +16,16 @@ import { tmpdir } from "node:os";
|
|
|
14
16
|
import { dirname, join } from "node:path";
|
|
15
17
|
import { checkAgentPrompts } from "./check-agent-prompts.ts";
|
|
16
18
|
|
|
17
|
-
// A manifest that deploys BPMN and the prompt resources
|
|
18
|
-
//
|
|
19
|
+
// A manifest that deploys BPMN and the prompt resources via an explicit `models` override (the
|
|
20
|
+
// escape hatch — exercised here to prove overrides still work alongside the ADR 0062 convention).
|
|
19
21
|
const MANIFEST = JSON.stringify({
|
|
20
22
|
models: { processes: ["resources/processes/*.bpmn", "prompts/*.md"] },
|
|
21
23
|
});
|
|
22
24
|
|
|
25
|
+
// The blessed ADR 0062 shape: NO `models` block, so deployables are discovered by the `resources/`
|
|
26
|
+
// convention walk (prompts live at `resources/prompts/*.md`).
|
|
27
|
+
const MANIFEST_CONVENTION = JSON.stringify({ id: "app" });
|
|
28
|
+
|
|
23
29
|
function link(resourceId: string, bindingType = "latest"): string {
|
|
24
30
|
return `<zeebe:linkedResource resourceId="${resourceId}" bindingType="${bindingType}" linkName="prompt" />`;
|
|
25
31
|
}
|
|
@@ -51,6 +57,33 @@ test("passes when every prompt link resolves to a deployed, non-blank, result-em
|
|
|
51
57
|
assertEquals(res.resolved, ["review-round"]);
|
|
52
58
|
});
|
|
53
59
|
|
|
60
|
+
test("discovers prompts by the resources/ convention when the manifest declares no models", () => {
|
|
61
|
+
const root = fixture({
|
|
62
|
+
"nano.app.json": MANIFEST_CONVENTION,
|
|
63
|
+
"resources/processes/loop.bpmn": serviceTask(link("review-round.md")),
|
|
64
|
+
"resources/prompts/review-round.md": "# Round\nDo the thing, then write `$AGENT_RESULT_FILE`.",
|
|
65
|
+
// Docs live OUTSIDE resources/ and must never be swept into the deploy set.
|
|
66
|
+
"docs/agent-guide.md": "# Guide\nnot a deployable",
|
|
67
|
+
});
|
|
68
|
+
const res = checkAgentPrompts(root);
|
|
69
|
+
assertEquals(res.errors, []);
|
|
70
|
+
assert(res.ok);
|
|
71
|
+
assertEquals(res.resolved, ["review-round"]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("convention walk flags a prompt link that has no deployed resource under resources/", () => {
|
|
75
|
+
const root = fixture({
|
|
76
|
+
"nano.app.json": MANIFEST_CONVENTION,
|
|
77
|
+
"resources/processes/loop.bpmn": serviceTask(link("review-round.md")),
|
|
78
|
+
// The prompt lives OUTSIDE resources/ (old top-level prompts/ layout) so convention never
|
|
79
|
+
// deploys it — the link would resolve to nothing at runtime.
|
|
80
|
+
"prompts/review-round.md": "# Round\nWrite `$AGENT_RESULT_FILE`.",
|
|
81
|
+
});
|
|
82
|
+
const res = checkAgentPrompts(root);
|
|
83
|
+
assert(!res.ok);
|
|
84
|
+
assert(res.errors.some((e) => e.includes("no deployed resource has that name")));
|
|
85
|
+
});
|
|
86
|
+
|
|
54
87
|
test("fails when a prompt link references a resourceId with no deployed file", () => {
|
|
55
88
|
const root = fixture({
|
|
56
89
|
"nano.app.json": MANIFEST,
|