task-manager-logic 0.1.5 → 0.1.6

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/processors.ts +35 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-manager-logic",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "description": "Runtime-first TaskSpace transition processors and backend-neutral in-memory owner",
6
6
  "type": "module",
package/src/processors.ts CHANGED
@@ -497,15 +497,10 @@ export async function replanTask(
497
497
  tasks: input.plan.tasks,
498
498
  relations: input.plan.relations,
499
499
  };
500
+ assertReplanDurableTasksImmutable(snapshot, definition);
500
501
  validateTaskSpaceDefinition(definition, normalizeTaskProcessorConfig(config));
501
502
  const artifactBodies = input.artifactBodies ?? [];
502
503
  await assertArtifactAuthority(runtime.owner, input.taskSpaceId, definition.tasks.flatMap(({ inputArtifacts }) => inputArtifacts), artifactBodies);
503
- const nextIds = new Set(definition.tasks.map(({ taskId }) => taskId));
504
- for (const prior of snapshot.tasks) {
505
- if (!nextIds.has(prior.taskId) && (isTerminal(prior.status) || prior.activeClaim)) {
506
- throw new TaskManagerError('terminal-task-immutable', `replan cannot remove durable task ${prior.taskId}`);
507
- }
508
- }
509
504
  const provisional: TaskRecord[] = definition.tasks.map((nextDefinition) => {
510
505
  const prior = taskRecordById(snapshot, nextDefinition.taskId);
511
506
  if (prior) return { ...prior, definition: nextDefinition, updatedAt: input.replannedAt };
@@ -714,6 +709,40 @@ function replaceTask(snapshot: TaskSpaceSnapshot, replacement: TaskRecord): Task
714
709
  return snapshot.tasks.map((task) => task.taskId === replacement.taskId ? replacement : task);
715
710
  }
716
711
 
712
+ function assertReplanDurableTasksImmutable(
713
+ snapshot: TaskSpaceSnapshot,
714
+ nextDefinition: TaskSpaceDefinition,
715
+ ): void {
716
+ for (const prior of snapshot.tasks) {
717
+ const next = nextDefinition.tasks.find(({ taskId }) => taskId === prior.taskId);
718
+ if (!next) {
719
+ if (isTerminal(prior.status) || prior.activeClaim) {
720
+ throw new TaskManagerError('terminal-task-immutable', `replan cannot remove durable task ${prior.taskId}`);
721
+ }
722
+ continue;
723
+ }
724
+ if (!isTerminal(prior.status)) continue;
725
+ const priorRelations = incidentRelations(snapshot.definition, prior.taskId);
726
+ const nextRelations = incidentRelations(nextDefinition, prior.taskId);
727
+ if (canonicalOwnDataDigest(prior.definition) !== canonicalOwnDataDigest(next)
728
+ || canonicalOwnDataDigest(priorRelations) !== canonicalOwnDataDigest(nextRelations)) {
729
+ throw new TaskManagerError(
730
+ 'terminal-task-immutable',
731
+ `replan cannot rewrite terminal task ${prior.taskId} definition, profile, identity or relations`,
732
+ );
733
+ }
734
+ }
735
+ }
736
+
737
+ function incidentRelations(
738
+ definition: TaskSpaceDefinition,
739
+ taskId: TaskId,
740
+ ): TaskSpaceDefinition['relations'] {
741
+ return definition.relations.filter((relation) => relation.kind === 'parent-child'
742
+ ? relation.parentTaskId === taskId || relation.childTaskId === taskId
743
+ : relation.predecessorTaskId === taskId || relation.successorTaskId === taskId);
744
+ }
745
+
717
746
  function dependenciesSucceeded(
718
747
  completionGraph: TaskCompletionGraph,
719
748
  tasks: readonly TaskRecord[],