@yemi33/minions 0.1.2424 → 0.1.2425

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 (182) hide show
  1. package/bin/install-internal-minions.js +1209 -0
  2. package/bin/minions.js +75 -9
  3. package/dashboard/docs/typography.md +27 -12
  4. package/dashboard/js/command-center.js +13 -11
  5. package/dashboard/js/live-stream.js +1 -1
  6. package/dashboard/js/memory-search.js +388 -43
  7. package/dashboard/js/modal-qa.js +5 -5
  8. package/dashboard/js/qa.js +39 -26
  9. package/dashboard/js/refresh.js +37 -18
  10. package/dashboard/js/render-agents.js +14 -18
  11. package/dashboard/js/render-dispatch.js +14 -74
  12. package/dashboard/js/render-inbox.js +3 -3
  13. package/dashboard/js/render-meetings.js +8 -8
  14. package/dashboard/js/render-other.js +45 -20
  15. package/dashboard/js/render-pipelines.js +6 -6
  16. package/dashboard/js/render-plans.js +14 -14
  17. package/dashboard/js/render-prd.js +46 -46
  18. package/dashboard/js/render-prs.js +9 -65
  19. package/dashboard/js/render-schedules.js +5 -5
  20. package/dashboard/js/render-skills.js +6 -6
  21. package/dashboard/js/render-utils.js +4 -4
  22. package/dashboard/js/render-watches.js +4 -16
  23. package/dashboard/js/render-work-items.js +95 -107
  24. package/dashboard/js/settings.js +175 -74
  25. package/dashboard/layout.html +3 -2
  26. package/dashboard/pages/inbox.html +1 -1
  27. package/dashboard/pages/work.html +1 -1
  28. package/dashboard/shared/model-display.js +9 -0
  29. package/dashboard/shared/watches-source.js +43 -0
  30. package/dashboard/shared/welcome-popup.js +259 -0
  31. package/dashboard/slim/body.html +6 -6
  32. package/dashboard/slim/js/chat.js +4 -2
  33. package/dashboard/slim/js/history.js +10 -3
  34. package/dashboard/slim/js/modals-tiles.js +85 -16
  35. package/dashboard/slim/js/status.js +12 -16
  36. package/dashboard/slim/styles.css +24 -4
  37. package/dashboard/styles.css +62 -27
  38. package/dashboard-build.js +8 -2
  39. package/dashboard.js +3854 -2514
  40. package/docs/README.md +6 -4
  41. package/docs/api-errors.md +144 -0
  42. package/docs/auto-discovery.md +83 -61
  43. package/docs/capture-demos.js +173 -30
  44. package/docs/command-center.md +5 -1
  45. package/docs/completion-reports.md +127 -11
  46. package/docs/constants.md +15 -3
  47. package/docs/constellation-style-telemetry.md +2 -2
  48. package/docs/copilot-cli-schema.md +32 -22
  49. package/docs/cross-repo-plans.md +30 -30
  50. package/docs/demo/memory-system.html +1 -1
  51. package/docs/deprecated.json +132 -18
  52. package/docs/diagnostics-memory.md +8 -4
  53. package/docs/human-vs-automated.md +2 -2
  54. package/docs/internal-install.md +190 -0
  55. package/docs/kb-pr3223-cascade-archiving.md +16 -0
  56. package/docs/kb-pr696-merge-conflict-docs.md +23 -0
  57. package/docs/kb-sweep.md +2 -2
  58. package/docs/keep-processes.md +8 -1
  59. package/docs/live-checkout-mode.md +1 -1
  60. package/docs/managed-spawn.md +21 -3
  61. package/docs/named-agents.md +3 -2
  62. package/docs/plan-lifecycle.md +32 -32
  63. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
  64. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
  65. package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
  66. package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
  67. package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
  68. package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
  69. package/docs/qa-runbook-lifecycle.md +67 -51
  70. package/docs/qa-runbooks.md +2 -2
  71. package/docs/runtime-adapters.md +101 -50
  72. package/docs/security.md +28 -0
  73. package/docs/self-improvement.md +47 -13
  74. package/docs/skills.md +29 -0
  75. package/docs/slim-ux/concepts.md +2 -2
  76. package/docs/specs/agent-configurability.md +43 -41
  77. package/docs/specs/agent-rename.md +13 -13
  78. package/docs/team-memory.md +283 -14
  79. package/docs/tutorials/03-plan-driven-feature.md +3 -3
  80. package/docs/tutorials/04-runtimes-and-harness.md +18 -21
  81. package/docs/tutorials/08-operations-and-recovery.md +3 -2
  82. package/docs/tutorials/README.md +4 -3
  83. package/docs/visual-evidence-ci.md +103 -0
  84. package/docs/watches.md +35 -30
  85. package/docs/worktree-lifecycle.md +151 -13
  86. package/engine/acp-transport.js +1 -0
  87. package/engine/ado.js +80 -33
  88. package/engine/agent-api-validation.js +571 -0
  89. package/engine/agent-worker-pool.js +7 -6
  90. package/engine/api-contracts/agent-content.js +761 -0
  91. package/engine/api-contracts/cc-ops.js +219 -0
  92. package/engine/api-contracts/config-runtime.js +419 -0
  93. package/engine/api-contracts/core.js +138 -0
  94. package/engine/api-contracts/index.js +506 -0
  95. package/engine/api-contracts/orchestration.js +602 -0
  96. package/engine/api-contracts/pull-requests.js +323 -0
  97. package/engine/api-contracts/qa-process.js +764 -0
  98. package/engine/api-contracts/work-plan-prd.js +494 -0
  99. package/engine/api-validation.js +543 -0
  100. package/engine/cc-api-validation.js +586 -0
  101. package/engine/cc-worker-pool.js +9 -1
  102. package/engine/cleanup.js +393 -271
  103. package/engine/cli.js +65 -7
  104. package/engine/comment-classifier.js +70 -5
  105. package/engine/consolidation.js +123 -5
  106. package/engine/content-api-validation.js +606 -0
  107. package/engine/create-pr-worktree.js +22 -1
  108. package/engine/db/migrations/027-review-learning-lifecycle.js +162 -0
  109. package/engine/dispatch.js +33 -0
  110. package/engine/distribution.js +189 -0
  111. package/engine/execution-model.js +59 -0
  112. package/engine/features.js +12 -0
  113. package/engine/github.js +67 -25
  114. package/engine/harness-context.js +313 -0
  115. package/engine/inbox-store.js +1 -1
  116. package/engine/kb-sweep.js +161 -38
  117. package/engine/keep-process-sweep.js +43 -3
  118. package/engine/lifecycle.js +1066 -165
  119. package/engine/llm.js +4 -0
  120. package/engine/managed-spawn.js +463 -48
  121. package/engine/meeting.js +197 -34
  122. package/engine/memory-retrieval.js +280 -0
  123. package/engine/memory-store.js +821 -38
  124. package/engine/model-discovery.js +17 -3
  125. package/engine/pipeline.js +802 -14
  126. package/engine/plan-prd-validation.js +745 -0
  127. package/engine/playbook.js +206 -17
  128. package/engine/pooled-agent-process.js +6 -1
  129. package/engine/pr-action.js +1 -1
  130. package/engine/pr-issue-validation.js +653 -0
  131. package/engine/pr-resolve.js +75 -13
  132. package/engine/prd-store.js +39 -6
  133. package/engine/preflight.js +7 -2
  134. package/engine/process-utils.js +79 -31
  135. package/engine/projects.js +198 -12
  136. package/engine/promotion.js +371 -0
  137. package/engine/qa-from-prd.js +3 -0
  138. package/engine/qa-process-validation.js +572 -0
  139. package/engine/qa-runbooks.js +41 -37
  140. package/engine/qa-runners/maestro.js +11 -7
  141. package/engine/qa-runners/playwright.js +37 -14
  142. package/engine/qa-runners.js +9 -10
  143. package/engine/qa-runs.js +124 -11
  144. package/engine/qa-sessions.js +139 -40
  145. package/engine/quarantine-refs.js +81 -11
  146. package/engine/queries.js +163 -9
  147. package/engine/review-learning-backfill.js +482 -0
  148. package/engine/review-learning.js +1236 -0
  149. package/engine/runtimes/claude.js +60 -2
  150. package/engine/runtimes/codex.js +57 -16
  151. package/engine/runtimes/contract.js +21 -0
  152. package/engine/runtimes/copilot.js +51 -3
  153. package/engine/runtimes/index.js +1 -0
  154. package/engine/scheduler.js +201 -32
  155. package/engine/settings-validation.js +909 -0
  156. package/engine/shared.js +705 -208
  157. package/engine/spawn-agent.js +48 -21
  158. package/engine/steering-constraints.js +31 -0
  159. package/engine/steering-store.js +10 -2
  160. package/engine/steering.js +14 -2
  161. package/engine/timeout.js +404 -111
  162. package/engine/untrusted-fence.js +24 -10
  163. package/engine/watch-actions.js +7 -2
  164. package/engine/watches.js +353 -51
  165. package/engine/work-item-validation.js +561 -0
  166. package/engine/work-items-store.js +61 -1
  167. package/engine/worktree-gc.js +656 -37
  168. package/engine/worktree-preflight.js +154 -0
  169. package/engine.js +2068 -342
  170. package/package.json +1 -1
  171. package/playbooks/_pr-description-audit.md +88 -41
  172. package/playbooks/build-fix-complex.md +3 -3
  173. package/playbooks/docs.md +2 -2
  174. package/playbooks/fix.md +18 -18
  175. package/playbooks/implement.md +18 -18
  176. package/playbooks/qa-session-draft.md +10 -12
  177. package/playbooks/qa-session-execute.md +16 -12
  178. package/playbooks/qa-validate.md +8 -7
  179. package/playbooks/shared-rules.md +45 -0
  180. package/playbooks/test.md +4 -0
  181. package/prompts/cc-system.md +1 -1
  182. package/skills/check-self-authored-review-comment/SKILL.md +2 -2
