@cleocode/contracts 2026.5.102 → 2026.5.104

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.
package/src/boundary.ts CHANGED
@@ -512,7 +512,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
512
512
  },
513
513
  amendments: ['adr-077-worktreeinclude', 'adr-078-boundary-registry'],
514
514
  rationale:
515
- 'Worktree primitives core (1,352 LOC) vendored from /mnt/projects/worktrunk per D010, consumed by worktree-napi. Reference implementation of the boundary-registry pattern. Internal-only today; publish=true is a future option if external worktree-tooling emerges.',
515
+ 'Refactored SoC per ADR-078 amendment 2026-05-23. Worktree primitives core (1,352 LOC) vendored from /mnt/projects/worktrunk per D010, consumed by worktree-napi. Reference implementation of the boundary-registry pattern. Full SDK surface documented in crates/worktrunk-core/README.md (T10223). Internal-only today; publish=true is a future option if external worktree-tooling emerges.',
516
516
  },
517
517
 
518
518
  // ============================================================
@@ -2500,6 +2500,36 @@ export const OPERATIONS: OperationDef[] = [
2500
2500
  },
2501
2501
  ] satisfies ParamDef[],
2502
2502
  },
2503
+ {
2504
+ // T10121 — idempotent cron-safe saga auto-close repair. Re-applies
2505
+ // T10116 logic for sagas whose members reached 100% terminal via paths
2506
+ // OTHER than completeTask (bulk SQL, crash recovery, manual edits).
2507
+ // Supersedes T10098 standalone scope.
2508
+ gateway: 'mutate',
2509
+ domain: 'tasks',
2510
+ operation: 'saga.reconcile',
2511
+ description:
2512
+ 'tasks.saga.reconcile (mutate) — idempotent cron-safe saga auto-close repair; per-saga advisory lock + audit log; supersedes T10098',
2513
+ tier: 0,
2514
+ idempotent: true,
2515
+ sessionRequired: false,
2516
+ requiredParams: [],
2517
+ params: [
2518
+ {
2519
+ name: 'sagaId',
2520
+ type: 'string',
2521
+ required: false,
2522
+ description: 'Optional saga task ID. Omit to walk every saga.',
2523
+ cli: { positional: true },
2524
+ },
2525
+ {
2526
+ name: 'dryRun',
2527
+ type: 'boolean',
2528
+ required: false,
2529
+ description: 'Report what would happen without mutating rows or writing audit log',
2530
+ },
2531
+ ] satisfies ParamDef[],
2532
+ },
2503
2533
  {
2504
2534
  gateway: 'mutate',
2505
2535
  domain: 'tasks',
@@ -1176,6 +1176,87 @@ export interface TasksSagaRepairResult {
1176
1176
  note?: string;
1177
1177
  }
1178
1178
 
1179
+ /**
1180
+ * Per-saga reconciliation action returned by `tasks.saga.reconcile`.
1181
+ *
1182
+ * @task T10121
1183
+ */
1184
+ export type TasksSagaReconcileAction = 'close' | 'no-op' | 'blocked' | 'error';
1185
+
1186
+ /**
1187
+ * Params for `tasks.saga.reconcile` — idempotent cron-safe saga auto-close
1188
+ * repair. Re-applies the T10116 auto-close logic for sagas whose members
1189
+ * reached 100% terminal status via paths OTHER than `completeTask` (bulk
1190
+ * SQL repair, crash recovery, manual state edits).
1191
+ *
1192
+ * @task T10121
1193
+ * @see ADR-073-above-epic-naming.md §1.3
1194
+ */
1195
+ export interface TasksSagaReconcileParams {
1196
+ /**
1197
+ * Optional single saga to reconcile. When omitted, the verb walks every
1198
+ * saga returned by `taskList({ type: 'epic', label: 'saga' })`.
1199
+ */
1200
+ sagaId?: string;
1201
+ /**
1202
+ * When `true`, run in report-only mode — log what would happen without
1203
+ * mutating any rows or writing to the audit log.
1204
+ */
1205
+ dryRun?: boolean;
1206
+ }
1207
+
1208
+ /**
1209
+ * Per-saga reconciliation outcome surfaced inside
1210
+ * {@link TasksSagaReconcileResult}.
1211
+ *
1212
+ * @task T10121
1213
+ */
1214
+ export interface TasksSagaReconcileEntry {
1215
+ /** Saga task ID this entry refers to. */
1216
+ sagaId: string;
1217
+ /** The action the reconciler took for this saga. */
1218
+ action: TasksSagaReconcileAction;
1219
+ /** Member task IDs considered by the closure check. */
1220
+ members: string[];
1221
+ /** Members that satisfied the terminal-status predicate. */
1222
+ terminalMembers: string[];
1223
+ /** Members that did NOT satisfy the terminal-status predicate. */
1224
+ pendingMembers: string[];
1225
+ /** Saga status BEFORE this run. */
1226
+ statusBefore: string;
1227
+ /** Saga status AFTER this run (== `statusBefore` for no-op/blocked/error). */
1228
+ statusAfter: string;
1229
+ /** Free-form human-readable reason recorded for the audit entry. */
1230
+ reason: string;
1231
+ /** ISO 8601 timestamp the decision was recorded. */
1232
+ timestamp: string;
1233
+ }
1234
+
1235
+ /**
1236
+ * Result of `tasks.saga.reconcile` — aggregate counters + per-saga entries
1237
+ * in stable id order.
1238
+ *
1239
+ * @task T10121
1240
+ */
1241
+ export interface TasksSagaReconcileResult {
1242
+ /** Total number of sagas inspected (== `entries.length`). */
1243
+ total: number;
1244
+ /** Number of sagas the run flipped to `status='done'`. */
1245
+ closed: number;
1246
+ /** Number of sagas already in the correct terminal state. */
1247
+ noOp: number;
1248
+ /** Number of sagas blocked behind a concurrent lock holder. */
1249
+ blocked: number;
1250
+ /** Number of sagas with pending non-terminal members (not closed). */
1251
+ pending: number;
1252
+ /** Number of sagas that errored out during reconciliation. */
1253
+ errors: number;
1254
+ /** Whether this run ran in dry-run mode. */
1255
+ dryRun: boolean;
1256
+ /** Detailed per-saga entries in stable id order. */
1257
+ entries: TasksSagaReconcileEntry[];
1258
+ }
1259
+
1179
1260
  /** Params for `tasks.saga.rollup` — aggregate member Epic statuses. */
1180
1261
  export interface TasksSagaRollupParams {
1181
1262
  /** Saga task ID. */
@@ -1268,4 +1349,6 @@ export type TasksOps = {
1268
1349
  readonly 'saga.rollup': readonly [TasksSagaRollupParams, TasksSagaRollupResult];
1269
1350
  /** T10117 — repair an I5-violating saga. */
1270
1351
  readonly 'saga.repair': readonly [TasksSagaRepairParams, TasksSagaRepairResult];
1352
+ /** T10121 — idempotent cron-safe auto-close repair (supersedes T10098 scope). */
1353
+ readonly 'saga.reconcile': readonly [TasksSagaReconcileParams, TasksSagaReconcileResult];
1271
1354
  };