@cleocode/contracts 2026.5.122 → 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 (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 +265 -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 +283 -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
@@ -25,6 +25,20 @@ export interface ValidationHistoryEntry {
25
25
  timestamp: string;
26
26
  }
27
27
 
28
+ /** Compact relation/document counts surfaced on default `cleo show` MVI output. */
29
+ export interface TaskRecordRelationCounts {
30
+ /** Direct dependencies declared by the task. */
31
+ depends: number;
32
+ /** Explicit blockedBy entries on the task record. */
33
+ blockedBy: number;
34
+ /** Non-dependency task relation links. */
35
+ relates: number;
36
+ /** Direct child tasks. */
37
+ children: number;
38
+ /** Docs attachments linked to the task. */
39
+ docs: number;
40
+ }
41
+
28
42
  /** String-widened Task for JSON serialization in dispatch/LAFS layer. */
29
43
  export interface TaskRecord {
30
44
  id: string;
@@ -58,6 +72,8 @@ export interface TaskRecord {
58
72
  lifecycleState?: string | null;
59
73
  validationHistory?: ValidationHistoryEntry[];
60
74
  blockedBy?: string[];
75
+ /** Compact counts for relationships and docs, kept in default MVI projection. */
76
+ relationCounts?: TaskRecordRelationCounts;
61
77
  cancellationReason?: string;
62
78
  /** RCASD-IVTR+C pipeline stage. @task T060 */
63
79
  pipelineStage?: string | null;
package/src/task.ts CHANGED
@@ -30,7 +30,12 @@ export type { TaskStatus };
30
30
  * A single acceptance criterion — either a free-text string (legacy) or a
31
31
  * structured {@link AcceptanceGate} (machine-verifiable).
32
32
  *
33
- * Mixed arrays of these are stored as JSON in the `acceptance_json` column.
33
+ * Mixed arrays of these were historically stored as JSON in the
34
+ * `acceptance_json` column. T10574 deprecates that column for completion:
35
+ * post-migration completion/readiness/AC-coverage logic must use canonical
36
+ * `task_acceptance_criteria` rows and `evidence_ac_bindings` instead.
37
+ * `acceptance_json` remains only for legacy import/backfill compatibility
38
+ * until all display fallbacks and historical fixtures have been removed.
34
39
  *
35
40
  * @epic T760
36
41
  * @task T780
@@ -81,14 +86,16 @@ export type TaskKind = 'work' | 'research' | 'experiment' | 'bug' | 'spike' | 'r
81
86
  export type TaskScope = 'project' | 'feature' | 'unit';
82
87
 
83
88
  /**
84
- * Bug severity axis. Only meaningful when `kind='bug'`; enforced by a DB-level
85
- * CHECK constraint on the `role` column (`severity IS NULL OR (severity IN (...) AND role='bug')`).
89
+ * Severity axis. Orthogonal to {@link TaskKind}; valid severities may be set
90
+ * on work/research/experiment/bug/spike/release tasks alike. The DB CHECK
91
+ * constraint only validates membership in P0-P3.
86
92
  *
87
93
  * OWNER-WRITE-ONLY: severity is intended to be set through owner-authenticated
88
94
  * paths only. This prevents a prompt-injection exploit where a compromised
89
95
  * Tier 3 agent could downgrade a P0 bug to P3 to force-ship.
90
96
  *
91
97
  * @task T944
98
+ * @task T10089
92
99
  */
93
100
  export type TaskSeverity = 'P0' | 'P1' | 'P2' | 'P3';
94
101
 
@@ -333,7 +340,7 @@ export type EvidenceAtom =
333
340
  kind: 'satisfies';
334
341
  /** Target task ID — `T<1-7 digits>` per ADR-079-r2 §2.1. */
335
342
  targetTaskId: string;
336
- /** Lowercase UUIDv4 — populated for canonical form; undefined for alias form. */
343
+ /** Lowercase UUIDv4/v5 — populated for canonical form; undefined for alias form. */
337
344
  targetAcId?: string;
338
345
  /** `AC<1-4 digits>` alias — populated for alias form; undefined for UUID form. */
339
346
  targetAcAlias?: string;
@@ -466,7 +473,7 @@ export interface Task {
466
473
  scope?: TaskScope;
467
474
 
468
475
  /**
469
- * Bug severity. Only valid when {@link role} is `'bug'`. OWNER-WRITE-ONLY.
476
+ * Severity axis. Valid for any {@link kind}. OWNER-WRITE-ONLY.
470
477
  * @defaultValue undefined
471
478
  * @task T944
472
479
  */
@@ -506,6 +513,9 @@ export interface Task {
506
513
  /** Related task entries (non-dependency relationships). @defaultValue undefined */
507
514
  relates?: TaskRelation[];
508
515
 
516
+ /** Gate status map (implemented/testsPassed/qaPassed). @defaultValue undefined */
517
+ gates?: Record<string, boolean>;
518
+
509
519
  /** Epic lifecycle state. Only meaningful when `type = 'epic'`. @defaultValue undefined */
510
520
  epicLifecycle?: EpicLifecycle | null;
511
521
 
@@ -638,7 +648,7 @@ export interface TaskCreate {
638
648
  scope?: TaskScope;
639
649
 
640
650
  /**
641
- * Bug severity (OWNER-WRITE-ONLY). Only valid with `kind='bug'`.
651
+ * Severity axis (OWNER-WRITE-ONLY). Valid for any `kind`.
642
652
  * @defaultValue undefined
643
653
  * @task T944
644
654
  */
package/src/tasks.ts CHANGED
@@ -15,7 +15,143 @@
15
15
  * @epic T1688
16
16
  */
17
17
 
18
+ import { z } from 'zod';
19
+ import { TASK_STATUSES } from './status-registry.js';
18
20
  import type { TaskPriority, TaskStatus } from './task.js';
21
+ import type { TaskRecord } from './task-record.js';
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Standard task mutation envelopes (T10608)
25
+ // ---------------------------------------------------------------------------
26
+
27
+ /** Mutation bucket names shared by task create/update/delete result envelopes. @task T10608 */
28
+ export type TaskMutationBucket = 'created' | 'updated' | 'deleted';
29
+
30
+ /** Severity for non-fatal warnings emitted by task mutation preflights/results. @task T10608 */
31
+ export type TaskMutationWarningSeverity = 'info' | 'warning';
32
+
33
+ /**
34
+ * Non-fatal warning surfaced by a task mutation.
35
+ *
36
+ * Warnings are structured so callers can distinguish partial-success hazards
37
+ * from hard validation errors without string parsing.
38
+ *
39
+ * @task T10608
40
+ */
41
+ export interface TaskMutationWarning {
42
+ /** Stable machine-readable code, e.g. `duplicate_likely` or `cascade_delete`. */
43
+ code: string;
44
+ /** Human-readable explanation of the warning. */
45
+ message: string;
46
+ /** Warning severity. Defaults to `warning` when omitted by older producers. */
47
+ severity?: TaskMutationWarningSeverity;
48
+ /** Task ID associated with the warning, when known. */
49
+ taskId?: string;
50
+ /** Input field associated with the warning, when applicable. */
51
+ field?: string;
52
+ /** Batch input index associated with the warning, when applicable. */
53
+ index?: number;
54
+ }
55
+
56
+ /**
57
+ * Dry-run/preflight projection for task mutations.
58
+ *
59
+ * Counts are split by verb so `created`, `updated`, and `deleted` result
60
+ * envelopes can all expose the same dry-run shape while preserving the older
61
+ * operation-specific count names during migration.
62
+ *
63
+ * @task T10608
64
+ */
65
+ export interface TaskMutationDryRunSummary {
66
+ /** Always true for a dry-run projection. */
67
+ dryRun: true;
68
+ /** Number of entities that would be created by executing the mutation. */
69
+ wouldCreate: number;
70
+ /** Number of entities that would be updated by executing the mutation. */
71
+ wouldUpdate: number;
72
+ /** Number of entities that would be deleted by executing the mutation. */
73
+ wouldDelete: number;
74
+ /** Total affected entities across create/update/delete buckets. */
75
+ wouldAffect: number;
76
+ /** Number of specs/entities that passed preflight validation. */
77
+ validatedCount: number;
78
+ /** Rows durably inserted during dry-run; always 0. */
79
+ insertedCount: 0;
80
+ /** Rows durably updated during dry-run; always 0. */
81
+ updatedCount: 0;
82
+ /** Rows durably deleted during dry-run; always 0. */
83
+ deletedCount: 0;
84
+ /** Non-fatal preflight warnings. Empty when no warnings were emitted. */
85
+ warnings: TaskMutationWarning[];
86
+ }
87
+
88
+ /**
89
+ * Standard envelope appended to task mutation results.
90
+ *
91
+ * Existing operation-specific legacy fields (`task`, `deletedTask`, `changes`,
92
+ * etc.) remain available, while new consumers can read the normalized buckets.
93
+ *
94
+ * @task T10608
95
+ */
96
+ export interface TaskMutationEnvelope<
97
+ TCreated = TaskRecord[],
98
+ TUpdated = TaskRecord[],
99
+ TDeleted = TaskRecord[],
100
+ > {
101
+ /** Whether this was a preview-only mutation. */
102
+ dryRun?: boolean;
103
+ /** Created task records. Empty for update/delete-only mutations. */
104
+ created: TCreated;
105
+ /** Updated task records. Empty for create/delete-only mutations. */
106
+ updated: TUpdated;
107
+ /** Deleted task records. Empty for create/update-only mutations. */
108
+ deleted: TDeleted;
109
+ /** Total number of live rows/entities affected by the mutation. */
110
+ affectedCount: number;
111
+ /** Structured partial-success/preflight warnings. */
112
+ mutationWarnings: TaskMutationWarning[];
113
+ /** Standard dry-run projection. Present only for dry-run/preflight responses. */
114
+ dryRunSummary?: TaskMutationDryRunSummary;
115
+ }
116
+
117
+ const taskMutationWarningSeveritySchema = z.enum(['info', 'warning']);
118
+
119
+ /** Zod schema for structured task mutation warnings. @task T10608 */
120
+ export const taskMutationWarningSchema = z.object({
121
+ code: z.string().min(1),
122
+ message: z.string().min(1),
123
+ severity: taskMutationWarningSeveritySchema.optional(),
124
+ taskId: z.string().min(1).optional(),
125
+ field: z.string().min(1).optional(),
126
+ index: z.number().int().nonnegative().optional(),
127
+ });
128
+
129
+ /** Zod schema for the standardized task mutation dry-run/preflight summary. @task T10608 */
130
+ export const taskMutationDryRunSummarySchema = z.object({
131
+ dryRun: z.literal(true),
132
+ wouldCreate: z.number().int().nonnegative(),
133
+ wouldUpdate: z.number().int().nonnegative(),
134
+ wouldDelete: z.number().int().nonnegative(),
135
+ wouldAffect: z.number().int().nonnegative(),
136
+ validatedCount: z.number().int().nonnegative(),
137
+ insertedCount: z.literal(0),
138
+ updatedCount: z.literal(0),
139
+ deletedCount: z.literal(0),
140
+ warnings: z.array(taskMutationWarningSchema),
141
+ });
142
+
143
+ const taskMutationTaskRecordSchema = z.object({ id: z.string().min(1) }).passthrough();
144
+
145
+ /** Zod schema for the standardized task mutation envelope. @task T10608 */
146
+ export const taskMutationEnvelopeSchema = z.object({
147
+ dryRun: z.boolean().optional(),
148
+ created: z.array(taskMutationTaskRecordSchema),
149
+ updated: z.array(taskMutationTaskRecordSchema),
150
+ deleted: z.array(taskMutationTaskRecordSchema),
151
+ affectedCount: z.number().int().nonnegative(),
152
+ mutationWarnings: z.array(taskMutationWarningSchema),
153
+ dryRunSummary: taskMutationDryRunSummarySchema.optional(),
154
+ });
19
155
 
20
156
  // ---------------------------------------------------------------------------
21
157
  // TaskView types (canonical task projection consumed by SDK, CLI, and REST)
@@ -152,6 +288,428 @@ export interface TaskView {
152
288
  nextAction: TaskViewNextAction;
153
289
  }
154
290
 
291
+ // ---------------------------------------------------------------------------
292
+ // Completion evaluation types (T10591)
293
+ // ---------------------------------------------------------------------------
294
+
295
+ export type CompletionCriterionKind = 'text' | 'evidence_bound' | 'child_task';
296
+
297
+ export type CompletionCriterionStatus = 'satisfied' | 'unsatisfied' | 'waived' | 'replaced';
298
+
299
+ export type CompletionBlockerReason =
300
+ | 'missing_evidence_binding'
301
+ | 'child_not_done'
302
+ | 'child_cancelled_requires_waiver'
303
+ | 'child_replacement_not_done'
304
+ | 'child_missing'
305
+ | 'done_parent_stale';
306
+
307
+ /**
308
+ * Explicit operator waiver for a child-task completion criterion. @task T10592
309
+ *
310
+ * Waivers are intentionally criterion-scoped: the same cancelled child may back
311
+ * multiple parent criteria, and each criterion needs its own reason, actor, and
312
+ * timestamp for auditable completion rollups.
313
+ */
314
+ export interface CompletionCriterionWaiver {
315
+ /** AC row id that is being waived. */
316
+ criterionAcId: string;
317
+ /** Child task id referenced by the waived AC row. */
318
+ childTaskId: string;
319
+ /** Human/operator rationale for accepting the cancelled child as waived. */
320
+ reason: string;
321
+ /** Actor that approved the waiver. */
322
+ actor: string;
323
+ /** ISO-8601 timestamp recording when the waiver was approved. */
324
+ waivedAt: string;
325
+ }
326
+
327
+ /**
328
+ * Explicit replacement policy for a child-task completion criterion. @task T10592
329
+ *
330
+ * Replacements only satisfy the original criterion when the replacement child is
331
+ * itself done; otherwise the criterion remains unsatisfied with a replacement
332
+ * blocker so rollups can distinguish replaced work from waived or satisfied work.
333
+ */
334
+ export interface CompletionCriterionReplacement {
335
+ /** AC row id whose original child requirement is replaced. */
336
+ criterionAcId: string;
337
+ /** Original child task id referenced by the AC row. */
338
+ originalChildTaskId: string;
339
+ /** Replacement child task id that must be done to satisfy this criterion. */
340
+ replacementChildTaskId: string;
341
+ /** Human/operator rationale for replacing the original child. */
342
+ reason: string;
343
+ /** Actor that approved the replacement. */
344
+ actor: string;
345
+ /** ISO-8601 timestamp recording when the replacement was approved. */
346
+ replacedAt: string;
347
+ }
348
+
349
+ /** A typed per-AC completion evaluation row. @task T10591 */
350
+ export interface CompletionCriterionEvaluation {
351
+ /** Stable AC row id from `task_acceptance_criteria.id`. */
352
+ acId: string;
353
+ /** Human alias derived from ordinal, e.g. AC1. */
354
+ alias: string;
355
+ /** Stored AC text. */
356
+ text: string;
357
+ /** Criteria kind from the AC table. */
358
+ kind: CompletionCriterionKind;
359
+ /** Whether this criterion is satisfied, unsatisfied, explicitly waived, or replaced. */
360
+ status: CompletionCriterionStatus;
361
+ /** Blocking reason when status is unsatisfied. */
362
+ reason?: CompletionBlockerReason;
363
+ /** Target child task id for child_task rows. */
364
+ targetTaskId?: string;
365
+ /** Observed child task status for child_task rows. */
366
+ targetTaskStatus?: TaskStatus;
367
+ /** Explicit waiver metadata when status is `waived`. */
368
+ waiver?: CompletionCriterionWaiver;
369
+ /** Explicit replacement metadata when a replacement policy applies. */
370
+ replacement?: CompletionCriterionReplacement;
371
+ /** Observed status of the replacement child task, when a replacement policy applies. */
372
+ replacementTaskStatus?: TaskStatus;
373
+ /** Number of evidence bindings attached to this AC. */
374
+ evidenceBindings: number;
375
+ }
376
+
377
+ export type CompletionStaleReason = 'done_parent_has_unsatisfied_criteria';
378
+
379
+ /** Lifecycle/audit actions included in completion context packs. @task T10594 */
380
+ export type CompletionHistoryEventAction =
381
+ | 'task_completed'
382
+ | 'task_reopened'
383
+ | 'task_cancelled'
384
+ | 'task_uncancelled'
385
+ | 'task_reparented'
386
+ | 'ac_projection_rebuilt';
387
+
388
+ /** Task relation bucket for an audit event carried by a completion context pack. @task T10594 */
389
+ export type CompletionHistoryEventRelation = 'self' | 'parent' | 'child' | 'sibling' | 'related';
390
+
391
+ /** Structured lifecycle/audit event available to completion evaluation. @task T10594 */
392
+ export interface CompletionHistoryEvent {
393
+ id: string;
394
+ timestamp: string;
395
+ action: CompletionHistoryEventAction;
396
+ taskId: string;
397
+ relation: CompletionHistoryEventRelation;
398
+ actor: string;
399
+ details?: Record<string, unknown>;
400
+ before?: Record<string, unknown>;
401
+ after?: Record<string, unknown>;
402
+ }
403
+
404
+ /** Event counts precomputed for explainCompletion and machine consumers. @task T10594 */
405
+ export interface CompletionContextPackSummary {
406
+ totalEvents: number;
407
+ byAction: Record<CompletionHistoryEventAction, number>;
408
+ byRelation: Record<CompletionHistoryEventRelation, number>;
409
+ latestEventAt: string | null;
410
+ }
411
+
412
+ /** Bounded recent lifecycle context sourced from audit_log. @task T10594 */
413
+ export interface CompletionContextPack {
414
+ taskId: string;
415
+ generatedAt: string;
416
+ source: 'audit_log';
417
+ window: {
418
+ limit: number;
419
+ since?: string;
420
+ relationDepth: number;
421
+ relatedTaskIds: string[];
422
+ };
423
+ events: CompletionHistoryEvent[];
424
+ summary: CompletionContextPackSummary;
425
+ }
426
+
427
+ /** Builder query controls for completion context packs. @task T10594 */
428
+ export interface CompletionContextPackOptions {
429
+ limit?: number;
430
+ since?: string;
431
+ relationDepth?: number;
432
+ }
433
+
434
+ /** Contract-backed aggregate returned by `evaluateCompletion`. @task T10591 */
435
+ export interface CompletionEvaluation {
436
+ taskId: string;
437
+ taskStatus: TaskStatus;
438
+ /** True only when there are no unsatisfied criteria and the parent is not stale. */
439
+ ready: boolean;
440
+ stale: boolean;
441
+ staleReasons: CompletionStaleReason[];
442
+ /** Optional bounded audit context used by explanation/evaluation consumers. @task T10594 */
443
+ contextPack?: CompletionContextPack;
444
+ satisfied: CompletionCriterionEvaluation[];
445
+ unsatisfied: CompletionCriterionEvaluation[];
446
+ waived: CompletionCriterionEvaluation[];
447
+ replaced: CompletionCriterionEvaluation[];
448
+ totals: {
449
+ criteria: number;
450
+ satisfied: number;
451
+ unsatisfied: number;
452
+ waived: number;
453
+ replaced: number;
454
+ };
455
+ }
456
+
457
+ /** Human-readable explanation generated from a CompletionEvaluation. @task T10591 */
458
+ export interface CompletionExplanation {
459
+ taskId: string;
460
+ ready: boolean;
461
+ stale: boolean;
462
+ summary: string;
463
+ /** Recent lifecycle context referenced by the summary/blockers when available. @task T10594 */
464
+ contextPack?: CompletionContextPack;
465
+ blockers: CompletionCriterionEvaluation[];
466
+ }
467
+
468
+ /** Parameter contract for `tasks.completion.list`. @task T10607 */
469
+ export interface CompletionListParams {
470
+ /** Task whose completion criteria should be listed. */
471
+ taskId: string;
472
+ /** Optional status filter for criteria rows. */
473
+ status?: CompletionCriterionStatus;
474
+ /** Optional criterion kind filter. */
475
+ kind?: CompletionCriterionKind;
476
+ }
477
+
478
+ /** Result contract for `tasks.completion.list`. @task T10607 */
479
+ export interface CompletionListResult {
480
+ taskId: string;
481
+ criteria: CompletionCriterionEvaluation[];
482
+ totals: CompletionEvaluation['totals'];
483
+ }
484
+
485
+ /** Parameter contract for `tasks.completion.evaluate`. @task T10607 */
486
+ export interface CompletionEvaluateParams extends CompletionContextPackOptions {
487
+ /** Task whose completion readiness should be evaluated. */
488
+ taskId: string;
489
+ /** Include bounded audit context in the evaluation response. */
490
+ includeContext?: boolean;
491
+ }
492
+
493
+ /** Result contract for `tasks.completion.evaluate`. @task T10607 */
494
+ export type CompletionEvaluateResult = CompletionEvaluation;
495
+
496
+ /** Parameter contract for `tasks.completion.explain`. @task T10607 */
497
+ export interface CompletionExplainParams extends CompletionEvaluateParams {}
498
+
499
+ /** Result contract for `tasks.completion.explain`. @task T10607 */
500
+ export type CompletionExplainResult = CompletionExplanation;
501
+
502
+ /** Typed stale-projection error codes returned by `tasks.projection.repair`. @task T10607 */
503
+ export type CompletionProjectionRepairErrorCode =
504
+ | 'projection_not_stale'
505
+ | 'criteria_missing'
506
+ | 'binding_target_missing'
507
+ | 'repair_conflict';
508
+
509
+ /** Stale completion-projection repair diagnostic. @task T10607 */
510
+ export interface CompletionProjectionRepairError {
511
+ code: CompletionProjectionRepairErrorCode;
512
+ message: string;
513
+ taskId: string;
514
+ acId?: string;
515
+ evidenceAtomId?: string;
516
+ }
517
+
518
+ /** Parameter contract for `tasks.projection.repair`. @task T10607 */
519
+ export interface CompletionProjectionRepairParams {
520
+ /** Task whose derived completion projection should be rebuilt/repaired. */
521
+ taskId: string;
522
+ /** Preview repair actions without writing projection state. */
523
+ dryRun?: boolean;
524
+ }
525
+
526
+ /** Result contract for `tasks.projection.repair`. @task T10607 */
527
+ export interface CompletionProjectionRepairResult {
528
+ taskId: string;
529
+ repaired: boolean;
530
+ dryRun: boolean;
531
+ staleBefore: boolean;
532
+ staleAfter: boolean;
533
+ errors: CompletionProjectionRepairError[];
534
+ }
535
+
536
+ const completionTaskStatusSchema = z.enum(TASK_STATUSES);
537
+ const completionCriterionKindSchema = z.enum(['text', 'evidence_bound', 'child_task']);
538
+ const completionCriterionStatusSchema = z.enum(['satisfied', 'unsatisfied', 'waived', 'replaced']);
539
+ const completionBlockerReasonSchema = z.enum([
540
+ 'missing_evidence_binding',
541
+ 'child_not_done',
542
+ 'child_cancelled_requires_waiver',
543
+ 'child_replacement_not_done',
544
+ 'child_missing',
545
+ 'done_parent_stale',
546
+ ]);
547
+ const completionStaleReasonSchema = z.enum(['done_parent_has_unsatisfied_criteria']);
548
+ const completionProjectionRepairErrorCodeSchema = z.enum([
549
+ 'projection_not_stale',
550
+ 'criteria_missing',
551
+ 'binding_target_missing',
552
+ 'repair_conflict',
553
+ ]);
554
+ const completionCriterionWaiverSchema = z.object({
555
+ criterionAcId: z.string().min(1),
556
+ childTaskId: z.string().min(1),
557
+ reason: z.string().min(1),
558
+ actor: z.string().min(1),
559
+ waivedAt: z.string().min(1),
560
+ });
561
+ const completionCriterionReplacementSchema = z.object({
562
+ criterionAcId: z.string().min(1),
563
+ originalChildTaskId: z.string().min(1),
564
+ replacementChildTaskId: z.string().min(1),
565
+ reason: z.string().min(1),
566
+ actor: z.string().min(1),
567
+ replacedAt: z.string().min(1),
568
+ });
569
+
570
+ /** Zod schema for one typed AC completion-result row. @task T10607 */
571
+ export const completionCriterionEvaluationSchema = z.object({
572
+ acId: z.string().min(1),
573
+ alias: z.string().min(1),
574
+ text: z.string(),
575
+ kind: completionCriterionKindSchema,
576
+ status: completionCriterionStatusSchema,
577
+ reason: completionBlockerReasonSchema.optional(),
578
+ targetTaskId: z.string().min(1).optional(),
579
+ targetTaskStatus: completionTaskStatusSchema.optional(),
580
+ waiver: completionCriterionWaiverSchema.optional(),
581
+ replacement: completionCriterionReplacementSchema.optional(),
582
+ replacementTaskStatus: completionTaskStatusSchema.optional(),
583
+ evidenceBindings: z.number().int().nonnegative(),
584
+ });
585
+
586
+ /** Zod schema for unsatisfied completion criteria. @task T10607 */
587
+ export const unsatisfiedCompletionCriterionSchema = completionCriterionEvaluationSchema.extend({
588
+ status: z.literal('unsatisfied'),
589
+ reason: completionBlockerReasonSchema,
590
+ });
591
+
592
+ const completionTotalsSchema = z.object({
593
+ criteria: z.number().int().nonnegative(),
594
+ satisfied: z.number().int().nonnegative(),
595
+ unsatisfied: z.number().int().nonnegative(),
596
+ waived: z.number().int().nonnegative(),
597
+ replaced: z.number().int().nonnegative(),
598
+ });
599
+ const completionContextPackSchema: z.ZodType<CompletionContextPack> = z.object({
600
+ taskId: z.string().min(1),
601
+ generatedAt: z.string().min(1),
602
+ source: z.literal('audit_log'),
603
+ window: z.object({
604
+ limit: z.number().int().positive(),
605
+ since: z.string().min(1).optional(),
606
+ relationDepth: z.number().int().nonnegative(),
607
+ relatedTaskIds: z.array(z.string().min(1)),
608
+ }),
609
+ events: z.array(
610
+ z.object({
611
+ id: z.string().min(1),
612
+ timestamp: z.string().min(1),
613
+ action: z.enum([
614
+ 'task_completed',
615
+ 'task_reopened',
616
+ 'task_cancelled',
617
+ 'task_uncancelled',
618
+ 'task_reparented',
619
+ 'ac_projection_rebuilt',
620
+ ]),
621
+ taskId: z.string().min(1),
622
+ relation: z.enum(['self', 'parent', 'child', 'sibling', 'related']),
623
+ actor: z.string().min(1),
624
+ details: z.record(z.string(), z.unknown()).optional(),
625
+ before: z.record(z.string(), z.unknown()).optional(),
626
+ after: z.record(z.string(), z.unknown()).optional(),
627
+ }),
628
+ ),
629
+ summary: z.object({
630
+ totalEvents: z.number().int().nonnegative(),
631
+ byAction: z.record(z.string(), z.number().int().nonnegative()),
632
+ byRelation: z.record(z.string(), z.number().int().nonnegative()),
633
+ latestEventAt: z.string().nullable(),
634
+ }),
635
+ });
636
+
637
+ /** Zod result contract for `tasks.completion.evaluate`. @task T10607 */
638
+ export const completionEvaluationSchema = z.object({
639
+ taskId: z.string().min(1),
640
+ taskStatus: completionTaskStatusSchema,
641
+ ready: z.boolean(),
642
+ stale: z.boolean(),
643
+ staleReasons: z.array(completionStaleReasonSchema),
644
+ contextPack: completionContextPackSchema.optional(),
645
+ satisfied: z.array(completionCriterionEvaluationSchema),
646
+ unsatisfied: z.array(unsatisfiedCompletionCriterionSchema),
647
+ waived: z.array(completionCriterionEvaluationSchema),
648
+ replaced: z.array(completionCriterionEvaluationSchema),
649
+ totals: completionTotalsSchema,
650
+ });
651
+
652
+ /** Zod result contract for `tasks.completion.explain`. @task T10607 */
653
+ export const completionExplanationSchema = z.object({
654
+ taskId: z.string().min(1),
655
+ ready: z.boolean(),
656
+ stale: z.boolean(),
657
+ summary: z.string(),
658
+ contextPack: completionContextPackSchema.optional(),
659
+ blockers: z.array(completionCriterionEvaluationSchema),
660
+ });
661
+
662
+ /** Zod params for `tasks.completion.list`. @task T10607 */
663
+ export const completionListParamsSchema = z.object({
664
+ taskId: z.string().min(1),
665
+ status: completionCriterionStatusSchema.optional(),
666
+ kind: completionCriterionKindSchema.optional(),
667
+ });
668
+
669
+ /** Zod result contract for `tasks.completion.list`. @task T10607 */
670
+ export const completionListResultSchema = z.object({
671
+ taskId: z.string().min(1),
672
+ criteria: z.array(completionCriterionEvaluationSchema),
673
+ totals: completionTotalsSchema,
674
+ });
675
+
676
+ /** Zod params for `tasks.completion.evaluate` and `tasks.completion.explain`. @task T10607 */
677
+ export const completionEvaluateParamsSchema = z.object({
678
+ taskId: z.string().min(1),
679
+ includeContext: z.boolean().optional(),
680
+ limit: z.number().int().positive().optional(),
681
+ since: z.string().min(1).optional(),
682
+ relationDepth: z.number().int().nonnegative().optional(),
683
+ });
684
+
685
+ /** Zod params for `tasks.completion.explain`. @task T10607 */
686
+ export const completionExplainParamsSchema = completionEvaluateParamsSchema;
687
+
688
+ /** Zod schema for typed stale-projection repair errors. @task T10607 */
689
+ export const completionProjectionRepairErrorSchema = z.object({
690
+ code: completionProjectionRepairErrorCodeSchema,
691
+ message: z.string().min(1),
692
+ taskId: z.string().min(1),
693
+ acId: z.string().min(1).optional(),
694
+ evidenceAtomId: z.string().min(1).optional(),
695
+ });
696
+
697
+ /** Zod params for `tasks.projection.repair`. @task T10607 */
698
+ export const completionProjectionRepairParamsSchema = z.object({
699
+ taskId: z.string().min(1),
700
+ dryRun: z.boolean().optional(),
701
+ });
702
+
703
+ /** Zod result contract for `tasks.projection.repair`. @task T10607 */
704
+ export const completionProjectionRepairResultSchema = z.object({
705
+ taskId: z.string().min(1),
706
+ repaired: z.boolean(),
707
+ dryRun: z.boolean(),
708
+ staleBefore: z.boolean(),
709
+ staleAfter: z.boolean(),
710
+ errors: z.array(completionProjectionRepairErrorSchema),
711
+ });
712
+
155
713
  // ---------------------------------------------------------------------------
156
714
  // Task tree node (used by tasks.tree operation)
157
715
  // ---------------------------------------------------------------------------