@@ -0,0 +1,602 @@
1
+ 'use strict';
2
+
3
+ const keepProcesses = require('../keep-process-sweep');
4
+ const managedSpawn = require('../managed-spawn');
5
+ const managedSpecNameMax = managedSpawn.validateManagedSpecName('x').maxLength;
6
+ const agentApiValidation = require('../agent-api-validation');
7
+
8
+ function absentOrEmptyOperatorBody() {
9
+ return {
10
+ audit: 'audited',
11
+ body: {
12
+ policy: 'optional',
13
+ fields: [],
14
+ rationale: 'The fixed operator action accepts either no body or an empty JSON object.',
15
+ },
16
+ constraints: [
17
+ { kind: 'absent-or-empty-object', location: 'body' },
18
+ ],
19
+ negativeTests: [
20
+ { strategy: 'send-meaningful-object-body', expectedStatus: 400 },
21
+ { strategy: 'send-non-object-json-body', expectedStatus: 400 },
22
+ ],
23
+ };
24
+ }
25
+
26
+ module.exports = {
27
+ owner: 'orchestration',
28
+ routes: [
29
+ 'GET /api/dispatch',
30
+ 'GET /api/schedules',
31
+ 'GET /api/pipelines',
32
+ 'GET /api/keep-processes',
33
+ 'GET /api/worktree-quarantine-refs',
34
+ 'POST /api/keep-processes/kill',
35
+ 'GET /api/managed-processes',
36
+ 'GET /api/managed-processes/by-name/<name>',
37
+ 'POST /api/managed-processes/kill',
38
+ 'POST /api/managed-processes/restart',
39
+ 'GET /api/managed-processes/log-stream/<name>',
40
+ 'POST /api/engine/polling/pause',
41
+ 'POST /api/engine/polling/resume',
42
+ 'POST /api/engine/auto-fix/pause',
43
+ 'POST /api/engine/auto-fix/resume',
44
+ 'GET /api/dispatch/<id>/completion-report',
45
+ 'POST /api/schedules/parse-natural',
46
+ 'POST /api/schedules',
47
+ 'POST /api/schedules/update',
48
+ 'POST /api/schedules/delete',
49
+ 'POST /api/schedules/run-now',
50
+ 'GET /api/watches',
51
+ 'GET /api/watches/target-types',
52
+ 'GET /api/watches/action-types',
53
+ 'GET /api/watches/<id>/history',
54
+ 'POST /api/watches',
55
+ 'POST /api/watches/update',
56
+ 'POST /api/watches/delete',
57
+ 'POST /api/pipelines',
58
+ 'POST /api/pipelines/update',
59
+ 'POST /api/pipelines/delete',
60
+ 'POST /api/pipelines/trigger',
61
+ 'POST /api/pipelines/continue',
62
+ 'POST /api/pipelines/abort',
63
+ 'POST /api/pipelines/retrigger',
64
+ 'POST /api/engine/wakeup',
65
+ 'POST /api/engine/restart',
66
+ ],
67
+ bodyless: [
68
+ 'POST /api/engine/polling/pause',
69
+ 'POST /api/engine/polling/resume',
70
+ 'POST /api/engine/auto-fix/pause',
71
+ 'POST /api/engine/auto-fix/resume',
72
+ 'POST /api/engine/wakeup',
73
+ 'POST /api/engine/restart',
74
+ ],
75
+ overrides: {
76
+ 'GET /api/keep-processes': { noInput: true },
77
+ 'GET /api/schedules': { noInput: true },
78
+ 'GET /api/pipelines': { noInput: true },
79
+ 'GET /api/watches': { noInput: true },
80
+ 'GET /api/watches/target-types': { noInput: true },
81
+ 'GET /api/watches/action-types': { noInput: true },
82
+ 'GET /api/watches/<id>/history': {
83
+ audit: 'audited',
84
+ path: {
85
+ policy: 'required',
86
+ fields: [
87
+ {
88
+ name: 'id',
89
+ type: 'identifier',
90
+ required: true,
91
+ maxLength: 128,
92
+ allowedValuesSource: 'watches[].id',
93
+ },
94
+ ],
95
+ },
96
+ constraints: [
97
+ { kind: 'validated-before-history-read', field: 'id', location: 'path' },
98
+ ],
99
+ negativeTests: [
100
+ { strategy: 'reject-malformed-watch-id', expectedStatus: 400 },
101
+ { strategy: 'return-not-found-for-valid-missing-watch-id', expectedStatus: 404 },
102
+ ],
103
+ },
104
+ 'GET /api/dispatch': {
105
+ audit: 'audited',
106
+ query: { policy: 'none', fields: [] },
107
+ noInputReason: 'The dispatch queue view is derived from live engine state; it consumes no caller-supplied body, path, query, or header input.',
108
+ },
109
+ 'GET /api/dispatch/<id>/completion-report': {
110
+ audit: 'audited',
111
+ path: {
112
+ policy: 'required',
113
+ fields: [{
114
+ name: 'id',
115
+ type: 'identifier',
116
+ required: true,
117
+ maxLength: agentApiValidation.DISPATCH_ID_MAX_LENGTH,
118
+ }],
119
+ },
120
+ negativeTests: [{
121
+ strategy: 'reject-before-completion-report-open',
122
+ expectedStatus: '4xx',
123
+ audit: 'audited',
124
+ }],
125
+ },
126
+ 'POST /api/engine/polling/pause': absentOrEmptyOperatorBody(),
127
+ 'POST /api/engine/polling/resume': absentOrEmptyOperatorBody(),
128
+ 'POST /api/engine/auto-fix/pause': absentOrEmptyOperatorBody(),
129
+ 'POST /api/engine/auto-fix/resume': absentOrEmptyOperatorBody(),
130
+ 'POST /api/engine/wakeup': absentOrEmptyOperatorBody(),
131
+ 'POST /api/engine/restart': absentOrEmptyOperatorBody(),
132
+ 'GET /api/worktree-quarantine-refs': {
133
+ audit: 'audited',
134
+ noInputReason: 'Reads quarantine refs only from configured project checkouts; accepts no caller input.',
135
+ constraints: [
136
+ {
137
+ kind: 'configured-project-membership',
138
+ source: 'config.projects[].localPath',
139
+ },
140
+ ],
141
+ },
142
+ 'GET /api/managed-processes': {
143
+ audit: 'audited',
144
+ query: {
145
+ policy: 'optional',
146
+ fields: [
147
+ {
148
+ name: 'project',
149
+ type: 'string',
150
+ required: false,
151
+ maxLength: 64,
152
+ allowedValuesSource: 'config.projects[].name',
153
+ },
154
+ ],
155
+ },
156
+ constraints: [{ kind: 'single-value-query-fields' }],
157
+ negativeTests: [
158
+ { strategy: 'invalid-project-shape', expectedStatus: 400 },
159
+ { strategy: 'unknown-project', expectedStatus: 404 },
160
+ ],
161
+ },
162
+ 'GET /api/managed-processes/log-stream/<name>': {
163
+ audit: 'audited',
164
+ path: {
165
+ policy: 'required',
166
+ fields: [{
167
+ name: 'name',
168
+ type: 'string',
169
+ required: true,
170
+ maxLength: managedSpecNameMax,
171
+ pattern: 'kebab-case',
172
+ validatorSource: 'managed-spawn.validateManagedSpecName',
173
+ }],
174
+ },
175
+ query: {
176
+ policy: 'optional',
177
+ fields: [
178
+ {
179
+ name: 'tail',
180
+ type: 'integer',
181
+ required: false,
182
+ default: managedSpawn.MANAGED_LOG_TAIL_DEFAULT_BYTES,
183
+ min: 1,
184
+ max: managedSpawn.MANAGED_LOG_TAIL_MAX_BYTES,
185
+ },
186
+ ],
187
+ },
188
+ constraints: [{ kind: 'single-value-query-fields' }],
189
+ negativeTests: [
190
+ { strategy: 'invalid-name-or-tail-before-state-or-file-read', expectedStatus: 400 },
191
+ { strategy: 'well-formed-missing-name-or-log', expectedStatus: 404 },
192
+ ],
193
+ },
194
+ 'POST /api/keep-processes/kill': {
195
+ audit: 'audited',
196
+ body: {
197
+ policy: 'required',
198
+ fields: [
199
+ {
200
+ name: 'agentId',
201
+ type: 'string',
202
+ required: true,
203
+ maxLength: keepProcesses.KEEP_PROCESS_AGENT_ID_MAX,
204
+ pattern: '^[A-Za-z0-9][A-Za-z0-9_-]*$',
205
+ validatorSource: 'keep-process-sweep.isSafeKeepProcessAgentId',
206
+ },
207
+ {
208
+ name: 'pid',
209
+ type: 'integer',
210
+ required: true,
211
+ min: 1,
212
+ max: keepProcesses.KEEP_PROCESS_PID_MAX,
213
+ validatorSource: 'keep-process-sweep.validateKeepProcessPid',
214
+ },
215
+ ],
216
+ },
217
+ constraints: [
218
+ { kind: 'no-unknown-fields', location: 'body' },
219
+ { kind: 'declared-pid-required-before-kill' },
220
+ { kind: 'valid-keep-pids-record-required-before-kill' },
221
+ { kind: 'identity-bound-process-kill', source: 'process-utils.listAllProcesses' },
222
+ ],
223
+ negativeTests: [
224
+ { strategy: 'invalid-agent-or-pid-before-file-read-or-kill', expectedStatus: 400 },
225
+ { strategy: 'missing-agent-file-or-pid', expectedStatus: 404 },
226
+ { strategy: 'process-spy-and-file-snapshot-on-rejection', expectedStatus: '4xx' },
227
+ ],
228
+ },
229
+ 'GET /api/managed-processes/by-name/<name>': {
230
+ audit: 'audited',
231
+ path: {
232
+ policy: 'required',
233
+ fields: [{
234
+ name: 'name',
235
+ type: 'string',
236
+ required: true,
237
+ maxLength: managedSpecNameMax,
238
+ pattern: 'kebab-case',
239
+ validatorSource: 'managed-spawn.validateManagedSpecName',
240
+ }],
241
+ },
242
+ negativeTests: [
243
+ { strategy: 'invalid-name-before-state-read', expectedStatus: 400 },
244
+ { strategy: 'well-formed-missing-name', expectedStatus: 404 },
245
+ ],
246
+ },
247
+ 'POST /api/managed-processes/kill': {
248
+ audit: 'audited',
249
+ body: {
250
+ policy: 'required',
251
+ fields: [{
252
+ name: 'name',
253
+ type: 'string',
254
+ required: true,
255
+ maxLength: managedSpecNameMax,
256
+ pattern: 'kebab-case',
257
+ validatorSource: 'managed-spawn.validateManagedSpecName',
258
+ }],
259
+ },
260
+ constraints: [
261
+ { kind: 'no-unknown-fields', location: 'body' },
262
+ {
263
+ kind: 'generation-and-identity-bound-process-control',
264
+ source: 'managed-processes pid + started_at, process-utils.getProcessIdentity',
265
+ },
266
+ ],
267
+ negativeTests: [
268
+ { strategy: 'invalid-name-before-state-read-or-kill', expectedStatus: 400 },
269
+ { strategy: 'well-formed-missing-name', expectedStatus: 404 },
270
+ { strategy: 'process-spy-zero-calls-on-rejection', expectedStatus: '4xx' },
271
+ ],
272
+ },
273
+ 'POST /api/managed-processes/restart': {
274
+ audit: 'audited',
275
+ body: {
276
+ policy: 'required',
277
+ fields: [{
278
+ name: 'name',
279
+ type: 'string',
280
+ required: true,
281
+ maxLength: managedSpecNameMax,
282
+ pattern: 'kebab-case',
283
+ validatorSource: 'managed-spawn.validateManagedSpecName',
284
+ }],
285
+ },
286
+ constraints: [
287
+ { kind: 'no-unknown-fields', location: 'body' },
288
+ {
289
+ kind: 'generation-and-identity-bound-process-control',
290
+ source: 'managed-processes pid + started_at, process-utils.getProcessIdentity',
291
+ },
292
+ ],
293
+ negativeTests: [
294
+ { strategy: 'invalid-name-before-state-read-kill-or-spawn', expectedStatus: 400 },
295
+ { strategy: 'well-formed-missing-name', expectedStatus: 404 },
296
+ { strategy: 'process-spy-zero-calls-on-rejection', expectedStatus: '4xx' },
297
+ ],
298
+ },
299
+ 'POST /api/schedules/parse-natural': {
300
+ audit: 'audited',
301
+ body: {
302
+ policy: 'required',
303
+ fields: [
304
+ { name: 'text', type: 'string', required: true, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.naturalText' },
305
+ ],
306
+ },
307
+ constraints: [
308
+ { kind: 'domain-validator', source: 'scheduler.validateCronExpression (LLM result)' },
309
+ ],
310
+ negativeTests: [
311
+ { strategy: 'reject-non-string-or-empty-natural-language-input-before-llm', expectedStatus: 400 },
312
+ { strategy: 'reject-invalid-llm-cron-result', expectedStatus: 422 },
313
+ ],
314
+ },
315
+ 'POST /api/schedules': {
316
+ audit: 'audited',
317
+ body: {
318
+ policy: 'required',
319
+ fields: [
320
+ { name: 'id', type: 'identifier', required: false, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.id' },
321
+ { name: 'cron', type: 'string', required: true },
322
+ { name: 'title', type: 'string', required: true, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.title' },
323
+ { name: 'description', type: 'string', required: false, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.description' },
324
+ { name: 'type', type: 'string', required: false, default: 'implement', allowedValuesSource: 'shared.WORK_TYPE' },
325
+ { name: 'project', type: 'string', required: false, allowedValuesSource: 'config.projects[].name' },
326
+ { name: 'agent', type: 'string', required: false, allowedValuesSource: 'config.agents keys' },
327
+ { name: 'participants', type: 'array', required: false, allowedValuesSource: 'config.agents keys' },
328
+ { name: 'priority', type: 'string', required: false, default: 'medium', allowedValuesSource: 'shared.WORK_ITEM_PRIORITY_VALUES' },
329
+ { name: 'enabled', type: 'boolean', required: false, default: true },
330
+ ],
331
+ },
332
+ constraints: [
333
+ { kind: 'domain-validator', source: 'scheduler.validateScheduleDefinition' },
334
+ ],
335
+ negativeTests: [
336
+ { strategy: 'reject-malformed-or-out-of-range-cron', expectedStatus: 400 },
337
+ { strategy: 'reject-unknown-work-type-project-agent-or-priority', expectedStatus: 400 },
338
+ { strategy: 'reject-non-boolean-enabled', expectedStatus: 400 },
339
+ ],
340
+ },
341
+ 'POST /api/schedules/update': {
342
+ audit: 'audited',
343
+ body: {
344
+ policy: 'required',
345
+ fields: [
346
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.id' },
347
+ { name: 'cron', type: 'string', required: false },
348
+ { name: 'title', type: 'string', required: false, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.title' },
349
+ { name: 'description', type: 'string', required: false, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.description' },
350
+ { name: 'type', type: 'string', required: false, allowedValuesSource: 'shared.WORK_TYPE' },
351
+ { name: 'project', type: 'string|null', required: false, allowedValuesSource: 'config.projects[].name' },
352
+ { name: 'agent', type: 'string|null', required: false, allowedValuesSource: 'config.agents keys' },
353
+ { name: 'participants', type: 'array', required: false, allowedValuesSource: 'config.agents keys' },
354
+ { name: 'priority', type: 'string', required: false, allowedValuesSource: 'shared.WORK_ITEM_PRIORITY_VALUES' },
355
+ { name: 'enabled', type: 'boolean', required: false },
356
+ ],
357
+ },
358
+ constraints: [
359
+ { kind: 'merged-candidate-domain-validator', source: 'scheduler.validateScheduleDefinition' },
360
+ ],
361
+ negativeTests: [
362
+ { strategy: 'reject-invalid-identifier-before-config-mutation', expectedStatus: 400 },
363
+ { strategy: 'reject-invalid-merged-schedule-atomically', expectedStatus: 400 },
364
+ { strategy: 'preserve-missing-schedule-semantics', expectedStatus: 404 },
365
+ ],
366
+ },
367
+ 'POST /api/schedules/delete': {
368
+ audit: 'audited',
369
+ body: {
370
+ policy: 'required',
371
+ fields: [
372
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.id' },
373
+ ],
374
+ },
375
+ negativeTests: [
376
+ { strategy: 'reject-invalid-identifier-before-config-mutation', expectedStatus: 400 },
377
+ { strategy: 'preserve-missing-schedule-semantics', expectedStatus: 404 },
378
+ ],
379
+ },
380
+ 'POST /api/schedules/run-now': {
381
+ audit: 'audited',
382
+ body: {
383
+ policy: 'required',
384
+ fields: [
385
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'scheduler.SCHEDULE_LIMITS.id' },
386
+ ],
387
+ },
388
+ constraints: [
389
+ { kind: 'stored-definition-domain-validator', source: 'scheduler.validateScheduleDefinition' },
390
+ ],
391
+ negativeTests: [
392
+ { strategy: 'reject-invalid-stored-schedule-before-dispatch', expectedStatus: 400 },
393
+ { strategy: 'preserve-missing-schedule-semantics', expectedStatus: 404 },
394
+ { strategy: 'preserve-active-work-item-conflict', expectedStatus: 409 },
395
+ ],
396
+ },
397
+ 'POST /api/pipelines': {
398
+ audit: 'audited',
399
+ body: {
400
+ policy: 'required',
401
+ fields: [
402
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.id' },
403
+ { name: 'title', type: 'string', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.title' },
404
+ { name: 'description', type: 'string', required: false, maxLengthSource: 'pipeline.PIPELINE_LIMITS.description' },
405
+ { name: 'stages', type: 'array', required: true, allowedValuesSource: 'shared.STAGE_TYPE' },
406
+ { name: 'trigger', type: 'object', required: false },
407
+ { name: 'stopWhen', type: 'string|object', required: false, allowedValuesSource: 'pipeline.PIPELINE_CONDITION_CHECKS' },
408
+ { name: 'monitoredResources', type: 'array', required: false },
409
+ { name: 'project', type: 'string', required: false, allowedValuesSource: 'config.projects[].name' },
410
+ { name: 'projects', type: 'array', required: false, allowedValuesSource: 'config.projects[].name' },
411
+ { name: 'enabled', type: 'boolean', required: false, default: true },
412
+ ],
413
+ },
414
+ constraints: [
415
+ { kind: 'recursive-plain-object-validator', source: 'pipeline.validatePipelineDefinition' },
416
+ { kind: 'acyclic-stage-dependency-graph' },
417
+ { kind: 'canonical-stage-work-type-priority-enums' },
418
+ ],
419
+ negativeTests: [
420
+ { strategy: 'reject-malformed-stage-graph-before-file-write', expectedStatus: 400 },
421
+ { strategy: 'reject-invalid-stage-specific-payload', expectedStatus: 400 },
422
+ { strategy: 'preserve-existing-pipeline-conflict', expectedStatus: 409 },
423
+ ],
424
+ },
425
+ 'POST /api/pipelines/update': {
426
+ audit: 'audited',
427
+ body: {
428
+ policy: 'required',
429
+ fields: [
430
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.id' },
431
+ { name: 'title', type: 'string', required: false, maxLengthSource: 'pipeline.PIPELINE_LIMITS.title' },
432
+ { name: 'description', type: 'string', required: false, maxLengthSource: 'pipeline.PIPELINE_LIMITS.description' },
433
+ { name: 'stages', type: 'array', required: false, allowedValuesSource: 'shared.STAGE_TYPE' },
434
+ { name: 'trigger', type: 'object|null', required: false },
435
+ { name: 'stopWhen', type: 'string|object|null', required: false, allowedValuesSource: 'pipeline.PIPELINE_CONDITION_CHECKS' },
436
+ { name: 'monitoredResources', type: 'array', required: false },
437
+ { name: 'project', type: 'string', required: false, allowedValuesSource: 'config.projects[].name' },
438
+ { name: 'projects', type: 'array', required: false, allowedValuesSource: 'config.projects[].name' },
439
+ { name: 'enabled', type: 'boolean', required: false },
440
+ ],
441
+ },
442
+ constraints: [
443
+ { kind: 'merged-candidate-domain-validator', source: 'pipeline.validatePipelineDefinition' },
444
+ ],
445
+ negativeTests: [
446
+ { strategy: 'reject-invalid-merged-definition-before-file-write', expectedStatus: 400 },
447
+ { strategy: 'preserve-missing-pipeline-semantics', expectedStatus: 404 },
448
+ ],
449
+ },
450
+ 'POST /api/pipelines/delete': {
451
+ audit: 'audited',
452
+ body: {
453
+ policy: 'required',
454
+ fields: [
455
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.id' },
456
+ ],
457
+ },
458
+ negativeTests: [
459
+ { strategy: 'reject-invalid-identifier-before-file-access', expectedStatus: 400 },
460
+ { strategy: 'preserve-missing-pipeline-semantics', expectedStatus: 404 },
461
+ ],
462
+ },
463
+ 'POST /api/pipelines/trigger': {
464
+ audit: 'audited',
465
+ body: {
466
+ policy: 'required',
467
+ fields: [
468
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.id' },
469
+ ],
470
+ },
471
+ constraints: [
472
+ { kind: 'stored-definition-domain-validator', source: 'pipeline.validatePipelineDefinition' },
473
+ { kind: 'no-active-run', statusSource: 'shared.PIPELINE_STATUS' },
474
+ ],
475
+ negativeTests: [
476
+ { strategy: 'preserve-missing-pipeline-semantics', expectedStatus: 404 },
477
+ { strategy: 'preserve-active-run-conflict', expectedStatus: 409 },
478
+ { strategy: 'reject-invalid-definition-before-run-write', expectedStatus: 400 },
479
+ ],
480
+ },
481
+ 'POST /api/pipelines/continue': {
482
+ audit: 'audited',
483
+ body: {
484
+ policy: 'required',
485
+ fields: [
486
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.id' },
487
+ { name: 'stageId', type: 'identifier', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.id' },
488
+ ],
489
+ },
490
+ constraints: [
491
+ { kind: 'stage-type-and-state', typeSource: 'shared.STAGE_TYPE.WAIT', statusSource: 'shared.PIPELINE_STATUS.WAITING_HUMAN' },
492
+ ],
493
+ negativeTests: [
494
+ { strategy: 'preserve-missing-pipeline-or-run-semantics', expectedStatus: 404 },
495
+ { strategy: 'reject-stage-not-waiting-with-conflict', expectedStatus: 409 },
496
+ ],
497
+ },
498
+ 'POST /api/pipelines/abort': {
499
+ audit: 'audited',
500
+ body: {
501
+ policy: 'required',
502
+ fields: [
503
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.id' },
504
+ ],
505
+ },
506
+ constraints: [
507
+ { kind: 'active-run-required', statusSource: 'shared.PIPELINE_STATUS' },
508
+ ],
509
+ negativeTests: [
510
+ { strategy: 'preserve-missing-pipeline-or-active-run-semantics', expectedStatus: 404 },
511
+ { strategy: 'reject-invalid-identifier-before-run-or-work-item-mutation', expectedStatus: 400 },
512
+ ],
513
+ },
514
+ 'POST /api/pipelines/retrigger': {
515
+ audit: 'audited',
516
+ body: {
517
+ policy: 'required',
518
+ fields: [
519
+ { name: 'id', type: 'identifier', required: true, maxLengthSource: 'pipeline.PIPELINE_LIMITS.id' },
520
+ ],
521
+ },
522
+ constraints: [
523
+ { kind: 'definition-existence-and-validation-before-abort', source: 'pipeline.validatePipelineDefinition' },
524
+ ],
525
+ negativeTests: [
526
+ { strategy: 'preserve-missing-pipeline-without-aborting-orphan-run', expectedStatus: 404 },
527
+ { strategy: 'preserve-racing-active-run-conflict', expectedStatus: 409 },
528
+ ],
529
+ },
530
+ 'POST /api/watches': {
531
+ audit: 'audited',
532
+ body: {
533
+ policy: 'required',
534
+ fields: [
535
+ { name: 'target', type: 'string|object', required: true, allowedValuesSource: 'watches target-type registry[targetType].validateTarget' },
536
+ { name: 'targetType', type: 'string', required: true, allowedValuesSource: 'watches target-type registry' },
537
+ { name: 'condition', type: 'string', required: true, allowedValuesSource: 'watches target-type registry[targetType].conditions' },
538
+ { name: 'interval', type: 'integer', required: false, default: 300000, min: 60000, max: Number.MAX_SAFE_INTEGER },
539
+ { name: 'owner', type: 'string', required: false, default: 'human', allowedValuesSource: 'human + config.agents keys' },
540
+ { name: 'description', type: 'string', required: false, maxLength: 4000 },
541
+ { name: 'project', type: 'string|null', required: false, allowedValuesSource: 'config.projects[].name' },
542
+ { name: 'notify', type: 'string', required: false, default: 'inbox', allowedValues: ['inbox', 'none'] },
543
+ { name: 'stopAfter', type: 'integer', required: false, default: 0, min: 0, max: Number.MAX_SAFE_INTEGER },
544
+ { name: 'onNotMet', type: 'string|null', required: false, allowedValues: ['notify'] },
545
+ { name: 'action', type: 'object|array|null', required: false, allowedValuesSource: 'watch-actions registry' },
546
+ { name: 'requires', type: 'array|null', required: false, allowedValuesSource: 'watches target-type registry requirement conditions' },
547
+ ],
548
+ },
549
+ constraints: [
550
+ { kind: 'target-validator-by-target-type', fields: ['target', 'targetType'] },
551
+ { kind: 'condition-enum-by-target-type', fields: ['condition', 'targetType'] },
552
+ { kind: 'registered-action-validator', field: 'action' },
553
+ { kind: 'registered-requirement-validator', field: 'requires', maxItems: 20 },
554
+ ],
555
+ negativeTests: [
556
+ { strategy: 'reject-coercive-or-out-of-range-interval-and-stop-count', expectedStatus: 400 },
557
+ { strategy: 'reject-invalid-dynamic-target-condition-action-or-requirement', expectedStatus: 400 },
558
+ { strategy: 'reject-unknown-owner-or-project', expectedStatus: 400 },
559
+ ],
560
+ },
561
+ 'POST /api/watches/update': {
562
+ audit: 'audited',
563
+ body: {
564
+ policy: 'required',
565
+ fields: [
566
+ { name: 'id', type: 'string', required: true },
567
+ { name: 'status', type: 'string', required: false, allowedValuesSource: 'shared.WATCH_STATUS' },
568
+ { name: 'condition', type: 'string', required: false, allowedValuesSource: 'watches target-type registry[current targetType].conditions' },
569
+ { name: 'interval', type: 'integer', required: false, min: 60000, max: Number.MAX_SAFE_INTEGER },
570
+ { name: 'description', type: 'string|null', required: false, maxLength: 4000 },
571
+ { name: 'notify', type: 'string', required: false, allowedValues: ['inbox', 'none'] },
572
+ { name: 'stopAfter', type: 'integer', required: false, min: 0, max: Number.MAX_SAFE_INTEGER },
573
+ { name: 'onNotMet', type: 'string|null', required: false, allowedValues: ['notify'] },
574
+ { name: 'action', type: 'object|array|null', required: false, allowedValuesSource: 'watch-actions registry' },
575
+ { name: 'requires', type: 'array|null', required: false, allowedValuesSource: 'watches target-type registry requirement conditions' },
576
+ ],
577
+ },
578
+ constraints: [
579
+ { kind: 'condition-enum-by-current-target-type', fields: ['id', 'condition'] },
580
+ { kind: 'registered-action-validator', field: 'action' },
581
+ { kind: 'registered-requirement-validator', field: 'requires', maxItems: 20 },
582
+ ],
583
+ negativeTests: [
584
+ { strategy: 'reject-invalid-status-condition-action-or-requirement-before-mutation', expectedStatus: 400 },
585
+ { strategy: 'return-not-found-only-for-valid-missing-watch-id', expectedStatus: 404 },
586
+ ],
587
+ },
588
+ 'POST /api/watches/delete': {
589
+ audit: 'audited',
590
+ body: {
591
+ policy: 'required',
592
+ fields: [
593
+ { name: 'id', type: 'string', required: true },
594
+ ],
595
+ },
596
+ negativeTests: [
597
+ { strategy: 'reject-malformed-watch-id', expectedStatus: 400 },
598
+ { strategy: 'return-not-found-for-missing-watch-id', expectedStatus: 404 },
599
+ ],
600
+ },
601
+ },
602
+ };