@cleocode/contracts 2026.5.121 → 2026.5.123

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 (128) hide show
  1. package/dist/__tests__/completion-operation-contracts.test.d.ts +8 -0
  2. package/dist/__tests__/completion-operation-contracts.test.d.ts.map +1 -0
  3. package/dist/__tests__/completion-operation-contracts.test.js +114 -0
  4. package/dist/__tests__/completion-operation-contracts.test.js.map +1 -0
  5. package/dist/__tests__/operation-envelope-validation.test.d.ts +8 -0
  6. package/dist/__tests__/operation-envelope-validation.test.d.ts.map +1 -0
  7. package/dist/__tests__/operation-envelope-validation.test.js +105 -0
  8. package/dist/__tests__/operation-envelope-validation.test.js.map +1 -0
  9. package/dist/__tests__/satisfies-atom.test.d.ts +24 -0
  10. package/dist/__tests__/satisfies-atom.test.d.ts.map +1 -0
  11. package/dist/__tests__/satisfies-atom.test.js +320 -0
  12. package/dist/__tests__/satisfies-atom.test.js.map +1 -0
  13. package/dist/__tests__/validator-types.test.d.ts +13 -0
  14. package/dist/__tests__/validator-types.test.d.ts.map +1 -0
  15. package/dist/__tests__/validator-types.test.js +293 -0
  16. package/dist/__tests__/validator-types.test.js.map +1 -0
  17. package/dist/__tests__/workgraph-context-scaffold-contracts.test.d.ts +8 -0
  18. package/dist/__tests__/workgraph-context-scaffold-contracts.test.d.ts.map +1 -0
  19. package/dist/__tests__/workgraph-context-scaffold-contracts.test.js +188 -0
  20. package/dist/__tests__/workgraph-context-scaffold-contracts.test.js.map +1 -0
  21. package/dist/__tests__/workgraph-operation-contracts.test.d.ts +8 -0
  22. package/dist/__tests__/workgraph-operation-contracts.test.d.ts.map +1 -0
  23. package/dist/__tests__/workgraph-operation-contracts.test.js +115 -0
  24. package/dist/__tests__/workgraph-operation-contracts.test.js.map +1 -0
  25. package/dist/config.d.ts +56 -0
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/data-accessor.d.ts +194 -0
  28. package/dist/data-accessor.d.ts.map +1 -1
  29. package/dist/data-accessor.js.map +1 -1
  30. package/dist/dispatch/__tests__/docs-update-registry.test.d.ts +2 -0
  31. package/dist/dispatch/__tests__/docs-update-registry.test.d.ts.map +1 -0
  32. package/dist/dispatch/__tests__/docs-update-registry.test.js +27 -0
  33. package/dist/dispatch/__tests__/docs-update-registry.test.js.map +1 -0
  34. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.d.ts +2 -0
  35. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.d.ts.map +1 -0
  36. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.js +265 -0
  37. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.js.map +1 -0
  38. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  39. package/dist/dispatch/operations-registry.js +487 -7
  40. package/dist/dispatch/operations-registry.js.map +1 -1
  41. package/dist/evidence-atom-schema.d.ts +91 -0
  42. package/dist/evidence-atom-schema.d.ts.map +1 -1
  43. package/dist/evidence-atom-schema.js +162 -1
  44. package/dist/evidence-atom-schema.js.map +1 -1
  45. package/dist/exit-codes.d.ts +41 -1
  46. package/dist/exit-codes.d.ts.map +1 -1
  47. package/dist/exit-codes.js +40 -0
  48. package/dist/exit-codes.js.map +1 -1
  49. package/dist/index.d.ts +17 -9
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +12 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/operation-envelope-validation.d.ts +79 -0
  54. package/dist/operation-envelope-validation.d.ts.map +1 -0
  55. package/dist/operation-envelope-validation.js +219 -0
  56. package/dist/operation-envelope-validation.js.map +1 -0
  57. package/dist/operations/__tests__/agent-operation-contract.test.d.ts +2 -0
  58. package/dist/operations/__tests__/agent-operation-contract.test.d.ts.map +1 -0
  59. package/dist/operations/__tests__/agent-operation-contract.test.js +128 -0
  60. package/dist/operations/__tests__/agent-operation-contract.test.js.map +1 -0
  61. package/dist/operations/__tests__/tasks-input-contracts.test.js +15 -0
  62. package/dist/operations/__tests__/tasks-input-contracts.test.js.map +1 -1
  63. package/dist/operations/agent-operation-contract.d.ts +227 -0
  64. package/dist/operations/agent-operation-contract.d.ts.map +1 -0
  65. package/dist/operations/agent-operation-contract.js +38 -0
  66. package/dist/operations/agent-operation-contract.js.map +1 -0
  67. package/dist/operations/docs.d.ts +19 -1
  68. package/dist/operations/docs.d.ts.map +1 -1
  69. package/dist/operations/docs.js +20 -0
  70. package/dist/operations/docs.js.map +1 -1
  71. package/dist/operations/focus.d.ts +1 -1
  72. package/dist/operations/index.d.ts +1 -0
  73. package/dist/operations/index.d.ts.map +1 -1
  74. package/dist/operations/index.js +1 -0
  75. package/dist/operations/index.js.map +1 -1
  76. package/dist/operations/orchestrate.d.ts +66 -0
  77. package/dist/operations/orchestrate.d.ts.map +1 -1
  78. package/dist/operations/tasks.d.ts +617 -11
  79. package/dist/operations/tasks.d.ts.map +1 -1
  80. package/dist/operations/tasks.js +22 -3
  81. package/dist/operations/tasks.js.map +1 -1
  82. package/dist/task-record.d.ts +15 -0
  83. package/dist/task-record.d.ts.map +1 -1
  84. package/dist/task.d.ts +52 -5
  85. package/dist/task.d.ts.map +1 -1
  86. package/dist/task.js.map +1 -1
  87. package/dist/tasks.d.ts +944 -0
  88. package/dist/tasks.d.ts.map +1 -1
  89. package/dist/tasks.js +198 -1
  90. package/dist/tasks.js.map +1 -1
  91. package/dist/validator/index.d.ts +375 -0
  92. package/dist/validator/index.d.ts.map +1 -0
  93. package/dist/validator/index.js +162 -0
  94. package/dist/validator/index.js.map +1 -0
  95. package/dist/workgraph.d.ts +1950 -0
  96. package/dist/workgraph.d.ts.map +1 -0
  97. package/dist/workgraph.js +449 -0
  98. package/dist/workgraph.js.map +1 -0
  99. package/package.json +10 -2
  100. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  101. package/src/__tests__/completion-operation-contracts.test.ts +154 -0
  102. package/src/__tests__/operation-envelope-validation.test.ts +140 -0
  103. package/src/__tests__/satisfies-atom.test.ts +383 -0
  104. package/src/__tests__/validator-types.test.ts +359 -0
  105. package/src/__tests__/workgraph-context-scaffold-contracts.test.ts +232 -0
  106. package/src/__tests__/workgraph-operation-contracts.test.ts +151 -0
  107. package/src/config.ts +58 -0
  108. package/src/data-accessor.ts +213 -0
  109. package/src/dispatch/__tests__/docs-update-registry.test.ts +35 -0
  110. package/src/dispatch/__tests__/operation-envelope-snapshots.test.ts +283 -0
  111. package/src/dispatch/operations-registry.ts +502 -7
  112. package/src/evidence-atom-schema.ts +188 -1
  113. package/src/exit-codes.ts +42 -0
  114. package/src/index.ts +230 -2
  115. package/src/operation-envelope-validation.ts +319 -0
  116. package/src/operations/__tests__/agent-operation-contract.test.ts +161 -0
  117. package/src/operations/__tests__/tasks-input-contracts.test.ts +30 -0
  118. package/src/operations/agent-operation-contract.ts +253 -0
  119. package/src/operations/docs.ts +27 -7
  120. package/src/operations/focus.ts +1 -1
  121. package/src/operations/index.ts +1 -0
  122. package/src/operations/orchestrate.ts +71 -0
  123. package/src/operations/tasks.ts +662 -15
  124. package/src/task-record.ts +16 -0
  125. package/src/task.ts +54 -5
  126. package/src/tasks.ts +558 -0
  127. package/src/validator/index.ts +385 -0
  128. package/src/workgraph.ts +1153 -0
