@miller-tech/uap 2.36.1 → 2.39.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 (101) hide show
  1. package/config/model-profiles/qwen38.json +3 -3
  2. package/config/model-registry.json +96 -6
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/bin/cli.js +57 -0
  5. package/dist/bin/cli.js.map +1 -1
  6. package/dist/cli/deliver.d.ts +46 -0
  7. package/dist/cli/deliver.d.ts.map +1 -1
  8. package/dist/cli/deliver.js +155 -36
  9. package/dist/cli/deliver.js.map +1 -1
  10. package/dist/cli/models.d.ts +11 -1
  11. package/dist/cli/models.d.ts.map +1 -1
  12. package/dist/cli/models.js +164 -3
  13. package/dist/cli/models.js.map +1 -1
  14. package/dist/cli/workflow.d.ts +36 -0
  15. package/dist/cli/workflow.d.ts.map +1 -0
  16. package/dist/cli/workflow.js +246 -0
  17. package/dist/cli/workflow.js.map +1 -0
  18. package/dist/delivery/run-state.d.ts +1 -1
  19. package/dist/delivery/run-state.d.ts.map +1 -1
  20. package/dist/delivery/run-state.js +1 -1
  21. package/dist/delivery/run-state.js.map +1 -1
  22. package/dist/delivery/team-solve-deliver.d.ts +83 -0
  23. package/dist/delivery/team-solve-deliver.d.ts.map +1 -0
  24. package/dist/delivery/team-solve-deliver.js +191 -0
  25. package/dist/delivery/team-solve-deliver.js.map +1 -0
  26. package/dist/inference/model-files.d.ts +111 -0
  27. package/dist/inference/model-files.d.ts.map +1 -0
  28. package/dist/inference/model-files.js +172 -0
  29. package/dist/inference/model-files.js.map +1 -0
  30. package/dist/placement/registry.d.ts +650 -0
  31. package/dist/placement/registry.d.ts.map +1 -1
  32. package/dist/placement/registry.js +21 -0
  33. package/dist/placement/registry.js.map +1 -1
  34. package/dist/workflow/loader.d.ts +34 -0
  35. package/dist/workflow/loader.d.ts.map +1 -0
  36. package/dist/workflow/loader.js +232 -0
  37. package/dist/workflow/loader.js.map +1 -0
  38. package/dist/workflow/runner.d.ts +90 -0
  39. package/dist/workflow/runner.d.ts.map +1 -0
  40. package/dist/workflow/runner.js +416 -0
  41. package/dist/workflow/runner.js.map +1 -0
  42. package/dist/workflow/types.d.ts +400 -0
  43. package/dist/workflow/types.d.ts.map +1 -0
  44. package/dist/workflow/types.js +147 -0
  45. package/dist/workflow/types.js.map +1 -0
  46. package/docs/INDEX.md +1 -0
  47. package/docs/architecture/adr/0013-declarative-workflow-layer.md +99 -0
  48. package/docs/architecture/adr/0014-loop-steps-and-team-solve-deliver.md +142 -0
  49. package/docs/benchmarks/LCB_DROID_SMOKE.md +111 -0
  50. package/docs/guides/DELIVER.md +3 -1
  51. package/docs/guides/WORKFLOWS.md +179 -0
  52. package/docs/plans/model-performance-proof-options.md +39 -0
  53. package/docs/reference/CLI.md +33 -0
  54. package/docs/reference/CONFIGURATION.md +1 -0
  55. package/docs/reference/FEATURES.md +18 -0
  56. package/examples/auto-coordinator-example.js +127 -0
  57. package/examples/demo-order-solid.ts +19 -0
  58. package/examples/order-processing-strategy-pattern.ts +186 -0
  59. package/examples/order-processor-refactored.ts +473 -0
  60. package/examples/order-solid-refactor.ts +830 -0
  61. package/examples/refactored-order-processing.ts +106 -0
  62. package/examples/run-order-processing.ts +48 -0
  63. package/examples/solid-order-processing.ts +324 -0
  64. package/examples/strategy-pattern-example.ts +100 -0
  65. package/examples/workflows/team-solve/README.md +85 -0
  66. package/examples/workflows/team-solve/check-samples.sh +50 -0
  67. package/examples/workflows/team-solve/sample/task.md +22 -0
  68. package/examples/workflows/team-solve/sample/tests/1.in +2 -0
  69. package/examples/workflows/team-solve/sample/tests/1.out +1 -0
  70. package/examples/workflows/team-solve/sample/tests/2.in +2 -0
  71. package/examples/workflows/team-solve/sample/tests/2.out +1 -0
  72. package/examples/workflows/team-solve/sample/tests/3.in +2 -0
  73. package/examples/workflows/team-solve/sample/tests/3.out +1 -0
  74. package/examples/workflows/team-solve/sample/tests/4.in +2 -0
  75. package/examples/workflows/team-solve/sample/tests/4.out +1 -0
  76. package/examples/workflows/team-solve/sample/tests/5.in +1 -0
  77. package/examples/workflows/team-solve/sample/tests/5.out +1 -0
  78. package/examples/workflows/team-solve/steps/ideate-a.md +26 -0
  79. package/examples/workflows/team-solve/steps/ideate-b.md +28 -0
  80. package/examples/workflows/team-solve/steps/plan.md +20 -0
  81. package/examples/workflows/team-solve/steps/repair.md +25 -0
  82. package/examples/workflows/team-solve/steps/review.md +29 -0
  83. package/examples/workflows/team-solve/steps/solve.md +26 -0
  84. package/examples/workflows/team-solve/workflow.yaml +78 -0
  85. package/examples/workflows/team-solve-deliver/README.md +84 -0
  86. package/examples/workflows/team-solve-deliver/steps/ideate-a.md +25 -0
  87. package/examples/workflows/team-solve-deliver/steps/ideate-b.md +28 -0
  88. package/examples/workflows/team-solve-deliver/steps/plan.md +25 -0
  89. package/examples/workflows/team-solve-deliver/steps/repair.md +20 -0
  90. package/examples/workflows/team-solve-deliver/steps/review.md +31 -0
  91. package/examples/workflows/team-solve-deliver/steps/solve.md +28 -0
  92. package/examples/workflows/team-solve-deliver/steps/verify.md +9 -0
  93. package/examples/workflows/team-solve-deliver/workflow.yaml +74 -0
  94. package/package.json +2 -1
  95. package/src/policies/enforcers/__pycache__/_common.cpython-311.pyc +0 -0
  96. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-311.pyc +0 -0
  97. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-311.pyc +0 -0
  98. package/templates/hooks/__pycache__/deliver_autoroute.cpython-311.pyc +0 -0
  99. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-311.pyc +0 -0
  100. package/tools/agents/scripts/anthropic_proxy.py +181 -15
  101. package/tools/agents/tests/test_signature_quality.py +51 -0
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Declarative workflow types — the static layer over the blackboard
3
+ * orchestrator.
4
+ *
5
+ * WHY THIS EXISTS. The orchestrator (src/delivery/task-orchestrator.ts)
6
+ * executes a task DAG with fresh minimal contexts, bounded repair, and
7
+ * resume-at-the-boundary — but the DAG it runs is MODEL-AUTHORED at runtime
8
+ * (decompose.ts), so every mission re-plans itself and nothing is a durable,
9
+ * inspectable, reusable asset. The measured alternative (Memdoor's
10
+ * team-solve, GVS5H's method: five Qwen3.8-27B roles beating a frontier
11
+ * single-call on LiveCodeBench Hard at 9.8 vs 8.0) is a workflow authored
12
+ * ONCE as data — fixed role prompts, `requires:` edges, deterministic
13
+ * command steps — pointed at whatever model is serving.
14
+ *
15
+ * This module is the file format: a `workflow.yaml` plus role-prompt files.
16
+ * The runner (runner.ts) maps these steps onto `orchestrate()` unchanged, so
17
+ * the graph semantics (dependency-triggered dispatch, skip cascade, minimal
18
+ * repair, resume) are the validated delivery semantics, not a reimplementation.
19
+ */
20
+ import { z } from 'zod';
21
+ /** Step kinds. Each maps to one execution seam in the runner. */
22
+ export declare const STEP_KINDS: readonly ["agent", "command", "gate", "loop"];
23
+ export type StepKind = (typeof STEP_KINDS)[number];
24
+ declare const WorkflowStepSchema: z.ZodObject<{
25
+ id: z.ZodString;
26
+ kind: z.ZodEnum<["agent", "command", "gate", "loop"]>;
27
+ /** Human label for logs; defaults to the id. */
28
+ title: z.ZodOptional<z.ZodString>;
29
+ /**
30
+ * Per-step model pin — a ModelPresets id (e.g. `qwen38-27b`, the local
31
+ * strata arm through the proxy). Absent = the workflow default, then the
32
+ * CLI pin, then the local fleet default. Mirrors the decompose C2 per-leaf
33
+ * model dial: the plan (here: the author) decides which rung each step pays.
34
+ */
35
+ model: z.ZodOptional<z.ZodString>;
36
+ /**
37
+ * Path to the role-prompt file, relative to the workflow directory.
38
+ * Agent steps take `prompt` XOR `goal` — a file keeps the workflow.yaml
39
+ * reviewable while the role prompt stays a first-class artifact (the
40
+ * Memdoor layout: one file per step). Loop steps take the same pair:
41
+ * the role instruction the convergence loop executes against.
42
+ */
43
+ prompt: z.ZodOptional<z.ZodString>;
44
+ /** Inline role prompt (alternative to `prompt:`). */
45
+ goal: z.ZodOptional<z.ZodString>;
46
+ /** Run-directory files whose CONTENTS are inlined into the agent prompt. */
47
+ reads: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
48
+ /**
49
+ * Run-directory files this step produces. The first entry receives the
50
+ * agent reply (or the command's captured output); the rest are declared
51
+ * for the graph's write-collision check. Overwrite semantics — the file
52
+ * ledger is authoritative, chat is not. NOT for loop steps: a loop works
53
+ * the project tree (its DeliveryResult is the blackboard entry), not the
54
+ * run directory.
55
+ */
56
+ writes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
57
+ /** DAG edges: step ids that must SUCCEED before this one runs. */
58
+ requires: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
59
+ /** Acceptance criteria lines — assembled into the agent prompt. */
60
+ criteria: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
61
+ /** command kind: shell line, run with cwd = the run directory. */
62
+ command: z.ZodOptional<z.ZodString>;
63
+ /**
64
+ * command kind: capture the output but never fail the run — Memdoor's
65
+ * `test` step ("test-output.log (never fails the run)"). The verdict step
66
+ * is simply a command WITHOUT this flag: a nonzero exit fails the run.
67
+ */
68
+ neverFails: z.ZodOptional<z.ZodBoolean>;
69
+ /** command kind: kill the command after this many ms (default 120000). */
70
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
71
+ /** gate kind: prompt shown when the run pauses for approval. */
72
+ approvalPrompt: z.ZodOptional<z.ZodString>;
73
+ /**
74
+ * gate kind: maximum ms to WAIT for `uap workflow approve` before
75
+ * reporting the run as blocked (exit 2, resumable). 0 = never wait:
76
+ * pause immediately. Default 0 — approval is an operator act, not
77
+ * something a headless run should block a terminal on.
78
+ */
79
+ waitMs: z.ZodOptional<z.ZodNumber>;
80
+ /**
81
+ * loop kind: max executor turns for the convergence loop this step runs
82
+ * (the caller's default applies when absent). The loop's exit condition
83
+ * is the project's real completion gates — this bounds the attempt
84
+ * budget, it does not replace verification.
85
+ */
86
+ turns: z.ZodOptional<z.ZodNumber>;
87
+ /**
88
+ * loop kind: run the gates ONLY — no executor turns. The independent
89
+ * final verdict after repair steps: a green repair is not "delivered"
90
+ * until a verify-only pass says so.
91
+ */
92
+ verifyOnly: z.ZodOptional<z.ZodBoolean>;
93
+ }, "strip", z.ZodTypeAny, {
94
+ id: string;
95
+ kind: "command" | "agent" | "loop" | "gate";
96
+ model?: string | undefined;
97
+ goal?: string | undefined;
98
+ command?: string | undefined;
99
+ title?: string | undefined;
100
+ turns?: number | undefined;
101
+ prompt?: string | undefined;
102
+ timeoutMs?: number | undefined;
103
+ criteria?: string[] | undefined;
104
+ writes?: string[] | undefined;
105
+ reads?: string[] | undefined;
106
+ requires?: string[] | undefined;
107
+ neverFails?: boolean | undefined;
108
+ approvalPrompt?: string | undefined;
109
+ waitMs?: number | undefined;
110
+ verifyOnly?: boolean | undefined;
111
+ }, {
112
+ id: string;
113
+ kind: "command" | "agent" | "loop" | "gate";
114
+ model?: string | undefined;
115
+ goal?: string | undefined;
116
+ command?: string | undefined;
117
+ title?: string | undefined;
118
+ turns?: number | undefined;
119
+ prompt?: string | undefined;
120
+ timeoutMs?: number | undefined;
121
+ criteria?: string[] | undefined;
122
+ writes?: string[] | undefined;
123
+ reads?: string[] | undefined;
124
+ requires?: string[] | undefined;
125
+ neverFails?: boolean | undefined;
126
+ approvalPrompt?: string | undefined;
127
+ waitMs?: number | undefined;
128
+ verifyOnly?: boolean | undefined;
129
+ }>;
130
+ declare const WorkflowDefSchema: z.ZodObject<{
131
+ name: z.ZodString;
132
+ description: z.ZodOptional<z.ZodString>;
133
+ /**
134
+ * Default model pin for steps that do not name one. The full resolution
135
+ * order is step.model → defaultModel → CLI --model → the local fleet
136
+ * default (see cli/workflow.ts) — the workflow names no model and stays
137
+ * portable across whatever arm is serving.
138
+ */
139
+ defaultModel: z.ZodOptional<z.ZodString>;
140
+ /**
141
+ * Max concurrent INDEPENDENT steps (clamped to [1,16] by the
142
+ * orchestrator). Agent steps are stateless completions writing distinct
143
+ * files, so >1 is safe for the steps that declare disjoint `writes`; the
144
+ * loader rejects shared write targets across PARALLEL siblings.
145
+ * Default 1 — one local rail serves one completion at a time.
146
+ */
147
+ parallel: z.ZodOptional<z.ZodNumber>;
148
+ /**
149
+ * Bounded fresh re-execution of a failed step before its dependents are
150
+ * blocked (orchestrator maxRepairsPerTask). Env hard-ceiling 5 applies.
151
+ * Default: the orchestrator's (1).
152
+ */
153
+ maxRepairsPerStep: z.ZodOptional<z.ZodNumber>;
154
+ /** Assembled-prompt budget in chars (orchestrator contextBudgetChars). */
155
+ contextBudgetChars: z.ZodOptional<z.ZodNumber>;
156
+ steps: z.ZodArray<z.ZodObject<{
157
+ id: z.ZodString;
158
+ kind: z.ZodEnum<["agent", "command", "gate", "loop"]>;
159
+ /** Human label for logs; defaults to the id. */
160
+ title: z.ZodOptional<z.ZodString>;
161
+ /**
162
+ * Per-step model pin — a ModelPresets id (e.g. `qwen38-27b`, the local
163
+ * strata arm through the proxy). Absent = the workflow default, then the
164
+ * CLI pin, then the local fleet default. Mirrors the decompose C2 per-leaf
165
+ * model dial: the plan (here: the author) decides which rung each step pays.
166
+ */
167
+ model: z.ZodOptional<z.ZodString>;
168
+ /**
169
+ * Path to the role-prompt file, relative to the workflow directory.
170
+ * Agent steps take `prompt` XOR `goal` — a file keeps the workflow.yaml
171
+ * reviewable while the role prompt stays a first-class artifact (the
172
+ * Memdoor layout: one file per step). Loop steps take the same pair:
173
+ * the role instruction the convergence loop executes against.
174
+ */
175
+ prompt: z.ZodOptional<z.ZodString>;
176
+ /** Inline role prompt (alternative to `prompt:`). */
177
+ goal: z.ZodOptional<z.ZodString>;
178
+ /** Run-directory files whose CONTENTS are inlined into the agent prompt. */
179
+ reads: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
180
+ /**
181
+ * Run-directory files this step produces. The first entry receives the
182
+ * agent reply (or the command's captured output); the rest are declared
183
+ * for the graph's write-collision check. Overwrite semantics — the file
184
+ * ledger is authoritative, chat is not. NOT for loop steps: a loop works
185
+ * the project tree (its DeliveryResult is the blackboard entry), not the
186
+ * run directory.
187
+ */
188
+ writes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
189
+ /** DAG edges: step ids that must SUCCEED before this one runs. */
190
+ requires: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
191
+ /** Acceptance criteria lines — assembled into the agent prompt. */
192
+ criteria: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
193
+ /** command kind: shell line, run with cwd = the run directory. */
194
+ command: z.ZodOptional<z.ZodString>;
195
+ /**
196
+ * command kind: capture the output but never fail the run — Memdoor's
197
+ * `test` step ("test-output.log (never fails the run)"). The verdict step
198
+ * is simply a command WITHOUT this flag: a nonzero exit fails the run.
199
+ */
200
+ neverFails: z.ZodOptional<z.ZodBoolean>;
201
+ /** command kind: kill the command after this many ms (default 120000). */
202
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
203
+ /** gate kind: prompt shown when the run pauses for approval. */
204
+ approvalPrompt: z.ZodOptional<z.ZodString>;
205
+ /**
206
+ * gate kind: maximum ms to WAIT for `uap workflow approve` before
207
+ * reporting the run as blocked (exit 2, resumable). 0 = never wait:
208
+ * pause immediately. Default 0 — approval is an operator act, not
209
+ * something a headless run should block a terminal on.
210
+ */
211
+ waitMs: z.ZodOptional<z.ZodNumber>;
212
+ /**
213
+ * loop kind: max executor turns for the convergence loop this step runs
214
+ * (the caller's default applies when absent). The loop's exit condition
215
+ * is the project's real completion gates — this bounds the attempt
216
+ * budget, it does not replace verification.
217
+ */
218
+ turns: z.ZodOptional<z.ZodNumber>;
219
+ /**
220
+ * loop kind: run the gates ONLY — no executor turns. The independent
221
+ * final verdict after repair steps: a green repair is not "delivered"
222
+ * until a verify-only pass says so.
223
+ */
224
+ verifyOnly: z.ZodOptional<z.ZodBoolean>;
225
+ }, "strip", z.ZodTypeAny, {
226
+ id: string;
227
+ kind: "command" | "agent" | "loop" | "gate";
228
+ model?: string | undefined;
229
+ goal?: string | undefined;
230
+ command?: string | undefined;
231
+ title?: string | undefined;
232
+ turns?: number | undefined;
233
+ prompt?: string | undefined;
234
+ timeoutMs?: number | undefined;
235
+ criteria?: string[] | undefined;
236
+ writes?: string[] | undefined;
237
+ reads?: string[] | undefined;
238
+ requires?: string[] | undefined;
239
+ neverFails?: boolean | undefined;
240
+ approvalPrompt?: string | undefined;
241
+ waitMs?: number | undefined;
242
+ verifyOnly?: boolean | undefined;
243
+ }, {
244
+ id: string;
245
+ kind: "command" | "agent" | "loop" | "gate";
246
+ model?: string | undefined;
247
+ goal?: string | undefined;
248
+ command?: string | undefined;
249
+ title?: string | undefined;
250
+ turns?: number | undefined;
251
+ prompt?: string | undefined;
252
+ timeoutMs?: number | undefined;
253
+ criteria?: string[] | undefined;
254
+ writes?: string[] | undefined;
255
+ reads?: string[] | undefined;
256
+ requires?: string[] | undefined;
257
+ neverFails?: boolean | undefined;
258
+ approvalPrompt?: string | undefined;
259
+ waitMs?: number | undefined;
260
+ verifyOnly?: boolean | undefined;
261
+ }>, "many">;
262
+ }, "strip", z.ZodTypeAny, {
263
+ name: string;
264
+ steps: {
265
+ id: string;
266
+ kind: "command" | "agent" | "loop" | "gate";
267
+ model?: string | undefined;
268
+ goal?: string | undefined;
269
+ command?: string | undefined;
270
+ title?: string | undefined;
271
+ turns?: number | undefined;
272
+ prompt?: string | undefined;
273
+ timeoutMs?: number | undefined;
274
+ criteria?: string[] | undefined;
275
+ writes?: string[] | undefined;
276
+ reads?: string[] | undefined;
277
+ requires?: string[] | undefined;
278
+ neverFails?: boolean | undefined;
279
+ approvalPrompt?: string | undefined;
280
+ waitMs?: number | undefined;
281
+ verifyOnly?: boolean | undefined;
282
+ }[];
283
+ description?: string | undefined;
284
+ parallel?: number | undefined;
285
+ defaultModel?: string | undefined;
286
+ maxRepairsPerStep?: number | undefined;
287
+ contextBudgetChars?: number | undefined;
288
+ }, {
289
+ name: string;
290
+ steps: {
291
+ id: string;
292
+ kind: "command" | "agent" | "loop" | "gate";
293
+ model?: string | undefined;
294
+ goal?: string | undefined;
295
+ command?: string | undefined;
296
+ title?: string | undefined;
297
+ turns?: number | undefined;
298
+ prompt?: string | undefined;
299
+ timeoutMs?: number | undefined;
300
+ criteria?: string[] | undefined;
301
+ writes?: string[] | undefined;
302
+ reads?: string[] | undefined;
303
+ requires?: string[] | undefined;
304
+ neverFails?: boolean | undefined;
305
+ approvalPrompt?: string | undefined;
306
+ waitMs?: number | undefined;
307
+ verifyOnly?: boolean | undefined;
308
+ }[];
309
+ description?: string | undefined;
310
+ parallel?: number | undefined;
311
+ defaultModel?: string | undefined;
312
+ maxRepairsPerStep?: number | undefined;
313
+ contextBudgetChars?: number | undefined;
314
+ }>;
315
+ export type WorkflowStep = z.infer<typeof WorkflowStepSchema>;
316
+ export type WorkflowDef = z.infer<typeof WorkflowDefSchema>;
317
+ /** The parsed + validated workflow, ready for the runner. */
318
+ export interface LoadedWorkflow {
319
+ /** Absolute path of the workflow directory (workflow.yaml's dir). */
320
+ dir: string;
321
+ def: WorkflowDef;
322
+ /**
323
+ * Role-prompt text per agent step id, resolved from `prompt:` files at
324
+ * load time — fail-closed: a missing prompt file is a load error, never a
325
+ * silent empty role.
326
+ */
327
+ rolePrompts: Map<string, string>;
328
+ }
329
+ export declare const WORKFLOW_FILE = "workflow.yaml";
330
+ /** Run lifecycle status, persisted to run-state.json after every change. */
331
+ export type WorkflowRunStatus = 'running' | 'succeeded' | 'failed' | 'blocked';
332
+ /**
333
+ * The persisted run state. `done` carries exactly what the orchestrator's
334
+ * onProgress emits — id + summary (+ contract) — so a resume seeds
335
+ * orchestrate.initialDone and finished work is never redone.
336
+ */
337
+ export interface WorkflowRunState {
338
+ runId: string;
339
+ workflow: string;
340
+ status: WorkflowRunStatus;
341
+ startedAt: string;
342
+ updatedAt: string;
343
+ endedAt?: string;
344
+ /** One-line mission text (orientation for every step context). */
345
+ mission: string;
346
+ /** Cumulative accepted steps (orchestrator onProgress payload). */
347
+ done: Array<{
348
+ id: string;
349
+ summary: string;
350
+ contract?: string;
351
+ }>;
352
+ /** Failed step ids from the last attempt (informational on resume). */
353
+ failed: string[];
354
+ }
355
+ /** Exit-code contract — CI/cron can branch without parsing output. */
356
+ export declare const WORKFLOW_EXIT: {
357
+ readonly ok: 0;
358
+ /** A step failed (and its repairs); dependents were skipped. */
359
+ readonly stepFailed: 1;
360
+ /** The run paused at a gate awaiting approval — resumable. */
361
+ readonly blocked: 2;
362
+ /** The workflow file itself is invalid — never retriable as-is. */
363
+ readonly invalid: 3;
364
+ };
365
+ /**
366
+ * The convergence-loop seam for `kind: loop` steps — the composition point
367
+ * where a workflow DIRECTS delivery machinery. The caller owns loop
368
+ * construction (deliver wires its ConvergenceLoop / mission dispatch);
369
+ * the engine owns assembly (role instruction + dependency outputs + reads)
370
+ * and the graph semantics around it. `stepId` lets one seam serve several
371
+ * roles (solve vs repair vs a verify-only verdict).
372
+ */
373
+ export type LoopStepExecutor = (args: {
374
+ stepId: string;
375
+ /** Assembled instruction: role prompt + direct-dependency outputs + reads. */
376
+ prompt: string;
377
+ /** The step's model pin, when the workflow names one. */
378
+ model?: string;
379
+ /** A verify-only step: gates, no executor turns. */
380
+ verifyOnly?: boolean;
381
+ /** The step's turns bound, when the workflow names one. */
382
+ turns?: number;
383
+ }) => Promise<{
384
+ success: boolean;
385
+ summary: string;
386
+ turns?: number;
387
+ }>;
388
+ /**
389
+ * Per-step role-call seam for agent steps — lets a caller with its own
390
+ * executor rails (deliver's generator/judge plan) drive role steps through
391
+ * them instead of a raw model client, preserving its discipline.
392
+ */
393
+ export type StepCallExecutor = (step: {
394
+ id: string;
395
+ kind: StepKind;
396
+ }, prompt: string) => Promise<string>;
397
+ /** Narrow the raw parsed YAML into the typed def, with a flat error list. */
398
+ export declare function parseWorkflowDef(raw: unknown): WorkflowDef;
399
+ export {};
400
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Declarative workflow types — the static layer over the blackboard
3
+ * orchestrator.
4
+ *
5
+ * WHY THIS EXISTS. The orchestrator (src/delivery/task-orchestrator.ts)
6
+ * executes a task DAG with fresh minimal contexts, bounded repair, and
7
+ * resume-at-the-boundary — but the DAG it runs is MODEL-AUTHORED at runtime
8
+ * (decompose.ts), so every mission re-plans itself and nothing is a durable,
9
+ * inspectable, reusable asset. The measured alternative (Memdoor's
10
+ * team-solve, GVS5H's method: five Qwen3.8-27B roles beating a frontier
11
+ * single-call on LiveCodeBench Hard at 9.8 vs 8.0) is a workflow authored
12
+ * ONCE as data — fixed role prompts, `requires:` edges, deterministic
13
+ * command steps — pointed at whatever model is serving.
14
+ *
15
+ * This module is the file format: a `workflow.yaml` plus role-prompt files.
16
+ * The runner (runner.ts) maps these steps onto `orchestrate()` unchanged, so
17
+ * the graph semantics (dependency-triggered dispatch, skip cascade, minimal
18
+ * repair, resume) are the validated delivery semantics, not a reimplementation.
19
+ */
20
+ import { z } from 'zod';
21
+ /** Step kinds. Each maps to one execution seam in the runner. */
22
+ export const STEP_KINDS = ['agent', 'command', 'gate', 'loop'];
23
+ /**
24
+ * File-system discipline for step ids: lowercase slug, no dots — a repair
25
+ * chain namespaces links as `<id>.r0-…` (task-orchestrator.ts) and a dot in
26
+ * the base id would blur that boundary in run-state.json.
27
+ */
28
+ const STEP_ID_RE = /^[a-z0-9][a-z0-9-]*$/;
29
+ const WorkflowStepSchema = z.object({
30
+ id: z.string().regex(STEP_ID_RE, 'step id must be a lowercase slug (a-z, 0-9, -)'),
31
+ kind: z.enum(STEP_KINDS),
32
+ /** Human label for logs; defaults to the id. */
33
+ title: z.string().min(1).optional(),
34
+ /**
35
+ * Per-step model pin — a ModelPresets id (e.g. `qwen38-27b`, the local
36
+ * strata arm through the proxy). Absent = the workflow default, then the
37
+ * CLI pin, then the local fleet default. Mirrors the decompose C2 per-leaf
38
+ * model dial: the plan (here: the author) decides which rung each step pays.
39
+ */
40
+ model: z.string().min(1).optional(),
41
+ /**
42
+ * Path to the role-prompt file, relative to the workflow directory.
43
+ * Agent steps take `prompt` XOR `goal` — a file keeps the workflow.yaml
44
+ * reviewable while the role prompt stays a first-class artifact (the
45
+ * Memdoor layout: one file per step). Loop steps take the same pair:
46
+ * the role instruction the convergence loop executes against.
47
+ */
48
+ prompt: z.string().min(1).optional(),
49
+ /** Inline role prompt (alternative to `prompt:`). */
50
+ goal: z.string().min(1).optional(),
51
+ /** Run-directory files whose CONTENTS are inlined into the agent prompt. */
52
+ reads: z.array(z.string().min(1)).optional(),
53
+ /**
54
+ * Run-directory files this step produces. The first entry receives the
55
+ * agent reply (or the command's captured output); the rest are declared
56
+ * for the graph's write-collision check. Overwrite semantics — the file
57
+ * ledger is authoritative, chat is not. NOT for loop steps: a loop works
58
+ * the project tree (its DeliveryResult is the blackboard entry), not the
59
+ * run directory.
60
+ */
61
+ writes: z.array(z.string().min(1)).optional(),
62
+ /** DAG edges: step ids that must SUCCEED before this one runs. */
63
+ requires: z.array(z.string().min(1)).optional(),
64
+ /** Acceptance criteria lines — assembled into the agent prompt. */
65
+ criteria: z.array(z.string().min(1)).optional(),
66
+ /** command kind: shell line, run with cwd = the run directory. */
67
+ command: z.string().min(1).optional(),
68
+ /**
69
+ * command kind: capture the output but never fail the run — Memdoor's
70
+ * `test` step ("test-output.log (never fails the run)"). The verdict step
71
+ * is simply a command WITHOUT this flag: a nonzero exit fails the run.
72
+ */
73
+ neverFails: z.boolean().optional(),
74
+ /** command kind: kill the command after this many ms (default 120000). */
75
+ timeoutMs: z.number().int().positive().optional(),
76
+ /** gate kind: prompt shown when the run pauses for approval. */
77
+ approvalPrompt: z.string().min(1).optional(),
78
+ /**
79
+ * gate kind: maximum ms to WAIT for `uap workflow approve` before
80
+ * reporting the run as blocked (exit 2, resumable). 0 = never wait:
81
+ * pause immediately. Default 0 — approval is an operator act, not
82
+ * something a headless run should block a terminal on.
83
+ */
84
+ waitMs: z.number().int().nonnegative().optional(),
85
+ /**
86
+ * loop kind: max executor turns for the convergence loop this step runs
87
+ * (the caller's default applies when absent). The loop's exit condition
88
+ * is the project's real completion gates — this bounds the attempt
89
+ * budget, it does not replace verification.
90
+ */
91
+ turns: z.number().int().positive().optional(),
92
+ /**
93
+ * loop kind: run the gates ONLY — no executor turns. The independent
94
+ * final verdict after repair steps: a green repair is not "delivered"
95
+ * until a verify-only pass says so.
96
+ */
97
+ verifyOnly: z.boolean().optional(),
98
+ });
99
+ const WorkflowDefSchema = z.object({
100
+ name: z.string().regex(STEP_ID_RE, 'workflow name must be a lowercase slug'),
101
+ description: z.string().min(1).optional(),
102
+ /**
103
+ * Default model pin for steps that do not name one. The full resolution
104
+ * order is step.model → defaultModel → CLI --model → the local fleet
105
+ * default (see cli/workflow.ts) — the workflow names no model and stays
106
+ * portable across whatever arm is serving.
107
+ */
108
+ defaultModel: z.string().min(1).optional(),
109
+ /**
110
+ * Max concurrent INDEPENDENT steps (clamped to [1,16] by the
111
+ * orchestrator). Agent steps are stateless completions writing distinct
112
+ * files, so >1 is safe for the steps that declare disjoint `writes`; the
113
+ * loader rejects shared write targets across PARALLEL siblings.
114
+ * Default 1 — one local rail serves one completion at a time.
115
+ */
116
+ parallel: z.number().int().min(1).max(16).optional(),
117
+ /**
118
+ * Bounded fresh re-execution of a failed step before its dependents are
119
+ * blocked (orchestrator maxRepairsPerTask). Env hard-ceiling 5 applies.
120
+ * Default: the orchestrator's (1).
121
+ */
122
+ maxRepairsPerStep: z.number().int().min(0).max(5).optional(),
123
+ /** Assembled-prompt budget in chars (orchestrator contextBudgetChars). */
124
+ contextBudgetChars: z.number().int().positive().optional(),
125
+ steps: z.array(WorkflowStepSchema).min(1, 'a workflow needs at least one step'),
126
+ });
127
+ export const WORKFLOW_FILE = 'workflow.yaml';
128
+ /** Exit-code contract — CI/cron can branch without parsing output. */
129
+ export const WORKFLOW_EXIT = {
130
+ ok: 0,
131
+ /** A step failed (and its repairs); dependents were skipped. */
132
+ stepFailed: 1,
133
+ /** The run paused at a gate awaiting approval — resumable. */
134
+ blocked: 2,
135
+ /** The workflow file itself is invalid — never retriable as-is. */
136
+ invalid: 3,
137
+ };
138
+ /** Narrow the raw parsed YAML into the typed def, with a flat error list. */
139
+ export function parseWorkflowDef(raw) {
140
+ const parsed = WorkflowDefSchema.safeParse(raw);
141
+ if (!parsed.success) {
142
+ const lines = parsed.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`);
143
+ throw new Error(`invalid workflow definition:\n${lines.join('\n')}`);
144
+ }
145
+ return parsed.data;
146
+ }
147
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":"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"}
package/docs/INDEX.md CHANGED
@@ -50,6 +50,7 @@ Full map: **[The UAP Delivery Pipeline](guides/DELIVERY_PIPELINE.md)**.
50
50
  | [What UAP Does Automatically (compact)](guides/AUTOMATIC.md) | A shorter, trigger-by-trigger telling of the same ground as AUTOMATIC_FEATURES |
51
51
  | [**`uap deliver`**](guides/DELIVER.md) | The Build+QC harness — convergence loop to verified completion, tiered gates, CI/deploy feedback loop ⭐ |
52
52
  | [**Orchestrator & Hands-Free**](guides/ORCHESTRATOR.md) | The long-task autonomy layer — blackboard orchestrator, epic controller, completion ledger, auto-seed/resume; any model runs a huge build to 100% hands-free ⭐ |
53
+ | [Workflows](guides/WORKFLOWS.md) | Static `workflow.yaml` step DAGs (agent/command/gate/loop) over the orchestrator — role relays with cross-correction, deterministic verdicts, resumable runs; bundled team-solve + the deliver-driven team-solve-deliver (`uap deliver --team-solve`, auto on high/critical) |
53
54
  | [**UAP Across Two Agents & Uplift**](guides/TWO_AGENTS_AND_UPLIFT.md) | The whole line applied seam-by-seam to opencode+Qwen3.6 (local) and Claude Code+Opus 4.8 (cloud), plus the benchmark that measures UAP uplift — diagrams + `benchmarks/suites/swe-bench-pro/` config |
54
55
  | [Memory](guides/MEMORY.md) | The 4-tier memory system, write-gates, semantic recall |
55
56
  | [MCP Router](guides/MCP_ROUTER.md) | Token-optimizing tool proxy + FTS5 output compression |
@@ -0,0 +1,99 @@
1
+ # ADR 0013: Declarative workflow layer — static step DAGs over the orchestrator
2
+
3
+ - Status: Accepted (amended by ADR 0014: a fourth step kind `loop`, the
4
+ caller-injected convergence-loop seam, and the team-solve-deliver
5
+ posture that `uap deliver` auto-engages on high/critical missions)
6
+ - Date: 2026-11-03
7
+ - Spec: `uap workflow` (src/workflow/, src/cli/workflow.ts,
8
+ examples/workflows/team-solve/, docs/guides/WORKFLOWS.md).
9
+ - Supersedes: none — additive layer; `orchestrate()` and every existing
10
+ delivery surface are untouched.
11
+
12
+ ## Context
13
+
14
+ UAP's task DAG machinery (ADR 0009's blackboard orchestrator) executes a
15
+ graph of fresh-context tasks with bounded repair and resume — but the DAG it
16
+ runs is model-authored at runtime (decompose.ts). Every mission re-plans
17
+ itself; nothing about the plan is a durable, reviewable, reusable asset.
18
+
19
+ The measured external counterpoint is GVS5H (Lee, 2026; reproduced by
20
+ Memdoor's team-solve): five Qwen3.8-27B roles on a STATIC graph — planner,
21
+ two parallel ideators from different algorithm families, solver, reviewer,
22
+ repairer — talking only through files, judged only by a deterministic
23
+ harness, matched the authors' own harness on LiveCodeBench Hard (9.8/10 vs
24
+ 9.6; a frontier single-call at 8.0). The claim is structure, not model
25
+ class: the graph and the ledger discipline carried the result.
26
+
27
+ UAP had the engine but not the declarative layer: no way to author the graph
28
+ as data, pin models per step, run deterministic command steps as first-class
29
+ nodes, or resume a blocked run — nothing a CI or a cron could execute as a
30
+ file.
31
+
32
+ ## Decision
33
+
34
+ 1. **A workflow is data.** `.uap/workflows/<name>/workflow.yaml` (zod-
35
+ validated) + role-prompt files; search roots project → home → bundled
36
+ `examples/` (first hit wins). The loader is fail-closed: unique ids, known
37
+ `requires:`, cycle rejection (Kahn — topoOrder's fail-soft is right for
38
+ model-authored plans, wrong for a static file), kind/field confusion,
39
+ parallel write collision (graph-safety FALSE INDEPENDENCE, checked at
40
+ authoring time), run-dir path escapes, load-time model-pin validation,
41
+ reserved-path rejection.
42
+ 2. **`orchestrate()` is the engine, unchanged.** The runner maps three step
43
+ kinds onto its existing seams: `agent` (fresh single completion; role
44
+ prompt + direct-dependency summaries + inlined `reads:` files; the reply
45
+ IS `writes[0]`), `command` (spawnSync exit-code verdict; `neverFails`
46
+ evidence mode), `gate` (approval file → BLOCKED exit 2, resumable). No
47
+ graph logic is reimplemented; dependency-triggered dispatch, skip
48
+ cascade, bounded repair, and resume-at-the-boundary are inherited and
49
+ already tested by the orchestrator suite.
50
+ 3. **Files, not chat.** Steps communicate only through the run directory
51
+ (`.uap/workflow-runs/<runId>/`); each agent step starts from a fresh
52
+ context assembled from the file ledger. One deliberate divergence from
53
+ Memdoor: one reply → one file (overwrite semantics), not append-to-notes;
54
+ per-step files keep the ledger unambiguous.
55
+ 4. **Steps name no model.** Pin ladder: step → workflow `defaultModel` →
56
+ `--model` → the local fleet default (`qwen38-27b`, the strata arm through
57
+ the proxy). A workflow is portable across arms and endpoints without
58
+ edits; `--endpoint` covers non-local OpenAI-compatible servers.
59
+ 5. **Exit contract for CI/cron:** 0 ok · 1 step failed · 2 gate blocked
60
+ (resumable) · 3 invalid workflow. A real step defect never hides behind a
61
+ blocked gate (failed > blocked precedence).
62
+ 6. **Trust boundary is the npm-scripts class.** `command:` lines run a shell
63
+ as the invoking user; `--input` bundles are dereferenced on copy (no
64
+ symlink escapes) and cannot plant run-state.json; model replies are data
65
+ (prompts and files, never a shell); model-written code executes only via
66
+ authored commands, exactly like `deliver` gate scripts. Untrusted input
67
+ composes with `uap sandbox --net off` externally — no sandbox stub inside
68
+ the layer.
69
+ 7. **Validation is a gated option, not a claim.** O7 in
70
+ docs/plans/model-performance-proof-options.md: paired three-arm (single-
71
+ shot / deliver gate loop / team-solve workflow) on the same local model;
72
+ starts only on an explicit go, with the house rules (paired rates,
73
+ bootstrap CI, McNemar, discrimination status).
74
+
75
+ ## Alternatives considered
76
+
77
+ - **Extend decompose.ts with static plan files** — rejected: couples the
78
+ declarative format to the model-planning path; a static workflow needs
79
+ different validation (fail-closed, not fail-soft) and different authoring
80
+ ergonomics than a runtime plan.
81
+ - **Byte-port Memdoor's mario engine** — rejected: UAP already owns a
82
+ validated orchestrator with repair/resume semantics; a second engine would
83
+ duplicate them. The port keeps the METHOD (roles, ledger discipline,
84
+ deterministic verdict), not the bytes.
85
+ - **Build workflows on droids/Task** — rejected: droid delegation is runtime
86
+ ad-hoc (the orchestrating model decides); a workflow must be executable
87
+ headless from a file, by CI, without any model in the driver's seat.
88
+
89
+ ## Consequences
90
+
91
+ - `uap workflow list|show|run|status|resume|approve` joins the command
92
+ surface (additive; registration is one lazy-map entry + one block).
93
+ - team-solve ships as the bundled flagship example and the O7 subject; its
94
+ published numbers belong to the authors (their harness, their host).
95
+ - Known follow-ups (tracked, deliberate): a shared preset resolver for
96
+ deliver + workflow (the pin ladder's resolution discipline is currently
97
+ expressed twice); dashboard integration over run-state.json's stable JSON
98
+ seam; gate repair-attempt accounting (an unapproved gate consumes its
99
+ step's repair budget while waiting).