@cleocode/contracts 2026.5.122 → 2026.5.124

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 (108) 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.js +21 -3
  10. package/dist/__tests__/satisfies-atom.test.js.map +1 -1
  11. package/dist/__tests__/workgraph-context-scaffold-contracts.test.d.ts +8 -0
  12. package/dist/__tests__/workgraph-context-scaffold-contracts.test.d.ts.map +1 -0
  13. package/dist/__tests__/workgraph-context-scaffold-contracts.test.js +188 -0
  14. package/dist/__tests__/workgraph-context-scaffold-contracts.test.js.map +1 -0
  15. package/dist/__tests__/workgraph-operation-contracts.test.d.ts +8 -0
  16. package/dist/__tests__/workgraph-operation-contracts.test.d.ts.map +1 -0
  17. package/dist/__tests__/workgraph-operation-contracts.test.js +115 -0
  18. package/dist/__tests__/workgraph-operation-contracts.test.js.map +1 -0
  19. package/dist/data-accessor.d.ts +77 -0
  20. package/dist/data-accessor.d.ts.map +1 -1
  21. package/dist/data-accessor.js.map +1 -1
  22. package/dist/dispatch/__tests__/docs-update-registry.test.d.ts +2 -0
  23. package/dist/dispatch/__tests__/docs-update-registry.test.d.ts.map +1 -0
  24. package/dist/dispatch/__tests__/docs-update-registry.test.js +27 -0
  25. package/dist/dispatch/__tests__/docs-update-registry.test.js.map +1 -0
  26. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.d.ts +2 -0
  27. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.d.ts.map +1 -0
  28. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.js +271 -0
  29. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.js.map +1 -0
  30. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  31. package/dist/dispatch/operations-registry.js +487 -7
  32. package/dist/dispatch/operations-registry.js.map +1 -1
  33. package/dist/evidence-atom-schema.d.ts +4 -4
  34. package/dist/evidence-atom-schema.d.ts.map +1 -1
  35. package/dist/evidence-atom-schema.js +9 -9
  36. package/dist/evidence-atom-schema.js.map +1 -1
  37. package/dist/index.d.ts +12 -6
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +8 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/operation-envelope-validation.d.ts +79 -0
  42. package/dist/operation-envelope-validation.d.ts.map +1 -0
  43. package/dist/operation-envelope-validation.js +219 -0
  44. package/dist/operation-envelope-validation.js.map +1 -0
  45. package/dist/operations/__tests__/agent-operation-contract.test.d.ts +2 -0
  46. package/dist/operations/__tests__/agent-operation-contract.test.d.ts.map +1 -0
  47. package/dist/operations/__tests__/agent-operation-contract.test.js +128 -0
  48. package/dist/operations/__tests__/agent-operation-contract.test.js.map +1 -0
  49. package/dist/operations/__tests__/tasks-input-contracts.test.js +15 -0
  50. package/dist/operations/__tests__/tasks-input-contracts.test.js.map +1 -1
  51. package/dist/operations/agent-operation-contract.d.ts +227 -0
  52. package/dist/operations/agent-operation-contract.d.ts.map +1 -0
  53. package/dist/operations/agent-operation-contract.js +38 -0
  54. package/dist/operations/agent-operation-contract.js.map +1 -0
  55. package/dist/operations/docs.d.ts +19 -1
  56. package/dist/operations/docs.d.ts.map +1 -1
  57. package/dist/operations/docs.js +20 -0
  58. package/dist/operations/docs.js.map +1 -1
  59. package/dist/operations/focus.d.ts +1 -1
  60. package/dist/operations/index.d.ts +1 -0
  61. package/dist/operations/index.d.ts.map +1 -1
  62. package/dist/operations/index.js +1 -0
  63. package/dist/operations/index.js.map +1 -1
  64. package/dist/operations/orchestrate.d.ts +66 -0
  65. package/dist/operations/orchestrate.d.ts.map +1 -1
  66. package/dist/operations/tasks.d.ts +568 -11
  67. package/dist/operations/tasks.d.ts.map +1 -1
  68. package/dist/operations/tasks.js +22 -3
  69. package/dist/operations/tasks.js.map +1 -1
  70. package/dist/task-record.d.ts +15 -0
  71. package/dist/task-record.d.ts.map +1 -1
  72. package/dist/task.d.ts +15 -6
  73. package/dist/task.d.ts.map +1 -1
  74. package/dist/task.js.map +1 -1
  75. package/dist/tasks.d.ts +944 -0
  76. package/dist/tasks.d.ts.map +1 -1
  77. package/dist/tasks.js +198 -1
  78. package/dist/tasks.js.map +1 -1
  79. package/dist/workgraph.d.ts +1950 -0
  80. package/dist/workgraph.d.ts.map +1 -0
  81. package/dist/workgraph.js +449 -0
  82. package/dist/workgraph.js.map +1 -0
  83. package/package.json +10 -2
  84. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  85. package/src/__tests__/completion-operation-contracts.test.ts +154 -0
  86. package/src/__tests__/operation-envelope-validation.test.ts +140 -0
  87. package/src/__tests__/satisfies-atom.test.ts +23 -3
  88. package/src/__tests__/workgraph-context-scaffold-contracts.test.ts +232 -0
  89. package/src/__tests__/workgraph-operation-contracts.test.ts +151 -0
  90. package/src/data-accessor.ts +94 -1
  91. package/src/dispatch/__tests__/docs-update-registry.test.ts +35 -0
  92. package/src/dispatch/__tests__/operation-envelope-snapshots.test.ts +289 -0
  93. package/src/dispatch/operations-registry.ts +502 -7
  94. package/src/evidence-atom-schema.ts +10 -10
  95. package/src/index.ts +193 -2
  96. package/src/operation-envelope-validation.ts +319 -0
  97. package/src/operations/__tests__/agent-operation-contract.test.ts +161 -0
  98. package/src/operations/__tests__/tasks-input-contracts.test.ts +30 -0
  99. package/src/operations/agent-operation-contract.ts +253 -0
  100. package/src/operations/docs.ts +27 -7
  101. package/src/operations/focus.ts +1 -1
  102. package/src/operations/index.ts +1 -0
  103. package/src/operations/orchestrate.ts +71 -0
  104. package/src/operations/tasks.ts +612 -15
  105. package/src/task-record.ts +16 -0
  106. package/src/task.ts +16 -6
  107. package/src/tasks.ts +558 -0
  108. 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.