@@ -28,9 +28,20 @@ import type { TaskPriority, TaskType } from '../task.js';
28
28
  import type { TaskRecord } from '../task-record.js';
29
29
  import type { ExternalTask, ExternalTaskLink, ReconcileResult } from '../task-sync.js';
30
30
  import type {
31
+ CompletionEvaluateParams,
32
+ CompletionEvaluateResult,
33
+ CompletionExplainParams,
34
+ CompletionExplainResult,
35
+ CompletionListParams,
36
+ CompletionListResult,
37
+ CompletionProjectionRepairParams,
38
+ CompletionProjectionRepairResult,
31
39
  TaskComplexityFactor,
32
40
  TaskDependsResult,
33
41
  TaskLabelInfo,
42
+ TaskMutationDryRunSummary,
43
+ TaskMutationEnvelope,
44
+ TaskMutationWarning,
34
45
  TaskPlanResult,
35
46
  TaskTreeNode,
36
47
  TaskView,
@@ -179,13 +190,15 @@ export interface TasksCurrentResult {
179
190
  sessionId?: string;
180
191
  }
181
192
 
182
- // tasks.show (with optional history/ivtrHistory flags)
193
+ // tasks.show (with optional history/ivtrHistory/relations flags)
183
194
  export interface TasksShowParams {
184
195
  taskId: string;
185
196
  /** When true, include task change history. */
186
197
  history?: boolean;
187
198
  /** When true, include IVTR phase history. */
188
199
  ivtrHistory?: boolean;
200
+ /** When true, include expanded relation/doc ID lists alongside compact counts. */
201
+ relations?: boolean;
189
202
  }
190
203
 
191
204
  /**
@@ -215,6 +228,19 @@ export interface TaskShowAttachmentEntry {
215
228
  kind: string;
216
229
  }
217
230
 
231
+ export interface TaskShowRelationsEntry {
232
+ /** Direct dependency IDs. */
233
+ depends: string[];
234
+ /** Explicit blockedBy entries from the task record. */
235
+ blockedBy: string[];
236
+ /** Non-dependency task relation links. */
237
+ relates: Array<{ taskId: string; type: string; reason?: string }>;
238
+ /** Direct child task IDs. */
239
+ children: string[];
240
+ /** Linked docs attachment IDs/slugs. */
241
+ docs: Array<{ attachmentId: string; slug?: string; type?: DocsType; kind: string }>;
242
+ }
243
+
218
244
  /**
219
245
  * Result of `tasks.show` — the full task record plus its canonical view
220
246
  * projection. `view` is null when the task has no lifecycle pipeline.
@@ -237,6 +263,40 @@ export interface TasksShowResult {
237
263
  * @task T9966
238
264
  */
239
265
  attachments: TaskShowAttachmentEntry[];
266
+ /**
267
+ * Acceptance-criterion rows hydrated from the `task_acceptance_criteria`
268
+ * table (T10502). Each entry carries the stable UUID `id`, the
269
+ * `AC<ordinal>` alias, the ordinal itself, and the canonical AC text.
270
+ *
271
+ * Optional — undefined when the task has no rows in the table (e.g.
272
+ * legacy tasks not yet backfilled by T10505). Consumers should fall
273
+ * back to `task.acceptance` (the legacy JSON string) in that case.
274
+ *
275
+ * @task T10508
276
+ * @epic T10381
277
+ */
278
+ acRows?: TaskShowAcRowEntry[];
279
+ /** Expanded relation/doc lists returned only when `cleo show --relations` is requested. */
280
+ relations?: TaskShowRelationsEntry;
281
+ }
282
+
283
+ /**
284
+ * One acceptance-criterion entry in {@link TasksShowResult.acRows}.
285
+ *
286
+ * Mirrors the `AcDetail` shape from `@cleocode/core/tasks/show.js` but
287
+ * lives in contracts so dispatch-layer consumers don't depend on core.
288
+ *
289
+ * @task T10508
290
+ */
291
+ export interface TaskShowAcRowEntry {
292
+ /** UUIDv4 stable identifier, immutable for the AC's lifetime. */
293
+ id: string;
294
+ /** Display alias derived from ordinal — `AC1`, `AC2`, etc. */
295
+ alias: string;
296
+ /** 1-based ordinal — never reused per task (gaps remain on shrink). */
297
+ ordinal: number;
298
+ /** The AC statement text. Structured gates round-trip as JSON. */
299
+ text: string;
240
300
  }
241
301
 
242
302
  // tasks.tree dispatch params (with optional withBlockers flag — dispatch alias)
