(
previousTask: Task,
input: unknown,
options: ValidateTaskUpdateOptions = {},
)
| 429 | export interface ValidateTaskUpdateOptions extends TaskStatusTransitionOptions {} |
| 430 | |
| 431 | export function validateTaskUpdate( |
| 432 | previousTask: Task, |
| 433 | input: unknown, |
| 434 | options: ValidateTaskUpdateOptions = {}, |
| 435 | ): TaskLedgerNormalizeResult<{ |
| 436 | subject?: string; |
| 437 | status?: TaskStatus; |
| 438 | blockedReason?: string; |
| 439 | failureReason?: string; |
| 440 | completionEvidence?: string; |
| 441 | explicitReopen?: boolean; |
| 442 | }> { |
| 443 | const normalized = normalizeUpdateTaskInput(input); |
| 444 | if (!normalized.ok) return normalized; |
| 445 | const { explicitReopen: patchExplicitReopen, ...taskPatch } = normalized.value; |
| 446 | const nextTask: Task = { |
| 447 | ...previousTask, |
| 448 | ...taskPatch, |
| 449 | }; |
| 450 | const explicitReopen = options.explicitReopen === true || patchExplicitReopen === true; |
| 451 | if ( |
| 452 | normalized.value.status !== undefined && |
| 453 | !canTransitionTaskStatus(previousTask.status, normalized.value.status, { |
| 454 | ...options, |
| 455 | explicitReopen, |
| 456 | }) |
| 457 | ) { |
| 458 | const recovery = |
| 459 | previousTask.status === 'pending' && normalized.value.status === 'completed' |
| 460 | ? '. Call task_update with status "in_progress" first, then call task_update with status "completed" and completionEvidence.' |
| 461 | : ''; |
| 462 | return invalid( |
| 463 | 'invalid_transition', |
| 464 | `Invalid task status transition from ${previousTask.status} to ${normalized.value.status}${recovery}`, |
| 465 | ); |
| 466 | } |
| 467 | const evidence = validateTaskEvidence(nextTask); |
| 468 | if (!evidence.ok) return evidence; |
| 469 | return normalized; |
| 470 | } |
| 471 | |
| 472 | export interface TaskResumeTrustRefs { |
| 473 | corruptLedger?: boolean; |
no test coverage detected