@@ -250,6 +276,8 @@ export interface TasksShowResult {
250
276
  * @epic T10381
251
277
  */
252
278
  acRows?: TaskShowAcRowEntry[];
279
+ /** Expanded relation/doc lists returned only when `cleo show --relations` is requested. */
280
+ relations?: TaskShowRelationsEntry;
253
281
  }
254
282
 
255
283
  /**
@@ -335,6 +363,52 @@ export interface TasksDependsParams {
335
363
  */
336
364
  export type TasksDependsResult = TaskDependsResult;
337
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
+
338
412
  // tasks.analyze dispatch params (with optional taskId and tierLimit)
339
413
  export interface TasksAnalyzeQueryParams {
340
414
  taskId?: string;
@@ -350,6 +424,211 @@ export interface TasksAnalyzeQueryParams {
350
424
  */
351
425
  export type TasksAnalyzeQueryResult = TaskAnalysisResult & { tierLimit: number };
352
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
+
353
632
  // tasks.deps.validate
354
633
  /**
355
634
  * Parameters for `tasks.deps.validate`.
@@ -509,6 +788,12 @@ export interface TasksRelatesParams {
509
788
  taskId: string;
510
789
  mode?: 'suggest' | 'discover';
511
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;
512
797
  }
513
798
  /**
514
799
  * Result of `tasks.relates` — task relations list.
@@ -519,11 +804,55 @@ export interface TasksRelatesResult {
519
804
  /** The task ID whose relations were loaded. */
520
805
  taskId: string;
521
806
  /** All relations for this task. */
522
- 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';
523
818
  /** Total number of relations. */
524
819
  count: number;
525
820
  }
526
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
+
527
856
  // tasks.complexity.estimate
528
857
  export interface TasksComplexityEstimateParams {
529
858
  taskId: string;
@@ -631,6 +960,14 @@ export interface TasksSyncLinksRemoveResult {
631
960
  export interface TasksCancelParams {
632
961
  taskId: string;
633
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;
634
971
  }
635
972
  /**
636
973
  * Result of `tasks.cancel` — cancellation confirmation.
@@ -655,6 +992,12 @@ export interface TasksCancelResult {
655
992
  * @defaultValue undefined
656
993
  */
657
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;
658
1001
  }
659
1002
 
660
1003
  // tasks.restore (with from routing: done → reopen, archived → unarchive)
@@ -751,6 +1094,57 @@ export interface TasksRelatesAddResult {
751
1094
  added: boolean;
752
1095
  }
753
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
+
754
1148
  // tasks.relates.remove
755
1149
  export interface TasksRelatesRemoveParams {
756
1150
  /** Source task ID. */
@@ -821,14 +1215,68 @@ export interface TasksAddBatchParams {
821
1215
  * Result of `tasks.add-batch`.
822
1216
  *
823
1217
  * @task T9814
1218
+ * @task T10599 (dry-run semantics: wouldCreate, wouldAffect, validatedCount, insertedCount, validationFindings)
824
1219
  */
825
- export interface TasksAddBatchResult {
1220
+ export interface TasksAddBatchResult extends TaskMutationEnvelope<number, [], []> {
826
1221
  /** Number of tasks actually created (0 on rollback or dry-run). */
827
1222
  created: number;
828
1223
  /** Per-task results in input order. */
829
1224
  tasks: TasksAddResult[];
830
1225
  /** Whether this was a dry run. */
831
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;
832
1280
  }
833
1281
 
834
1282
  // tasks.add (dispatch-level params — extends TasksCreateParams)
@@ -868,7 +1316,7 @@ export interface TasksAddParams {
868
1316
  *
869
1317
  * @task T1703
870
1318
  */
871
- export interface TasksAddResult {
1319
+ export interface TasksAddResult extends TaskMutationEnvelope<TaskRecord[], [], []> {
872
1320
  /** The created task record. */
873
1321
  task: TaskRecord;
874
1322
  /** Whether a duplicate was detected (but bypassed via forceDuplicate). */
@@ -877,6 +1325,18 @@ export interface TasksAddResult {
877
1325
  dryRun?: boolean;
878
1326
  /** Non-blocking validation warnings. @defaultValue undefined */
879
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;
880
1340
  }
881
1341
 
882
1342
  // tasks.update (dispatch-level params — extends TasksUpdateParams)
@@ -934,11 +1394,25 @@ export interface TasksUpdateQueryParams {
934
1394
  *
935
1395
  * @task T1703
936
1396
  */
937
- export interface TasksUpdateQueryResult {
1397
+ export interface TasksUpdateQueryResult extends TaskMutationEnvelope<[], TaskRecord[], []> {
938
1398
  /** Updated task record. */
939
1399
  task: TaskRecord;
940
1400
  /** Human-readable list of fields that were changed. @defaultValue undefined */
941
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;
942
1416
  }
943
1417
 
944
1418
  // tasks.complete (dispatch-level params)
@@ -1001,13 +1475,25 @@ export interface TasksDeleteQueryParams {
1001
1475
  *
1002
1476
  * @task T1703
1003
1477
  */
1004
- export interface TasksDeleteQueryResult {
1478
+ export interface TasksDeleteQueryResult extends TaskMutationEnvelope<[], [], boolean> {
1005
1479
  /** The deleted task record. */
1006
1480
  deletedTask: TaskRecord;
1007
1481
  /** Whether the deletion was applied (always true on success). */
1008
1482
  deleted: boolean;
1009
1483
  /** IDs of child tasks cascade-deleted along with the parent. @defaultValue undefined */
1010
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;
1011
1497
  }
1012
1498
 
1013
1499
  // tasks.archive (dispatch-level params)
@@ -1103,6 +1589,8 @@ export interface TasksSagaCreateParams {
1103
1589
  description?: string;
1104
1590
  /** Pipe-separated acceptance criteria. */
1105
1591
  acceptance?: string[];
1592
+ /** Validate and preview the Saga without writing rows. */
1593
+ dryRun?: boolean;
1106
1594
  }
1107
1595
 
1108
1596
  /**
@@ -1112,13 +1600,23 @@ export interface TasksSagaCreateParams {
1112
1600
  * @task T9521
1113
1601
  */
1114
1602
  export interface TasksSagaCreateResult {
1115
- /** The created task record (type=epic, labels includes 'saga'). */
1603
+ /** The created task record (type='saga'). */
1116
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;
1117
1615
  }
1118
1616
 
1119
1617
  /** Params for `tasks.saga.add` — link an Epic to a Saga. */
1120
1618
  export interface TasksSagaAddParams {
1121
- /** Saga task ID (must have label='saga'). */
1619
+ /** Saga task ID (must have type='saga'). */
1122
1620
  sagaId: string;
1123
1621
  /** Epic task ID to link as a member. */
1124
1622
  epicId: string;
@@ -1237,7 +1735,7 @@ export interface TasksSagaMembersResult {
1237
1735
  * @see ADR-073-above-epic-naming.md §1.2 — invariant I5
1238
1736
  */
1239
1737
  export interface TasksSagaRepairParams {
1240
- /** Saga task ID (must have `label='saga'`). */
1738
+ /** Saga task ID (must have type='saga'). */
1241
1739
  sagaId: string;
1242
1740
  }
1243
1741
 
@@ -1266,6 +1764,70 @@ export interface TasksSagaRepairResult {
1266
1764
  note?: string;
1267
1765
  }
1268
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
+
1269
1831
  /**
1270
1832
  * Per-saga reconciliation action returned by `tasks.saga.reconcile`.
1271
1833
  *
@@ -1285,7 +1847,7 @@ export type TasksSagaReconcileAction = 'close' | 'no-op' | 'blocked' | 'error';
1285
1847
  export interface TasksSagaReconcileParams {
1286
1848
  /**
1287
1849
  * Optional single saga to reconcile. When omitted, the verb walks every
1288
- * saga returned by `taskList({ type: 'epic', label: 'saga' })`.
1850
+ * saga returned by `taskList({ type: 'saga' })`.
1289
1851
  */
1290
1852
  sagaId?: string;
1291
1853
  /**
@@ -1396,12 +1958,20 @@ export type TasksOps = {
1396
1958
  readonly tree: readonly [TasksTreeDispatchParams, TasksTreeDispatchResult];
1397
1959
  readonly blockers: readonly [TasksBlockersQueryParams, TasksBlockersQueryResult];
1398
1960
  readonly depends: readonly [TasksDependsParams, TasksDependsResult];
1961
+ readonly slice: readonly [TasksSliceParams, TasksSliceResult];
1399
1962
  readonly 'deps.validate': readonly [TasksDepsValidateParams, TasksDepsValidateResult];
1400
1963
  readonly 'deps.tree': readonly [TasksDepsTreeParams, TasksDepsTreeResult];
1401
1964
  readonly analyze: readonly [TasksAnalyzeQueryParams, TasksAnalyzeQueryResult];
1402
1965
  readonly impact: readonly [TasksImpactParams, TasksImpactResult];
1403
1966
  readonly next: readonly [TasksNextQueryParams, TasksNextQueryResult];
1404
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
+ ];
1405
1975
  readonly relates: readonly [TasksRelatesParams, TasksRelatesResult];
1406
1976
  readonly 'complexity.estimate': readonly [
1407
1977
  TasksComplexityEstimateParams,
@@ -1411,6 +1981,8 @@ export type TasksOps = {
1411
1981
  readonly current: readonly [TasksCurrentParams, TasksCurrentResult];
1412
1982
  readonly 'label.list': readonly [TasksLabelListParams, TasksLabelListResult];
1413
1983
  readonly 'sync.links': readonly [TasksSyncLinksParams, TasksSyncLinksResult];
1984
+ // T10629 — task-scoped context pack with token budget
1985
+ readonly context: readonly [TasksContextParams, TasksContextResult];
1414
1986
  // Mutate ops
1415
1987
  readonly add: readonly [TasksAddParams, TasksAddResult];
1416
1988
  readonly 'add-batch': readonly [TasksAddBatchParams, TasksAddBatchResult];
@@ -1423,6 +1995,7 @@ export type TasksOps = {
1423
1995
  readonly reparent: readonly [TasksReparentQueryParams, TasksReparentDispatchResult];
1424
1996
  readonly reorder: readonly [TasksReorderQueryParams, TasksReorderDispatchResult];
1425
1997
  readonly 'relates.add': readonly [TasksRelatesAddParams, TasksRelatesAddResult];
1998
+ readonly 'relates.add-batch': readonly [TasksRelatesAddBatchParams, TasksRelatesAddBatchResult];
1426
1999
  readonly 'relates.remove': readonly [TasksRelatesRemoveParams, TasksRelatesRemoveResult];
1427
2000
  readonly start: readonly [TasksStartQueryParams, TasksStartQueryResult];
1428
2001
  readonly stop: readonly [TasksStopQueryParams, TasksStopQueryResult];
@@ -1441,6 +2014,11 @@ export type TasksOps = {
1441
2014
  readonly 'saga.repair': readonly [TasksSagaRepairParams, TasksSagaRepairResult];
1442
2015
  /** T10121 — idempotent cron-safe auto-close repair (supersedes T10098 scope). */
1443
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
+ ];
1444
2022
  };
1445
2023
 
1446
2024
  // ---------------------------------------------------------------------------
@@ -1479,7 +2057,7 @@ export interface TasksAddBatchEntry {
1479
2057
  description?: string;
1480
2058
  /** Parent task ID (makes this task a subtask). */
1481
2059
  parent?: string;
1482
- /** Comma-separated dependency task IDs. */
2060
+ /** Dependency task IDs as a JSON array. */
1483
2061
  depends?: string[];
1484
2062
  /** Task priority (low | medium | high | critical). */
1485
2063
  priority?: string;
@@ -1487,7 +2065,7 @@ export interface TasksAddBatchEntry {
1487
2065
  labels?: string[];
1488
2066
  /** Task type (saga | epic | task | subtask). */
1489
2067
  type?: TaskType; // SSoT-EXEMPT:kind≠type — same axis split as TasksAddBatchParams (T944) // ssot-exempt-ok: mirrors parent type
1490
- /** Pipe-separated acceptance criteria, normalized to string[]. */
2068
+ /** Acceptance criteria as a JSON array; pipe-delimited strings are not canonical. */
1491
2069
  acceptance?: string[];
1492
2070
  /** Phase slug to assign the task to. */
1493
2071
  phase?: string;
@@ -1600,9 +2178,17 @@ export const TASKS_ADD_BATCH_INPUT_SCHEMA: JsonSchema = {
1600
2178
  parent: { type: 'string' },
1601
2179
  depends: { type: 'array', items: { type: 'string' } },
1602
2180
  priority: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] },
1603
- 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
+ },
1604
2186
  type: { type: 'string', enum: ['saga', 'epic', 'task', 'subtask'] },
1605
- 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
+ },
1606
2192
  phase: { type: 'string' },
1607
2193
  size: { type: 'string', enum: ['small', 'medium', 'large'] },
1608
2194
  notes: { type: 'string' },
@@ -1634,7 +2220,18 @@ export const tasksAddBatchInputContract: OperationInputContract<TasksAddBatchPar
1634
2220
  {
1635
2221
  name: 'two-tasks',
1636
2222
  value: {
1637
- 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
+ ],
1638
2235
  },
1639
2236
  description: 'Atomic insert of two tasks under no shared parent.',
1640
2237
  },