@@ -303,6 +363,52 @@ export interface TasksDependsParams {
303
363
  */
304
364
  export type TasksDependsResult = TaskDependsResult;
305
365
 
366
+ // tasks.slice
367
+ /** Localized task/workgraph node returned by `tasks.slice`. */
368
+ export interface TasksSliceNode {
369
+ id: string;
370
+ title: string;
371
+ status: TaskStatus;
372
+ type?: TaskType;
373
+ priority?: TaskPriority;
374
+ /** Canonical wire field for parent task ID (ADR-057 D2). */
375
+ parent?: string;
376
+ children: string[];
377
+ depends: string[];
378
+ dependents: string[];
379
+ depth: number;
380
+ }
381
+
382
+ /** Params for `tasks.slice` — fixed-radius local WorkGraph context around a task. */
383
+ export interface TasksSliceParams {
384
+ taskId: string;
385
+ /** Slice direction. `around` returns upstream, downstream, and siblings. Defaults to `around`. */
386
+ direction?: 'upstream' | 'downstream' | 'around';
387
+ /** Back-compat alias for dependency traversal depth. */
388
+ radius?: number;
389
+ /** Dependency traversal depth for upstream/downstream. Defaults to 1. */
390
+ depth?: number;
391
+ /** Maximum nodes per returned section. Defaults to unbounded. */
392
+ budget?: number;
393
+ /** Include direct non-dependency `relates` neighbors in a `related` bucket. */
394
+ includeRelates?: boolean;
395
+ }
396
+
397
+ /** Result of `tasks.slice` — center task, deps, dependents, siblings, and optional relations. */
398
+ export interface TasksSliceResult {
399
+ taskId: string;
400
+ direction: 'upstream' | 'downstream' | 'around';
401
+ depth: number;
402
+ radius: number;
403
+ budget?: number;
404
+ includeRelates: boolean;
405
+ center: TasksSliceNode;
406
+ upstream: TasksSliceNode[];
407
+ downstream: TasksSliceNode[];
408
+ siblings: TasksSliceNode[];
409
+ related?: TasksSliceNode[];
410
+ }
411
+
306
412
  // tasks.analyze dispatch params (with optional taskId and tierLimit)
307
413
  export interface TasksAnalyzeQueryParams {
308
414
  taskId?: string;
@@ -318,6 +424,211 @@ export interface TasksAnalyzeQueryParams {
318
424
  */
319
425
  export type TasksAnalyzeQueryResult = TaskAnalysisResult & { tierLimit: number };
320
426
 
427
+ // ---------------------------------------------------------------------------
428
+ // tasks.context — Task-scoped context pack with token budget (T10629)
429
+ // ---------------------------------------------------------------------------
430
+
431
+ /** Params for `tasks.context` — bounded task context pack. */
432
+ export interface TasksContextParams {
433
+ /** Task ID to build context for. */
434
+ taskId: string;
435
+ /**
436
+ * Scope mode: `saga` resolves member-epic rollup + ready frontier across
437
+ * all members; `epic` resolves child-task rollup + ready frontier for the
438
+ * epic. When omitted or `undefined`, behaves as a single-task context pack
439
+ * (T10629 baseline). @defaultValue undefined (task-only)
440
+ */
441
+ scope?: 'saga' | 'epic';
442
+ /** Maximum token budget for the returned pack. @defaultValue 1500 */
443
+ budgetTokens?: number;
444
+ /** Include acceptance criteria in the pack. @defaultValue true */
445
+ includeAcceptance?: boolean;
446
+ /** Include blocker summary in the pack. @defaultValue true */
447
+ includeBlockers?: boolean;
448
+ /** Include attached docs in the pack. @defaultValue true */
449
+ includeDocs?: boolean;
450
+ /** Include graph edges (upstream/downstream/siblings/related) in the pack. @defaultValue true */
451
+ includeEdges?: boolean;
452
+ /** Include recent activity (audit log) in the pack. @defaultValue true */
453
+ includeActivity?: boolean;
454
+ /** Maximum number of recent activity events. @defaultValue 10 */
455
+ activityLimit?: number;
456
+ /** Maximum depth for graph edges when includeEdges is true. @defaultValue 1 */
457
+ edgeDepth?: number;
458
+ }
459
+
460
+ /** A single omitted section with reason and count. */
461
+ export interface TasksContextOmission {
462
+ /** Section path that was omitted (e.g. "acceptance", "blockers", "docs", "edges.upstream", "activity"). */
463
+ path: string;
464
+ /** Reason for omission. */
465
+ reason: 'budget_exceeded' | 'not_requested' | 'not_available';
466
+ /** Human-readable explanation. */
467
+ message: string;
468
+ /** Number of items omitted, when countable. */
469
+ count?: number;
470
+ /**
471
+ * CLI command to expand this section, when an expansion path exists
472
+ * (e.g. `"cleo orchestrate ready T10547"`, `"cleo saga rollup T10538"`).
473
+ * @task T10630
474
+ */
475
+ expansionCommand?: string;
476
+ }
477
+
478
+ /** Budget accounting for a task context pack. */
479
+ export interface TasksContextBudget {
480
+ /** Caller-provided token budget. */
481
+ tokenBudget: number;
482
+ /** Estimated tokens consumed by the returned payload. */
483
+ estimatedTokens: number;
484
+ /** Remaining tokens after payload selection. */
485
+ remainingTokens: number;
486
+ /** True when one or more sections were truncated or omitted. */
487
+ truncated: boolean;
488
+ }
489
+
490
+ /** Single AC entry in the context pack. */
491
+ export interface TasksContextAcceptanceEntry {
492
+ /** AC alias (e.g. "AC1") or UUID. */
493
+ alias: string;
494
+ /** Full acceptance criterion text. */
495
+ text: string;
496
+ /** Whether this AC has evidence bindings. */
497
+ hasEvidence?: boolean;
498
+ }
499
+
500
+ /** Single blocker entry in the context pack. */
501
+ export interface TasksContextBlockerEntry {
502
+ /** Blocking task ID. */
503
+ taskId: string;
504
+ /** Title of the blocking task. */
505
+ title: string;
506
+ /** Status of the blocking task. */
507
+ status: string;
508
+ /** Blocker kind: dependency or gate. */
509
+ kind: 'dependency' | 'gate';
510
+ }
511
+
512
+ /** Single doc entry in the context pack. */
513
+ export interface TasksContextDocEntry {
514
+ /** Doc slug or attachment identifier. */
515
+ slug: string;
516
+ /** Doc type. */
517
+ type: string;
518
+ /** Human-readable title or description. */
519
+ title: string;
520
+ }
521
+
522
+ /** Single activity event in the context pack. */
523
+ export interface TasksContextActivityEvent {
524
+ /** ISO timestamp. */
525
+ timestamp: string;
526
+ /** Action type. */
527
+ action: string;
528
+ /** Actor who performed the action. */
529
+ actor?: string;
530
+ /** Free-form details. */
531
+ details?: string;
532
+ }
533
+
534
+ /**
535
+ * Rollup summary for a saga or epic scope.
536
+ * @task T10630
537
+ */
538
+ export interface TasksScopeRollup {
539
+ /** Total number of member epics (saga) or child tasks (epic). */
540
+ total: number;
541
+ done: number;
542
+ active: number;
543
+ blocked: number;
544
+ pending: number;
545
+ /** Completion percentage (0–100). */
546
+ completionPct: number;
547
+ }
548
+
549
+ /**
550
+ * A single member epic entry in a saga context pack.
551
+ * @task T10630
552
+ */
553
+ export interface TasksScopeMember {
554
+ /** Epic task ID. */
555
+ epicId: string;
556
+ /** Epic title. */
557
+ title: string;
558
+ /** Current status. */
559
+ status: string;
560
+ }
561
+
562
+ /**
563
+ * A single ready-frontier entry in a saga/epic context pack.
564
+ * @task T10630
565
+ */
566
+ export interface TasksScopeReadyEntry {
567
+ /** Task ID that is ready to execute. */
568
+ id: string;
569
+ /** Task title. */
570
+ title: string;
571
+ /** Priority level. */
572
+ priority: string;
573
+ /** Declared dependency IDs. */
574
+ depends: string[];
575
+ }
576
+
577
+ /** Result of `tasks.context` — bounded task context pack with omission tracking. */
578
+ export interface TasksContextResult {
579
+ /** Task ID this pack describes. */
580
+ taskId: string;
581
+ /** ISO timestamp when the pack was generated. */
582
+ generatedAt: string;
583
+ /** Budget accounting. */
584
+ budget: TasksContextBudget;
585
+ /** Task identity (title, status, type, priority, description). */
586
+ identity: {
587
+ id: string;
588
+ title: string;
589
+ status: string;
590
+ type?: string;
591
+ priority?: string;
592
+ description?: string;
593
+ parentId?: string | null;
594
+ };
595
+ /** Acceptance criteria, when requested and within budget. */
596
+ acceptance?: TasksContextAcceptanceEntry[];
597
+ /** Blockers, when requested and within budget. */
598
+ blockers?: TasksContextBlockerEntry[];
599
+ /** Attached docs, when requested and within budget. */
600
+ docs?: TasksContextDocEntry[];
601
+ /** Graph edges, when requested and within budget. */
602
+ edges?: {
603
+ upstream: TasksSliceNode[];
604
+ downstream: TasksSliceNode[];
605
+ siblings: TasksSliceNode[];
606
+ related?: TasksSliceNode[];
607
+ };
608
+ /** Recent activity events, when requested and within budget. */
609
+ activity?: TasksContextActivityEvent[];
610
+ /**
611
+ * Saga/epic rollup summary — present when `scope` is `'saga'` or
612
+ * `'epic'` and rollup data is available. @task T10630
613
+ */
614
+ rollup?: TasksScopeRollup;
615
+ /**
616
+ * Saga member epics — present when `scope` is `'saga'` and member data
617
+ * is available. @task T10630
618
+ */
619
+ members?: TasksScopeMember[];
620
+ /**
621
+ * Ready frontier — parallel-safe tasks that can be dispatched now.
622
+ * Present when `scope` is `'saga'` or `'epic'` and ready data is
623
+ * available. @task T10630
624
+ */
625
+ readyFrontier?: TasksScopeReadyEntry[];
626
+ /** Omitted/truncated sections with reasons and counts. */
627
+ omissions: TasksContextOmission[];
628
+ /** Expansion hints for omitted sections. */
629
+ expansionHints: Record<string, string>;
630
+ }
631
+
321
632
  // tasks.deps.validate
322
633
  /**
323
634
  * Parameters for `tasks.deps.validate`.
@@ -477,6 +788,12 @@ export interface TasksRelatesParams {
477
788
  taskId: string;
478
789
  mode?: 'suggest' | 'discover';
479
790
  threshold?: number;
791
+ /** List edges where taskId is the source, target, or either side. Defaults to both. */
792
+ direction?: 'out' | 'in' | 'both';
793
+ /** Filter by relation type, or `depends`/`depends_on` for dependency edges. */
794
+ type?: string;
795
+ /** Include scheduler dependency edges in list output. Defaults to true. */
796
+ includeDependencies?: boolean;
480
797
  }
481
798
  /**
482
799
  * Result of `tasks.relates` — task relations list.
@@ -487,11 +804,55 @@ export interface TasksRelatesResult {
487
804
  /** The task ID whose relations were loaded. */
488
805
  taskId: string;
489
806
  /** All relations for this task. */
490
- relations: Array<{ taskId: string; type: string; reason?: string }>;
807
+ relations: Array<{
808
+ taskId: string;
809
+ type: string;
810
+ reason?: string;
811
+ direction?: 'out' | 'in';
812
+ source?: 'relation' | 'dependency';
813
+ ready?: boolean;
814
+ status?: string;
815
+ }>;
816
+ /** Direction filter applied by list output. */
817
+ direction?: 'out' | 'in' | 'both';
491
818
  /** Total number of relations. */
492
819
  count: number;
493
820
  }
494
821
 
822
+ // tasks.relates.add-batch
823
+ export interface TasksRelatesAddBatchEntry {
824
+ taskId?: string;
825
+ from?: string;
826
+ relatedId?: string;
827
+ to?: string;
828
+ type: string;
829
+ reason?: string;
830
+ }
831
+ export interface TasksRelatesAddBatchParams {
832
+ relations?: TasksRelatesAddBatchEntry[];
833
+ edges?: TasksRelatesAddBatchEntry[];
834
+ dryRun?: boolean;
835
+ reasonWaiver?: string;
836
+ }
837
+ export interface TasksRelatesAddBatchResult {
838
+ dryRun: boolean;
839
+ validatedCount: number;
840
+ created: number;
841
+ wouldCreate: number;
842
+ relations: Array<{
843
+ from: string;
844
+ to: string;
845
+ type: string;
846
+ reason?: string;
847
+ waivedReason?: boolean;
848
+ }>;
849
+ warnings: Array<{
850
+ code: string;
851
+ message: string;
852
+ edge?: { from: string; to: string; type: string };
853
+ }>;
854
+ }
855
+
495
856
  // tasks.complexity.estimate
496
857
  export interface TasksComplexityEstimateParams {
497
858
  taskId: string;
@@ -599,6 +960,14 @@ export interface TasksSyncLinksRemoveResult {
599
960
  export interface TasksCancelParams {
600
961
  taskId: string;
601
962
  reason?: string;
963
+ /** Explicit child handling mode. Defaults to 'block' so propagation is never implicit. */
964
+ children?: 'block' | 'cascade' | 'orphan';
965
+ /** Operator waiver for large cascade cancellation. */
966
+ force?: boolean;
967
+ /** Large-subtree cascade guard threshold. Defaults to 10 descendants. */
968
+ cascadeThreshold?: number;
969
+ /** Config-level cascade permission. Defaults to true. */
970
+ allowCascade?: boolean;
602
971
  }
603
972
  /**
604
973
  * Result of `tasks.cancel` — cancellation confirmation.
@@ -623,6 +992,12 @@ export interface TasksCancelResult {
623
992
  * @defaultValue undefined
624
993
  */
625
994
  alreadyCancelled?: boolean;
995
+ /** Explicit child handling mode applied by the operation. */
996
+ childStrategy?: 'block' | 'cascade' | 'orphan';
997
+ /** Child or descendant tasks affected by cascade/orphan handling. */
998
+ affectedTasks?: string[];
999
+ /** Count of affected child/descendant tasks. */
1000
+ affectedCount?: number;
626
1001
  }
627
1002
 
628
1003
  // tasks.restore (with from routing: done → reopen, archived → unarchive)
@@ -719,6 +1094,57 @@ export interface TasksRelatesAddResult {
719
1094
  added: boolean;
720
1095
  }
721
1096
 
1097
+ export interface TasksRelatesAddBatchEntry {
1098
+ /** Source task ID. */
1099
+ taskId?: string;
1100
+ /** Source task ID alias accepted by file payloads. */
1101
+ from?: string;
1102
+ /** Target task ID. */
1103
+ relatedId?: string;
1104
+ /** Target task ID alias accepted by file payloads. */
1105
+ to?: string;
1106
+ /** Relation type: related|blocks|duplicates|absorbs|fixes|extends|supersedes|groups. */
1107
+ type: string;
1108
+ /** Audit reason for creating this advisory relation. Required unless reasonWaiver is set. */
1109
+ reason?: string;
1110
+ }
1111
+
1112
+ export interface TasksRelatesAddBatchParams {
1113
+ /** Relation edges to add atomically. */
1114
+ relations?: TasksRelatesAddBatchEntry[];
1115
+ /** Relation edges alias for WorkGraph-flavored callers. */
1116
+ edges?: TasksRelatesAddBatchEntry[];
1117
+ /** Validate and report without writing. */
1118
+ dryRun?: boolean;
1119
+ /** Explicit reason-waiver text applied to any edge missing an edge-level reason. */
1120
+ reasonWaiver?: string;
1121
+ }
1122
+
1123
+ export interface TasksRelatesAddBatchResult {
1124
+ /** Whether this dispatch only previewed writes. */
1125
+ dryRun: boolean;
1126
+ /** Validated relation count. */
1127
+ validatedCount: number;
1128
+ /** Number of relation rows inserted or previewed. */
1129
+ created: number;
1130
+ /** Dry-run prediction of rows that would be inserted. */
1131
+ wouldCreate: number;
1132
+ /** Accepted normalized relation edges. */
1133
+ relations: Array<{
1134
+ from: string;
1135
+ to: string;
1136
+ type: string;
1137
+ reason?: string;
1138
+ waivedReason?: boolean;
1139
+ }>;
1140
+ /** Non-fatal audit notes, including explicit missing-reason waivers. */
1141
+ warnings: Array<{
1142
+ code: string;
1143
+ message: string;
1144
+ edge?: { from: string; to: string; type: string };
1145
+ }>;
1146
+ }
1147
+
722
1148
  // tasks.relates.remove
723
1149
  export interface TasksRelatesRemoveParams {
724
1150
  /** Source task ID. */
@@ -789,14 +1215,68 @@ export interface TasksAddBatchParams {
789
1215
  * Result of `tasks.add-batch`.
790
1216
  *
791
1217
  * @task T9814
1218
+ * @task T10599 (dry-run semantics: wouldCreate, wouldAffect, validatedCount, insertedCount, validationFindings)
792
1219
  */
793
- export interface TasksAddBatchResult {
1220
+ export interface TasksAddBatchResult extends TaskMutationEnvelope<number, [], []> {
794
1221
  /** Number of tasks actually created (0 on rollback or dry-run). */
795
1222
  created: number;
796
1223
  /** Per-task results in input order. */
797
1224
  tasks: TasksAddResult[];
798
1225
  /** Whether this was a dry run. */
799
1226
  dryRun?: boolean;
1227
+ /**
1228
+ * Number of tasks that would be created if the dry run were executed for real.
1229
+ * Only present when `dryRun` is `true`. Use this for `--output count` in
1230
+ * dry-run mode — `created` is always 0 in a dry run.
1231
+ *
1232
+ * @task T10599
1233
+ */
1234
+ wouldCreate?: number;
1235
+ /**
1236
+ * Generic number of rows/entities that would be affected by the dry-run operation.
1237
+ * For `tasks.add-batch`, this equals `wouldCreate`.
1238
+ *
1239
+ * @task T10599
1240
+ */
1241
+ wouldAffect?: number;
1242
+ /**
1243
+ * Number of task specs that successfully passed validation during a dry run.
1244
+ * Always equals `wouldCreate` when no validation errors occurred; may be less
1245
+ * when partial validation failures are tolerated.
1246
+ *
1247
+ * @task T10599
1248
+ */
1249
+ validatedCount?: number;
1250
+ /**
1251
+ * Number of tasks durably written to the database.
1252
+ * - Live run: equals `created`.
1253
+ * - Dry run: always `0` (no writes performed).
1254
+ *
1255
+ * Kept separate from `created` so callers can distinguish "batch was a
1256
+ * dry run with 0 writes" from "batch was live but created nothing due to
1257
+ * rollback/validation failure".
1258
+ *
1259
+ * @task T10599
1260
+ */
1261
+ insertedCount?: number;
1262
+ /**
1263
+ * Per-task non-blocking validation warnings emitted during a dry run.
1264
+ * Only present when at least one task spec produced a warning.
1265
+ * Index matches the position of the spec in the `tasks` input array.
1266
+ *
1267
+ * @task T10599
1268
+ */
1269
+ validationFindings?: Array<{ index: number; warnings: string[] }>;
1270
+ /** Standard dry-run/preflight projection shared by task mutations. @task T10608 */
1271
+ dryRunSummary?: TaskMutationDryRunSummary;
1272
+ /** Structured partial-success warnings, flattened across batch entries. @task T10608 */
1273
+ mutationWarnings: TaskMutationWarning[];
1274
+ /** Updated task records; empty for add-batch. @task T10608 */
1275
+ updated: [];
1276
+ /** Deleted task records; empty for add-batch. @task T10608 */
1277
+ deleted: [];
1278
+ /** Total number of live rows/entities affected. @task T10608 */
1279
+ affectedCount: number;
800
1280
  }
801
1281
 
802
1282
  // tasks.add (dispatch-level params — extends TasksCreateParams)
@@ -836,7 +1316,7 @@ export interface TasksAddParams {
836
1316
  *
837
1317
  * @task T1703
838
1318
  */
839
- export interface TasksAddResult {
1319
+ export interface TasksAddResult extends TaskMutationEnvelope<TaskRecord[], [], []> {
840
1320
  /** The created task record. */
841
1321
  task: TaskRecord;
842
1322
  /** Whether a duplicate was detected (but bypassed via forceDuplicate). */
@@ -845,6 +1325,18 @@ export interface TasksAddResult {
845
1325
  dryRun?: boolean;
846
1326
  /** Non-blocking validation warnings. @defaultValue undefined */
847
1327
  warnings?: string[];
1328
+ /** Standardized created bucket; contains `task` on live create, empty on dry-run. @task T10608 */
1329
+ created: TaskRecord[];
1330
+ /** Standardized updated bucket; empty for create. @task T10608 */
1331
+ updated: [];
1332
+ /** Standardized deleted bucket; empty for create. @task T10608 */
1333
+ deleted: [];
1334
+ /** Total number of live rows/entities affected. @task T10608 */
1335
+ affectedCount: number;
1336
+ /** Structured partial-success/preflight warnings. @task T10608 */
1337
+ mutationWarnings: TaskMutationWarning[];
1338
+ /** Standard dry-run/preflight projection. @task T10608 */
1339
+ dryRunSummary?: TaskMutationDryRunSummary;
848
1340
  }
849
1341
 
850
1342
  // tasks.update (dispatch-level params — extends TasksUpdateParams)
@@ -902,11 +1394,25 @@ export interface TasksUpdateQueryParams {
902
1394
  *
903
1395
  * @task T1703
904
1396
  */
905
- export interface TasksUpdateQueryResult {
1397
+ export interface TasksUpdateQueryResult extends TaskMutationEnvelope<[], TaskRecord[], []> {
906
1398
  /** Updated task record. */
907
1399
  task: TaskRecord;
908
1400
  /** Human-readable list of fields that were changed. @defaultValue undefined */
909
1401
  changes?: string[];
1402
+ /** Whether this was a dry run (no mutation persisted). @defaultValue undefined */
1403
+ dryRun?: boolean;
1404
+ /** Standardized created bucket; empty for update. @task T10608 */
1405
+ created: [];
1406
+ /** Standardized updated bucket; contains the updated task on live update, empty on dry-run. @task T10608 */
1407
+ updated: TaskRecord[];
1408
+ /** Standardized deleted bucket; empty for update. @task T10608 */
1409
+ deleted: [];
1410
+ /** Total number of live rows/entities affected. @task T10608 */
1411
+ affectedCount: number;
1412
+ /** Structured partial-success/preflight warnings. @task T10608 */
1413
+ mutationWarnings: TaskMutationWarning[];
1414
+ /** Standard dry-run/preflight projection. @task T10608 */
1415
+ dryRunSummary?: TaskMutationDryRunSummary;
910
1416
  }
911
1417
 
912
1418
  // tasks.complete (dispatch-level params)
@@ -926,6 +1432,24 @@ export interface TasksCompleteQueryParams {
926
1432
  overrideReason?: string;
927
1433
  /** Reason for acknowledging CRITICAL nexus impact risk (bypasses nexusImpact gate). */
928
1434
  acknowledgeRisk?: string;
1435
+ /**
1436
+ * Comma-separated AC tokens (UUIDs or `AC<n>` aliases) waived from the
1437
+ * AC-coverage gate (T10509). MUST be paired with {@link waiveReason} —
1438
+ * the gate rejects the call when one is set without the other.
1439
+ *
1440
+ * Waivers are recorded to `.cleo/audit/ac-waiver.jsonl` per ADR-079-r4 §4.
1441
+ *
1442
+ * @task T10509
1443
+ * @saga T10377 (SG-IVTR-AC-BINDING)
1444
+ */
1445
+ waiveAc?: string;
1446
+ /**
1447
+ * Justification text for the {@link waiveAc} waiver. Mandatory whenever
1448
+ * `waiveAc` is non-empty.
1449
+ *
1450
+ * @task T10509
1451
+ */
1452
+ waiveReason?: string;
929
1453
  }
930
1454
  /**
931
1455
  * Result of `tasks.complete` — completion confirmation with unblocked tasks.
@@ -951,13 +1475,25 @@ export interface TasksDeleteQueryParams {
951
1475
  *
952
1476
  * @task T1703
953
1477
  */
954
- export interface TasksDeleteQueryResult {
1478
+ export interface TasksDeleteQueryResult extends TaskMutationEnvelope<[], [], boolean> {
955
1479
  /** The deleted task record. */
956
1480
  deletedTask: TaskRecord;
957
1481
  /** Whether the deletion was applied (always true on success). */
958
1482
  deleted: boolean;
959
1483
  /** IDs of child tasks cascade-deleted along with the parent. @defaultValue undefined */
960
1484
  cascadeDeleted?: string[];
1485
+ /** Whether this was a dry run (no mutation persisted). @defaultValue undefined */
1486
+ dryRun?: boolean;
1487
+ /** Standardized created bucket; empty for delete. @task T10608 */
1488
+ created: [];
1489
+ /** Standardized updated bucket; empty for delete. @task T10608 */
1490
+ updated: [];
1491
+ /** Total number of live rows/entities affected. @task T10608 */
1492
+ affectedCount: number;
1493
+ /** Structured partial-success/preflight warnings. @task T10608 */
1494
+ mutationWarnings: TaskMutationWarning[];
1495
+ /** Standard dry-run/preflight projection. @task T10608 */
1496
+ dryRunSummary?: TaskMutationDryRunSummary;
961
1497
  }
962
1498
 
963
1499
  // tasks.archive (dispatch-level params)
@@ -1053,6 +1589,8 @@ export interface TasksSagaCreateParams {
1053
1589
  description?: string;
1054
1590
  /** Pipe-separated acceptance criteria. */
1055
1591
  acceptance?: string[];
1592
+ /** Validate and preview the Saga without writing rows. */
1593
+ dryRun?: boolean;
1056
1594
  }
1057
1595
 
1058
1596
  /**
@@ -1062,13 +1600,23 @@ export interface TasksSagaCreateParams {
1062
1600
  * @task T9521
1063
1601
  */
1064
1602
  export interface TasksSagaCreateResult {
1065
- /** The created task record (type=epic, labels includes 'saga'). */
1603
+ /** The created task record (type='saga'). */
1066
1604
  task: TaskRecord;
1605
+ /** True when this is a validation-only preview. */
1606
+ dryRun?: boolean;
1607
+ /** Number of Saga tasks that would be created by a successful dry-run. */
1608
+ wouldCreate?: number;
1609
+ /** Generic affected-entity count for dry-run projection. */
1610
+ wouldAffect?: number;
1611
+ /** Number of Saga specs validated during dry-run preflight. */
1612
+ validatedCount?: number;
1613
+ /** Number of rows durably inserted; always 0 for dry-run. */
1614
+ insertedCount?: number;
1067
1615
  }
1068
1616
 
1069
1617
  /** Params for `tasks.saga.add` — link an Epic to a Saga. */
1070
1618
  export interface TasksSagaAddParams {
1071
- /** Saga task ID (must have label='saga'). */
1619
+ /** Saga task ID (must have type='saga'). */
1072
1620
  sagaId: string;
1073
1621
  /** Epic task ID to link as a member. */
1074
1622
  epicId: string;
@@ -1187,7 +1735,7 @@ export interface TasksSagaMembersResult {
1187
1735
  * @see ADR-073-above-epic-naming.md §1.2 — invariant I5
1188
1736
  */
1189
1737
  export interface TasksSagaRepairParams {
1190
- /** Saga task ID (must have `label='saga'`). */
1738
+ /** Saga task ID (must have type='saga'). */
1191
1739
  sagaId: string;
1192
1740
  }
1193
1741
 
@@ -1216,6 +1764,70 @@ export interface TasksSagaRepairResult {
1216
1764
  note?: string;
1217
1765
  }
1218
1766
 
1767
+ /**
1768
+ * Params for `tasks.saga.migrate-containment` — migrate parent_id-based Saga
1769
+ * membership to `task_relations.type='groups'` (ADR-073 §1.2 invariant I5).
1770
+ *
1771
+ * Converts Epics whose `parent_id` points to a `type='saga'` task into
1772
+ * proper `groups` relations. Non-Epic tasks with Saga parents are documented
1773
+ * as conflicts requiring manual resolution.
1774
+ *
1775
+ * @task T10637
1776
+ * @see ADR-073-above-epic-naming.md §1.2 — invariant I5
1777
+ */
1778
+ export interface TasksSagaMigrateContainmentParams {
1779
+ /** Specific Saga ID to migrate. When omitted, migrates ALL sagas. */
1780
+ sagaId?: string;
1781
+ /** Dry-run mode: scan only, no mutations performed. */
1782
+ dryRun?: boolean;
1783
+ }
1784
+
1785
+ /**
1786
+ * A single Epic that was migrated from parent_id to groups relation.
1787
+ *
1788
+ * @task T10637
1789
+ */
1790
+ export interface TasksSagaMigratedEpic {
1791
+ epicId: string;
1792
+ sagaId: string;
1793
+ oldParentId: string | null;
1794
+ groupsRelation: { from: string; to: string; type: 'groups' };
1795
+ }
1796
+
1797
+ /**
1798
+ * A task (non-Epic) with a Saga parent — cannot be auto-migrated.
1799
+ *
1800
+ * @task T10637
1801
+ */
1802
+ export interface TasksSagaContainmentConflict {
1803
+ taskId: string;
1804
+ sagaId: string;
1805
+ taskType: string;
1806
+ taskTitle: string;
1807
+ reason: string;
1808
+ }
1809
+
1810
+ /**
1811
+ * Result of `tasks.saga.migrate-containment` — reports every Epic migrated
1812
+ * and every non-Epic conflict that needs manual resolution.
1813
+ *
1814
+ * @task T10637
1815
+ */
1816
+ export interface TasksSagaMigrateContainmentResult {
1817
+ /** Total sagas scanned. */
1818
+ sagasScanned: number;
1819
+ /** Number of Epics successfully migrated. */
1820
+ migrated: number;
1821
+ /** Number of already-correct Epics skipped (idempotent no-op). */
1822
+ skipped: number;
1823
+ /** Detailed list of migrated Epics. */
1824
+ migratedEpics: TasksSagaMigratedEpic[];
1825
+ /** Non-Epic tasks with Saga parents — need manual resolution. */
1826
+ conflicts: TasksSagaContainmentConflict[];
1827
+ /** Whether this was a dry run (no mutations). */
1828
+ dryRun: boolean;
1829
+ }
1830
+
1219
1831
  /**
1220
1832
  * Per-saga reconciliation action returned by `tasks.saga.reconcile`.
1221
1833
  *
@@ -1235,7 +1847,7 @@ export type TasksSagaReconcileAction = 'close' | 'no-op' | 'blocked' | 'error';
1235
1847
  export interface TasksSagaReconcileParams {
1236
1848
  /**
1237
1849
  * Optional single saga to reconcile. When omitted, the verb walks every
1238
- * saga returned by `taskList({ type: 'epic', label: 'saga' })`.
1850
+ * saga returned by `taskList({ type: 'saga' })`.
1239
1851
  */
1240
1852
  sagaId?: string;
1241
1853
  /**
@@ -1346,12 +1958,20 @@ export type TasksOps = {
1346
1958
  readonly tree: readonly [TasksTreeDispatchParams, TasksTreeDispatchResult];
1347
1959
  readonly blockers: readonly [TasksBlockersQueryParams, TasksBlockersQueryResult];
1348
1960
  readonly depends: readonly [TasksDependsParams, TasksDependsResult];
1961
+ readonly slice: readonly [TasksSliceParams, TasksSliceResult];
1349
1962
  readonly 'deps.validate': readonly [TasksDepsValidateParams, TasksDepsValidateResult];
1350
1963
  readonly 'deps.tree': readonly [TasksDepsTreeParams, TasksDepsTreeResult];
1351
1964
  readonly analyze: readonly [TasksAnalyzeQueryParams, TasksAnalyzeQueryResult];
1352
1965
  readonly impact: readonly [TasksImpactParams, TasksImpactResult];
1353
1966
  readonly next: readonly [TasksNextQueryParams, TasksNextQueryResult];
1354
1967
  readonly plan: readonly [TasksPlanParams, TasksPlanResult];
1968
+ readonly 'completion.list': readonly [CompletionListParams, CompletionListResult];
1969
+ readonly 'completion.evaluate': readonly [CompletionEvaluateParams, CompletionEvaluateResult];
1970
+ readonly 'completion.explain': readonly [CompletionExplainParams, CompletionExplainResult];
1971
+ readonly 'projection.repair': readonly [
1972
+ CompletionProjectionRepairParams,
1973
+ CompletionProjectionRepairResult,
1974
+ ];
1355
1975
  readonly relates: readonly [TasksRelatesParams, TasksRelatesResult];
1356
1976
  readonly 'complexity.estimate': readonly [
1357
1977
  TasksComplexityEstimateParams,
@@ -1361,6 +1981,8 @@ export type TasksOps = {
1361
1981
  readonly current: readonly [TasksCurrentParams, TasksCurrentResult];
1362
1982
  readonly 'label.list': readonly [TasksLabelListParams, TasksLabelListResult];
1363
1983
  readonly 'sync.links': readonly [TasksSyncLinksParams, TasksSyncLinksResult];
1984
+ // T10629 — task-scoped context pack with token budget
1985
+ readonly context: readonly [TasksContextParams, TasksContextResult];
1364
1986
  // Mutate ops
1365
1987
  readonly add: readonly [TasksAddParams, TasksAddResult];
1366
1988
  readonly 'add-batch': readonly [TasksAddBatchParams, TasksAddBatchResult];
@@ -1373,6 +1995,7 @@ export type TasksOps = {
1373
1995
  readonly reparent: readonly [TasksReparentQueryParams, TasksReparentDispatchResult];
1374
1996
  readonly reorder: readonly [TasksReorderQueryParams, TasksReorderDispatchResult];
1375
1997
  readonly 'relates.add': readonly [TasksRelatesAddParams, TasksRelatesAddResult];
1998
+ readonly 'relates.add-batch': readonly [TasksRelatesAddBatchParams, TasksRelatesAddBatchResult];
1376
1999
  readonly 'relates.remove': readonly [TasksRelatesRemoveParams, TasksRelatesRemoveResult];
1377
2000
  readonly start: readonly [TasksStartQueryParams, TasksStartQueryResult];
1378
2001
  readonly stop: readonly [TasksStopQueryParams, TasksStopQueryResult];
@@ -1391,6 +2014,11 @@ export type TasksOps = {
1391
2014
  readonly 'saga.repair': readonly [TasksSagaRepairParams, TasksSagaRepairResult];
1392
2015
  /** T10121 — idempotent cron-safe auto-close repair (supersedes T10098 scope). */
1393
2016
  readonly 'saga.reconcile': readonly [TasksSagaReconcileParams, TasksSagaReconcileResult];
2017
+ /** T10637 — migrate parent_id-based Saga membership to groups relations. */
2018
+ readonly 'saga.migrate-containment': readonly [
2019
+ TasksSagaMigrateContainmentParams,
2020
+ TasksSagaMigrateContainmentResult,
2021
+ ];
1394
2022
  };
1395
2023
 
1396
2024
  // ---------------------------------------------------------------------------
@@ -1429,7 +2057,7 @@ export interface TasksAddBatchEntry {
1429
2057
  description?: string;
1430
2058
  /** Parent task ID (makes this task a subtask). */
1431
2059
  parent?: string;
1432
- /** Comma-separated dependency task IDs. */
2060
+ /** Dependency task IDs as a JSON array. */
1433
2061
  depends?: string[];
1434
2062
  /** Task priority (low | medium | high | critical). */
1435
2063
  priority?: string;
@@ -1437,7 +2065,7 @@ export interface TasksAddBatchEntry {
1437
2065
  labels?: string[];
1438
2066
  /** Task type (saga | epic | task | subtask). */
1439
2067
  type?: TaskType; // SSoT-EXEMPT:kind≠type — same axis split as TasksAddBatchParams (T944) // ssot-exempt-ok: mirrors parent type
1440
- /** Pipe-separated acceptance criteria, normalized to string[]. */
2068
+ /** Acceptance criteria as a JSON array; pipe-delimited strings are not canonical. */
1441
2069
  acceptance?: string[];
1442
2070
  /** Phase slug to assign the task to. */
1443
2071
  phase?: string;
@@ -1550,9 +2178,17 @@ export const TASKS_ADD_BATCH_INPUT_SCHEMA: JsonSchema = {
1550
2178
  parent: { type: 'string' },
1551
2179
  depends: { type: 'array', items: { type: 'string' } },
1552
2180
  priority: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] },
1553
- labels: { type: 'array', items: { type: 'string' } },
2181
+ labels: {
2182
+ type: 'array',
2183
+ items: { type: 'string' },
2184
+ 'x-fix-hint': 'pass labels as a JSON array, e.g. ["pm-core-v2", "wave.3"]',
2185
+ },
1554
2186
  type: { type: 'string', enum: ['saga', 'epic', 'task', 'subtask'] },
1555
- acceptance: { type: 'array', items: { type: 'string' } },
2187
+ acceptance: {
2188
+ type: 'array',
2189
+ items: { type: 'string' },
2190
+ 'x-fix-hint': 'pass acceptance as a JSON array, not a pipe-delimited string',
2191
+ },
1556
2192
  phase: { type: 'string' },
1557
2193
  size: { type: 'string', enum: ['small', 'medium', 'large'] },
1558
2194
  notes: { type: 'string' },
@@ -1584,7 +2220,18 @@ export const tasksAddBatchInputContract: OperationInputContract<TasksAddBatchPar
1584
2220
  {
1585
2221
  name: 'two-tasks',
1586
2222
  value: {
1587
- tasks: [{ title: 'first task' }, { title: 'second task' }],
2223
+ tasks: [
2224
+ {
2225
+ title: 'first task',
2226
+ labels: ['pm-core-v2', 'wave.3'],
2227
+ acceptance: ['first acceptance criterion'],
2228
+ },
2229
+ {
2230
+ title: 'second task',
2231
+ labels: ['batch'],
2232
+ acceptance: ['second acceptance criterion'],
2233
+ },
2234
+ ],
1588
2235
  },
1589
2236
  description: 'Atomic insert of two tasks under no shared parent.',
1590
2237
  },