@outbuild-company/schedule-core 1.9.0 → 1.9.2

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/dist/index.js CHANGED
@@ -399,16 +399,10 @@ var FIELD_REGISTRY = {
399
399
  valueKind: "reference",
400
400
  extract: (activity) => activity.calendarId === null ? null : String(activity.calendarId)
401
401
  },
402
- // Normalized to string like calendarId. It is stored as a number, so it used
403
- // to reach compareValues through the numeric branch while its sibling
404
- // reference went through the string one — the same field kind comparing two
405
- // different ways. localeCompare with numeric:true keeps the digit ordering.
406
402
  subcontractId: {
407
403
  valueKind: "reference",
408
404
  extract: (activity) => activity.subcontractId === null ? null : String(activity.subcontractId)
409
405
  },
410
- // Declared boolean, not enum: it is one, and saying so is what lets the enum
411
- // branch demand an order without inventing one for true/false.
412
406
  isCritical: {
413
407
  valueKind: "boolean",
414
408
  extract: (activity) => activity.isCritical
@@ -418,107 +412,6 @@ function getFieldDescriptor(field) {
418
412
  return FIELD_REGISTRY[field] ?? null;
419
413
  }
420
414
 
421
- // src/internal/state/view-state.ts
422
- function isRelativePlacementPinValid(activityId, pin, parentOf) {
423
- const branchParentId = String(pin.branchParentId);
424
- const currentParentId = String(parentOf(activityId) ?? "0");
425
- if (currentParentId !== branchParentId) return false;
426
- const referenceId = String(pin.referenceId);
427
- return referenceId === branchParentId || String(parentOf(referenceId) ?? "0") === branchParentId;
428
- }
429
- var ViewStateStore = class {
430
- checkedSet = /* @__PURE__ */ new Set();
431
- hiddenSet = /* @__PURE__ */ new Set();
432
- activeFilter = null;
433
- activeOrder = null;
434
- relativePlacementPins = /* @__PURE__ */ new Map();
435
- isChecked(activityId) {
436
- return this.checkedSet.has(activityId);
437
- }
438
- isVisible(activityId) {
439
- return !this.hiddenSet.has(activityId);
440
- }
441
- hiddenIds() {
442
- return [...this.hiddenSet];
443
- }
444
- getActiveFilter() {
445
- return this.activeFilter;
446
- }
447
- setActiveFilter(nextFilter) {
448
- this.activeFilter = nextFilter;
449
- }
450
- getActiveOrder() {
451
- return this.activeOrder;
452
- }
453
- setActiveOrder(nextOrder) {
454
- this.activeOrder = nextOrder;
455
- }
456
- setRelativePlacementPin(activityId, pin) {
457
- this.relativePlacementPins.set(String(activityId), {
458
- referenceId: String(pin.referenceId),
459
- branchParentId: String(pin.branchParentId),
460
- side: pin.side
461
- });
462
- }
463
- getRelativePlacementPins() {
464
- return this.relativePlacementPins;
465
- }
466
- restoreRelativePlacementPins(pins) {
467
- this.relativePlacementPins = new Map(pins);
468
- }
469
- checkedIds() {
470
- return [...this.checkedSet];
471
- }
472
- checkedCount() {
473
- return this.checkedSet.size;
474
- }
475
- setChecked(activityId, nextChecked) {
476
- if (nextChecked === this.checkedSet.has(activityId)) return false;
477
- if (nextChecked) this.checkedSet.add(activityId);
478
- else this.checkedSet.delete(activityId);
479
- return true;
480
- }
481
- setVisible(activityId, nextVisible) {
482
- if (nextVisible === !this.hiddenSet.has(activityId)) return false;
483
- if (nextVisible) this.hiddenSet.delete(activityId);
484
- else this.hiddenSet.add(activityId);
485
- return true;
486
- }
487
- forget(activityId) {
488
- this.checkedSet.delete(activityId);
489
- this.hiddenSet.delete(activityId);
490
- this.relativePlacementPins.delete(activityId);
491
- for (const [pinnedId, pin] of this.relativePlacementPins) {
492
- if (pin.referenceId === activityId) {
493
- this.relativePlacementPins.delete(pinnedId);
494
- }
495
- }
496
- }
497
- hydrate(seed2) {
498
- this.checkedSet = new Set(seed2.checkedIds ?? []);
499
- this.hiddenSet = new Set(seed2.hiddenIds ?? []);
500
- this.activeFilter = null;
501
- this.activeOrder = null;
502
- this.relativePlacementPins = /* @__PURE__ */ new Map();
503
- }
504
- snapshot() {
505
- return {
506
- checked: new Set(this.checkedSet),
507
- hidden: new Set(this.hiddenSet),
508
- activeFilter: this.activeFilter,
509
- activeOrder: this.activeOrder,
510
- relativePlacementPins: new Map(this.relativePlacementPins)
511
- };
512
- }
513
- restore(snapshot) {
514
- this.checkedSet = new Set(snapshot.checked);
515
- this.hiddenSet = new Set(snapshot.hidden);
516
- this.activeFilter = snapshot.activeFilter;
517
- this.activeOrder = snapshot.activeOrder;
518
- this.relativePlacementPins = new Map(snapshot.relativePlacementPins);
519
- }
520
- };
521
-
522
415
  // src/internal/filter/evaluate-filter.ts
523
416
  function toTargetDate(value) {
524
417
  if (value instanceof Date) return value;
@@ -645,7 +538,6 @@ function evaluateVisibleIds(input) {
645
538
  function seedFilterProjection(input) {
646
539
  const { activities, parentOf, filter, context } = input;
647
540
  const matchedIds = /* @__PURE__ */ new Set();
648
- const pinnedVisibleIds = /* @__PURE__ */ new Set();
649
541
  const visibleReferenceCounts = /* @__PURE__ */ new Map();
650
542
  const parentIds = /* @__PURE__ */ new Map();
651
543
  const remainingChildren = /* @__PURE__ */ new Map();
@@ -666,35 +558,11 @@ function seedFilterProjection(input) {
666
558
  }
667
559
  return {
668
560
  matchedIds,
669
- pinnedVisibleIds,
670
561
  visibleReferenceCounts,
671
562
  parentIds,
672
563
  remainingChildren
673
564
  };
674
565
  }
675
- function applyRelativePlacementPins(seed2, pins) {
676
- if (pins === void 0) return;
677
- for (const [activityId, pin] of pins) {
678
- const pinnedId = String(activityId);
679
- const referenceId = String(pin.referenceId);
680
- if (!seed2.parentIds.has(pinnedId) || !seed2.parentIds.has(referenceId) || !isRelativePlacementPinValid(
681
- pinnedId,
682
- pin,
683
- (activityId2) => seed2.parentIds.get(activityId2) ?? null
684
- ) || (seed2.visibleReferenceCounts.get(referenceId) ?? 0) === 0) {
685
- continue;
686
- }
687
- seed2.pinnedVisibleIds.add(pinnedId);
688
- let currentId = pinnedId;
689
- while (currentId !== null) {
690
- seed2.visibleReferenceCounts.set(
691
- currentId,
692
- (seed2.visibleReferenceCounts.get(currentId) ?? 0) + 1
693
- );
694
- currentId = seed2.parentIds.get(currentId) ?? null;
695
- }
696
- }
697
- }
698
566
  function propagateVisibleReferenceCounts(seed2) {
699
567
  const { visibleReferenceCounts, parentIds, remainingChildren } = seed2;
700
568
  const pendingIds = [];
@@ -726,13 +594,11 @@ function collectVisibleIds(visibleReferenceCounts) {
726
594
  function buildFilterProjection(input) {
727
595
  const seed2 = seedFilterProjection(input);
728
596
  propagateVisibleReferenceCounts(seed2);
729
- applyRelativePlacementPins(seed2, input.relativePlacementPins);
730
597
  const visibleIds = collectVisibleIds(seed2.visibleReferenceCounts);
731
598
  return {
732
599
  filter: input.filter,
733
600
  context: input.context,
734
601
  matchedIds: seed2.matchedIds,
735
- pinnedVisibleIds: seed2.pinnedVisibleIds,
736
602
  visibleIds,
737
603
  visibleReferenceCounts: seed2.visibleReferenceCounts
738
604
  };
@@ -790,8 +656,6 @@ var OPERATORS_BY_KIND = {
790
656
  date: /* @__PURE__ */ new Set(["after", "before"]),
791
657
  "id-array": /* @__PURE__ */ new Set(["someOf", "notSomeOf"]),
792
658
  enum: /* @__PURE__ */ new Set(["someOf", "notSomeOf"]),
793
- // Same membership operators as enum: splitting the kinds was about ordering,
794
- // not about filtering, and these two filter exactly like an enum.
795
659
  boolean: /* @__PURE__ */ new Set(["someOf", "notSomeOf"]),
796
660
  reference: /* @__PURE__ */ new Set(["someOf", "notSomeOf"])
797
661
  };
@@ -914,8 +778,7 @@ function resolveFilter(adapter, filter, hoursPerDay) {
914
778
  activities,
915
779
  parentOf: (activityId) => adapter.getParentId(activityId),
916
780
  filter,
917
- context: buildFilterContext(hoursPerDay),
918
- relativePlacementPins: adapter.getRelativePlacementPins()
781
+ context: buildFilterContext(hoursPerDay)
919
782
  });
920
783
  return { visibleIds: projection.visibleIds, projection };
921
784
  }
@@ -966,118 +829,25 @@ function normalizeParentKey(parent) {
966
829
 
967
830
  // src/internal/hierarchy/visual-order.ts
968
831
  var NOT_SET_SORT_VALUE = Number.MAX_SAFE_INTEGER;
969
- var EMPTY_RELATIVE_PLACEMENT_PINS = /* @__PURE__ */ new Map();
970
- function buildRelativePlacementPinIndex(adapter) {
971
- const index = /* @__PURE__ */ new Map();
972
- for (const [activityId, pin] of adapter.getRelativePlacementPins?.() ?? []) {
973
- const pinnedId = String(activityId);
974
- const referenceId = String(pin.referenceId);
975
- if (adapter.getActivity(pinnedId) === null || adapter.getActivity(referenceId) === null || !isRelativePlacementPinValid(
976
- pinnedId,
977
- pin,
978
- (activityId2) => adapter.getParentId(activityId2)
979
- )) {
980
- continue;
981
- }
982
- const targetParentId = String(pin.branchParentId);
983
- const branchPins = index.get(targetParentId) ?? /* @__PURE__ */ new Map();
984
- branchPins.set(pinnedId, pin);
985
- index.set(targetParentId, branchPins);
986
- }
987
- return index;
988
- }
989
- function getChildrenInVisualOrder(parentId, adapter, pinIndex = buildRelativePlacementPinIndex(adapter)) {
832
+ function getChildrenInVisualOrder(parentId, adapter) {
990
833
  const children = collectChildrenSnapshots(parentId, adapter);
991
834
  const userOrder = adapter.getOrderComparator?.() ?? null;
992
835
  children.sort(
993
836
  userOrder === null ? compareActivitiesInVisualOrder : (a, b) => userOrder(a, b) || compareActivitiesInVisualOrder(a, b)
994
837
  );
995
- return applyRelativePlacementPins2(
996
- children.map((activity) => String(activity.id)),
997
- parentId,
998
- pinIndex.get(String(parentId)) ?? EMPTY_RELATIVE_PLACEMENT_PINS
999
- );
1000
- }
1001
- function applyRelativePlacementPins2(orderedIds, parentId, pins) {
1002
- const order = buildLinkedActivityOrder(orderedIds);
1003
- for (const [activityId, pin] of pins) {
1004
- const pinnedId = String(activityId);
1005
- const referenceId = String(pin.referenceId);
1006
- const isFirstChild = referenceId === String(parentId);
1007
- if (!order.previous.has(pinnedId) || !isFirstChild && (!order.previous.has(referenceId) || pinnedId === referenceId)) {
1008
- continue;
1009
- }
1010
- detachActivity(order, pinnedId);
1011
- if (isFirstChild) insertActivityFirst(order, pinnedId);
1012
- else insertActivityBeside(order, pinnedId, referenceId, pin.side);
1013
- }
1014
- return materializeActivityOrder(order);
1015
- }
1016
- function buildLinkedActivityOrder(orderedIds) {
1017
- const previous = /* @__PURE__ */ new Map();
1018
- const next = /* @__PURE__ */ new Map();
1019
- for (let index = 0; index < orderedIds.length; index++) {
1020
- const activityId = orderedIds[index];
1021
- if (activityId === void 0) continue;
1022
- previous.set(activityId, orderedIds[index - 1] ?? null);
1023
- next.set(activityId, orderedIds[index + 1] ?? null);
1024
- }
1025
- return { previous, next, head: orderedIds[0] ?? null };
1026
- }
1027
- function detachActivity(order, activityId) {
1028
- const before = order.previous.get(activityId) ?? null;
1029
- const after = order.next.get(activityId) ?? null;
1030
- if (before === null) order.head = after;
1031
- else order.next.set(before, after);
1032
- if (after !== null) order.previous.set(after, before);
1033
- }
1034
- function insertActivityFirst(order, activityId) {
1035
- order.previous.set(activityId, null);
1036
- order.next.set(activityId, order.head);
1037
- if (order.head !== null) order.previous.set(order.head, activityId);
1038
- order.head = activityId;
1039
- }
1040
- function insertActivityBeside(order, activityId, referenceId, side) {
1041
- if (side === "before") {
1042
- const before = order.previous.get(referenceId) ?? null;
1043
- order.previous.set(activityId, before);
1044
- order.next.set(activityId, referenceId);
1045
- order.previous.set(referenceId, activityId);
1046
- if (before === null) order.head = activityId;
1047
- else order.next.set(before, activityId);
1048
- return;
1049
- }
1050
- const after = order.next.get(referenceId) ?? null;
1051
- order.previous.set(activityId, referenceId);
1052
- order.next.set(activityId, after);
1053
- order.next.set(referenceId, activityId);
1054
- if (after !== null) order.previous.set(after, activityId);
1055
- }
1056
- function materializeActivityOrder(order) {
1057
- const result = [];
1058
- let activityId = order.head;
1059
- while (activityId !== null) {
1060
- result.push(activityId);
1061
- activityId = order.next.get(activityId) ?? null;
1062
- }
1063
- return result;
838
+ return children.map((activity) => String(activity.id));
1064
839
  }
1065
840
  function iterateInVisualOrder(adapter) {
1066
841
  const result = [];
1067
- const pinIndex = buildRelativePlacementPinIndex(adapter);
1068
842
  const visit = (activity) => {
1069
843
  result.push(activity);
1070
- const childIds = getChildrenInVisualOrder(
1071
- String(activity.id),
1072
- adapter,
1073
- pinIndex
1074
- );
844
+ const childIds = getChildrenInVisualOrder(String(activity.id), adapter);
1075
845
  for (const childId of childIds) {
1076
846
  const child = adapter.getActivity(childId);
1077
847
  if (child) visit(child);
1078
848
  }
1079
849
  };
1080
- const rootIds = getChildrenInVisualOrder(ROOT_PARENT_ID, adapter, pinIndex);
850
+ const rootIds = getChildrenInVisualOrder(ROOT_PARENT_ID, adapter);
1081
851
  for (const rootId of rootIds) {
1082
852
  const root = adapter.getActivity(rootId);
1083
853
  if (root) visit(root);
@@ -1138,9 +908,8 @@ function setHas(set, id) {
1138
908
  // src/dispatch/shared/collect-branch-order.ts
1139
909
  function collectBranchOrder(adapter) {
1140
910
  const branches = [];
1141
- const pinIndex = buildRelativePlacementPinIndex(adapter);
1142
911
  const visit = (parentId) => {
1143
- const childIds = getChildrenInVisualOrder(parentId, adapter, pinIndex);
912
+ const childIds = getChildrenInVisualOrder(parentId, adapter);
1144
913
  if (childIds.length > 1) {
1145
914
  branches.push({ parentId, childIds });
1146
915
  }
@@ -1153,9 +922,8 @@ function collectBranchOrder(adapter) {
1153
922
  }
1154
923
  function collectBranchOrderForParents(adapter, parentIds) {
1155
924
  const branches = [];
1156
- const pinIndex = buildRelativePlacementPinIndex(adapter);
1157
925
  for (const parentId of parentIds) {
1158
- const childIds = getChildrenInVisualOrder(parentId, adapter, pinIndex);
926
+ const childIds = getChildrenInVisualOrder(parentId, adapter);
1159
927
  if (childIds.length > 1) branches.push({ parentId, childIds });
1160
928
  }
1161
929
  return branches;
@@ -1188,107 +956,61 @@ function dispatchSortSet(action, deps) {
1188
956
  return { ok: true, changes };
1189
957
  }
1190
958
 
1191
- // src/columns/text/constants.ts
1192
- var TEXT = "text";
1193
-
1194
- // src/columns/description/constants.ts
1195
- var DESCRIPTION = "description";
1196
-
1197
- // src/columns/duration/constants.ts
1198
- var DURATION = "duration";
1199
-
1200
- // src/columns/progress/constants.ts
1201
- var PROGRESS = "progress";
1202
-
1203
- // src/columns/cost/constants.ts
1204
- var COST = "cost";
1205
-
1206
- // src/columns/used-cost/constants.ts
1207
- var USED_COST = "used_cost";
1208
-
1209
- // src/columns/hh-work-time/constants.ts
1210
- var HH_WORK_TIME = "hhWorkTime";
1211
-
1212
- // src/columns/start-date/constants.ts
1213
- var START_DATE = "start_date";
1214
-
1215
- // src/columns/end-date/constants.ts
1216
- var END_DATE = "end_date";
1217
-
1218
- // src/columns/constraint-type/constants.ts
1219
- var CONSTRAINT_TYPE = "constraint_type";
1220
-
1221
- // src/columns/constraint-date/constants.ts
1222
- var CONSTRAINT_DATE = "constraint_date";
1223
-
1224
- // src/columns/calendar-id/constants.ts
1225
- var CALENDAR_ID = "calendar_id";
1226
-
1227
- // src/columns/custom-id/constants.ts
1228
- var CUSTOM_ID = "custom_id";
1229
-
1230
- // src/columns/subcontract-id/constants.ts
1231
- var SUBCONTRACT_ID = "subcontractId";
1232
-
1233
- // src/columns/responsables/constants.ts
1234
- var RESPONSABLES = "responsables";
1235
-
1236
- // src/columns/tags/constants.ts
1237
- var TAGS = "tags";
1238
-
1239
- // src/columns/predecessors/constants.ts
1240
- var CUSTOM_PREDECESSORS = "custom_predecessors";
1241
-
1242
- // src/columns/successors/constants.ts
1243
- var CUSTOM_SUCESSORS = "custom_sucessors";
1244
-
1245
- // src/columns/real-work/constants.ts
1246
- var REAL_WORK = "real_work";
1247
-
1248
- // src/columns/real-cost/constants.ts
1249
- var REAL_COST = "real_cost";
1250
-
1251
- // src/columns/expected-progress/constants.ts
1252
- var EXPECTED_PROGRESS = "expected_progress";
1253
-
1254
- // src/columns/expected-progress-base/constants.ts
1255
- var EXPECTED_PROGRESS_BASE = "expected_progress_base";
1256
-
1257
- // src/columns/start-base/constants.ts
1258
- var START_BASE = "start_base";
1259
-
1260
- // src/columns/end-base/constants.ts
1261
- var END_BASE = "end_base";
1262
-
1263
- // src/columns/duration-base/constants.ts
1264
- var DURATION_BASE = "duration_base";
1265
-
1266
- // src/columns/cost-base/constants.ts
1267
- var COST_BASE = "cost_base";
1268
-
1269
- // src/columns/work-base/constants.ts
1270
- var WORK_BASE = "work_base";
1271
-
1272
- // src/columns/calendar-duration/constants.ts
1273
- var CALENDAR_DURATION = "calendarDuration";
1274
-
1275
- // src/columns/critical-path/constants.ts
1276
- var IS_CRITICAL = "is_critical";
1277
- var EARLY_START = "earlyStart";
1278
- var EARLY_FINISH = "earlyFinish";
1279
- var LATE_START = "lateStart";
1280
- var LATE_FINISH = "lateFinish";
1281
- var FREE_SLACK = "freeSlack";
1282
- var TOTAL_SLACK = "totalSlack";
1283
-
1284
- // src/columns/correlative-id/constants.ts
1285
- var CORRELATIVE_ID = "correlative_id";
959
+ // src/constants/columns.ts
960
+ var EDITABLE = {
961
+ TEXT: "text",
962
+ DESCRIPTION: "description",
963
+ DURATION: "duration",
964
+ PROGRESS: "progress",
965
+ COST: "cost",
966
+ USED_COST: "used_cost",
967
+ HH_WORK_TIME: "hhWorkTime",
968
+ START_DATE: "start_date",
969
+ END_DATE: "end_date",
970
+ CONSTRAINT_TYPE: "constraint_type",
971
+ CONSTRAINT_DATE: "constraint_date",
972
+ CALENDAR_ID: "calendar_id",
973
+ CUSTOM_ID: "custom_id",
974
+ SUBCONTRACT_ID: "subcontractId",
975
+ RESPONSABLES: "responsables",
976
+ TAGS: "tags",
977
+ CUSTOM_PREDECESSORS: "custom_predecessors",
978
+ CUSTOM_SUCESSORS: "custom_sucessors"
979
+ };
980
+ var READ_ONLY = {
981
+ REAL_WORK: "real_work",
982
+ REAL_COST: "real_cost",
983
+ EXPECTED_PROGRESS: "expected_progress",
984
+ EXPECTED_PROGRESS_BASE: "expected_progress_base",
985
+ START_BASE: "start_base",
986
+ END_BASE: "end_base",
987
+ DURATION_BASE: "duration_base",
988
+ COST_BASE: "cost_base",
989
+ WORK_BASE: "work_base",
990
+ CALENDAR_DURATION: "calendarDuration",
991
+ IS_CRITICAL: "is_critical",
992
+ EARLY_START: "earlyStart",
993
+ EARLY_FINISH: "earlyFinish",
994
+ LATE_START: "lateStart",
995
+ LATE_FINISH: "lateFinish",
996
+ FREE_SLACK: "freeSlack",
997
+ TOTAL_SLACK: "totalSlack",
998
+ CORRELATIVE_ID: "correlative_id",
999
+ UNIQUE_CORRELATIVE_ID: "unique_correlative_id",
1000
+ PONDERATOR: "ponderator",
1001
+ STATUS: "status"
1002
+ };
1003
+ var COLUMN = { ...EDITABLE, ...READ_ONLY };
1286
1004
 
1287
- // src/columns/unique-correlative-id/constants.ts
1288
- var UNIQUE_CORRELATIVE_ID = "unique_correlative_id";
1005
+ // src/shared/assert-never.ts
1006
+ function assertNever(value, message) {
1007
+ throw new Error(message);
1008
+ }
1289
1009
 
1290
- // src/columns/ponderator/constants.ts
1291
- var PONDERATOR = "ponderator";
1010
+ // src/autoscheduler/utils/cancellation.ts
1011
+ function createIsCurrent(id, getCurrentId) {
1012
+ return () => getCurrentId() === id;
1013
+ }
1292
1014
 
1293
1015
  // src/types.ts
1294
1016
  var WORK_TIME_DIRECTION = {
@@ -1307,71 +1029,8 @@ var STATUS = {
1307
1029
  DOING: "Doing"
1308
1030
  };
1309
1031
 
1310
- // src/columns/status/constants.ts
1311
- var STATUS_COLUMN = "status";
1312
- var DEFAULT_STATUS_CRITERIA = "Baseline";
1313
- function resolveStatusCriteria(value) {
1314
- return value === "Actual" ? "Actual" : DEFAULT_STATUS_CRITERIA;
1315
- }
1316
-
1317
- // src/constants/columns.ts
1318
- var EDITABLE = {
1319
- TEXT,
1320
- DESCRIPTION,
1321
- DURATION,
1322
- PROGRESS,
1323
- COST,
1324
- USED_COST,
1325
- HH_WORK_TIME,
1326
- START_DATE,
1327
- END_DATE,
1328
- CONSTRAINT_TYPE,
1329
- CONSTRAINT_DATE,
1330
- CALENDAR_ID,
1331
- CUSTOM_ID,
1332
- SUBCONTRACT_ID,
1333
- RESPONSABLES,
1334
- TAGS,
1335
- CUSTOM_PREDECESSORS,
1336
- CUSTOM_SUCESSORS
1337
- };
1338
- var READ_ONLY = {
1339
- REAL_WORK,
1340
- REAL_COST,
1341
- EXPECTED_PROGRESS,
1342
- EXPECTED_PROGRESS_BASE,
1343
- START_BASE,
1344
- END_BASE,
1345
- DURATION_BASE,
1346
- COST_BASE,
1347
- WORK_BASE,
1348
- CALENDAR_DURATION,
1349
- IS_CRITICAL,
1350
- EARLY_START,
1351
- EARLY_FINISH,
1352
- LATE_START,
1353
- LATE_FINISH,
1354
- FREE_SLACK,
1355
- TOTAL_SLACK,
1356
- CORRELATIVE_ID,
1357
- UNIQUE_CORRELATIVE_ID,
1358
- PONDERATOR,
1359
- STATUS: STATUS_COLUMN
1360
- };
1361
- var COLUMN = { ...EDITABLE, ...READ_ONLY };
1362
-
1363
- // src/shared/assert-never.ts
1364
- function assertNever(value, message) {
1365
- throw new Error(message);
1366
- }
1367
-
1368
- // src/autoscheduler/utils/cancellation.ts
1369
- function createIsCurrent(id, getCurrentId) {
1370
- return () => getCurrentId() === id;
1371
- }
1372
-
1373
1032
  // src/constraints/index.ts
1374
- var CONSTRAINT_TYPE2 = {
1033
+ var CONSTRAINT_TYPE = {
1375
1034
  ASAP: "asap",
1376
1035
  ALAP: "alap",
1377
1036
  SNET: "snet",
@@ -2288,7 +1947,7 @@ function processConstraints(activityIds, adapter, plans) {
2288
1947
  const activity = adapter.getActivity(id);
2289
1948
  if (!activity) continue;
2290
1949
  const type = activity.constraintType;
2291
- if (!type || type === CONSTRAINT_TYPE2.ASAP || type === CONSTRAINT_TYPE2.ALAP) {
1950
+ if (!type || type === CONSTRAINT_TYPE.ASAP || type === CONSTRAINT_TYPE.ALAP) {
2292
1951
  continue;
2293
1952
  }
2294
1953
  const bounds = computeConstraintBounds(activity, adapter);
@@ -2337,14 +1996,14 @@ function validateMilestoneConstraint(activity, successorStart) {
2337
1996
  const { constraintType, constraintDate } = activity;
2338
1997
  if (!constraintType || !constraintDate) return successorStart;
2339
1998
  switch (constraintType) {
2340
- case CONSTRAINT_TYPE2.MSO:
2341
- case CONSTRAINT_TYPE2.MFO:
1999
+ case CONSTRAINT_TYPE.MSO:
2000
+ case CONSTRAINT_TYPE.MFO:
2342
2001
  return new Date(constraintDate);
2343
- case CONSTRAINT_TYPE2.SNET:
2344
- case CONSTRAINT_TYPE2.FNET:
2002
+ case CONSTRAINT_TYPE.SNET:
2003
+ case CONSTRAINT_TYPE.FNET:
2345
2004
  return constraintDate > successorStart ? new Date(constraintDate) : successorStart;
2346
- case CONSTRAINT_TYPE2.SNLT:
2347
- case CONSTRAINT_TYPE2.FNLT:
2005
+ case CONSTRAINT_TYPE.SNLT:
2006
+ case CONSTRAINT_TYPE.FNLT:
2348
2007
  return constraintDate < successorStart ? new Date(constraintDate) : successorStart;
2349
2008
  default:
2350
2009
  return successorStart;
@@ -2473,7 +2132,7 @@ async function asapPass(orderedIds, links, adapter, options, isCurrent, plans) {
2473
2132
  });
2474
2133
  plan.earliestSchedulingStart = linkStart;
2475
2134
  plan.earliestSchedulingEnd = plan.endDate;
2476
- } else if (activity.constraintType === CONSTRAINT_TYPE2.MSO) {
2135
+ } else if (activity.constraintType === CONSTRAINT_TYPE.MSO) {
2477
2136
  if (plan.earliestStart) {
2478
2137
  plan.startDate = plan.earliestStart;
2479
2138
  plan.endDate = plan.earliestEnd ?? adapter.calculateEndDate({
@@ -2482,7 +2141,7 @@ async function asapPass(orderedIds, links, adapter, options, isCurrent, plans) {
2482
2141
  task: activity
2483
2142
  });
2484
2143
  }
2485
- } else if (activity.constraintType === CONSTRAINT_TYPE2.MFO) {
2144
+ } else if (activity.constraintType === CONSTRAINT_TYPE.MFO) {
2486
2145
  if (plan.latestStart) {
2487
2146
  plan.startDate = plan.latestStart;
2488
2147
  plan.endDate = plan.latestEnd ?? adapter.calculateEndDate({
@@ -2508,7 +2167,7 @@ async function alapPass(reversedIds, links, asapPlans, adapter, _options, isCurr
2508
2167
  const activityCount = allActivities.length;
2509
2168
  const alapActivityIds = [];
2510
2169
  for (const activity of allActivities) {
2511
- if (activity.constraintType === CONSTRAINT_TYPE2.ALAP) {
2170
+ if (activity.constraintType === CONSTRAINT_TYPE.ALAP) {
2512
2171
  alapActivityIds.push(activity.id);
2513
2172
  }
2514
2173
  }
@@ -2555,7 +2214,7 @@ async function alapPass(reversedIds, links, asapPlans, adapter, _options, isCurr
2555
2214
  sliceStart = yieldResult.newSliceStart;
2556
2215
  const activity = adapter.getActivity(activityId);
2557
2216
  if (!activity) continue;
2558
- if (activity.constraintType !== CONSTRAINT_TYPE2.ALAP) continue;
2217
+ if (activity.constraintType !== CONSTRAINT_TYPE.ALAP) continue;
2559
2218
  if (!activity.autoScheduling) continue;
2560
2219
  if (activity.progress === COMPLETED_PROGRESS) continue;
2561
2220
  if (isSummary(activity.type, adapter.getChildren(activityId).length > 0))
@@ -3064,9 +2723,7 @@ function applyLinkCreates(links, deps) {
3064
2723
  break;
3065
2724
  }
3066
2725
  duplicateKeys.set(key, linkId);
3067
- if (existingGraphHasCycle && // ponytail: preserve legacy sequential semantics; optimize with dynamic
3068
- // SCC reachability only if corrupt cyclic schedules become measurable.
3069
- wouldIntroduceCycle(edges, {
2726
+ if (existingGraphHasCycle && wouldIntroduceCycle(edges, {
3070
2727
  source: operation.source,
3071
2728
  target: operation.target
3072
2729
  })) {
@@ -3544,7 +3201,7 @@ function widenScopeToAlapActivities(group, allLinks, adapter) {
3544
3201
  const widenedAlapKeys = /* @__PURE__ */ new Set();
3545
3202
  for (const activity of adapter.getAllActivities()) {
3546
3203
  const activityKey = String(activity.id);
3547
- const isOutOfComponentAlap = activity.constraintType === CONSTRAINT_TYPE2.ALAP && !idSet.has(activityKey);
3204
+ const isOutOfComponentAlap = activity.constraintType === CONSTRAINT_TYPE.ALAP && !idSet.has(activityKey);
3548
3205
  if (!isOutOfComponentAlap) continue;
3549
3206
  widenedAlapKeys.add(activityKey);
3550
3207
  idSet.add(activityKey);
@@ -3630,6 +3287,84 @@ function roundProgressPerLevel(value) {
3630
3287
  return Number(value.toFixed(2));
3631
3288
  }
3632
3289
 
3290
+ // src/propagations/upward/progress-rollup.ts
3291
+ function recomputeAllProgressRollup(adapter) {
3292
+ for (const parentId of parentsDeepestFirst(adapter)) {
3293
+ rollupParent(parentId, adapter);
3294
+ }
3295
+ }
3296
+ function recomputeProgressRollupForParents(parentIdsDeepestFirst, adapter) {
3297
+ for (const parentId of parentIdsDeepestFirst) {
3298
+ rollupParent(parentId, adapter);
3299
+ }
3300
+ }
3301
+ function parentsDeepestFirst(adapter) {
3302
+ const parents = /* @__PURE__ */ new Set();
3303
+ adapter.forEachActivity((_activity, id) => {
3304
+ if (adapter.getChildren(id).length > 0) parents.add(id);
3305
+ });
3306
+ const depthById = /* @__PURE__ */ new Map();
3307
+ for (const id of parents) depthById.set(id, depthOf(id, adapter));
3308
+ return [...parents].sort(
3309
+ (a, b) => (depthById.get(b) ?? 0) - (depthById.get(a) ?? 0)
3310
+ );
3311
+ }
3312
+ function depthOf(id, adapter) {
3313
+ let depth = 0;
3314
+ let current = id;
3315
+ while (current && current !== "0") {
3316
+ if (!adapter.getActivity(current)) break;
3317
+ depth += 1;
3318
+ current = adapter.getParentId(current);
3319
+ }
3320
+ return depth;
3321
+ }
3322
+ function rollupParent(parentId, adapter) {
3323
+ const childIds = adapter.getChildren(parentId);
3324
+ if (childIds.length === 0) return;
3325
+ const children = [];
3326
+ for (const childId of childIds) {
3327
+ const child = adapter.getActivity(childId);
3328
+ if (!child) continue;
3329
+ children.push({
3330
+ progress: Number(child.progress ?? 0),
3331
+ ponderator: Number(child.ponderator ?? 0)
3332
+ });
3333
+ }
3334
+ applyWeightedProgressRollup(parentId, children, adapter);
3335
+ }
3336
+ function applyWeightedProgressRollup(parentId, children, adapter) {
3337
+ const parent = adapter.getActivity(parentId);
3338
+ if (!parent) return;
3339
+ const rollup = computeWeightedProgressRollup(children);
3340
+ if (rollup === null) {
3341
+ const fallback = adapter.getProgressFallback(parentId);
3342
+ if (fallback !== null) {
3343
+ adapter.setActivityField(parentId, COLUMN.PROGRESS, fallback);
3344
+ }
3345
+ return;
3346
+ }
3347
+ if (adapter.getProgressFallback(parentId) === null) {
3348
+ adapter.setProgressFallback(parentId, Number(parent.progress ?? 0));
3349
+ }
3350
+ adapter.setActivityField(
3351
+ parentId,
3352
+ COLUMN.PROGRESS,
3353
+ roundProgressPerLevel(rollup)
3354
+ );
3355
+ }
3356
+ function setExplicitSummaryProgressFallbacks(activityId, progress, adapter) {
3357
+ const pending = [activityId];
3358
+ while (pending.length > 0) {
3359
+ const currentId = pending.pop();
3360
+ if (currentId === void 0) continue;
3361
+ const children = adapter.getChildren(currentId);
3362
+ if (children.length === 0) continue;
3363
+ adapter.setProgressFallback(currentId, progress);
3364
+ pending.push(...children);
3365
+ }
3366
+ }
3367
+
3633
3368
  // src/columns/real-work/compute.ts
3634
3369
  function computeLeafRealWork(workHours, progress) {
3635
3370
  return (Number(workHours) || 0) * (Number(progress) || 0) / 100;
@@ -3745,16 +3480,8 @@ function recomputeParentFromChildren(parentId, adapter, progressMode = "freeze-w
3745
3480
  adapter.setActivityField(parentId, "startDate", newStart);
3746
3481
  adapter.setActivityField(parentId, "endDate", newEnd);
3747
3482
  adapter.setActivityField(parentId, "durationHours", durationHours);
3748
- const progressRollup = computeWeightedProgressRollup(
3749
- aggregate.weightedChildren
3750
- );
3751
- const shouldWriteProgress = progressMode !== "skip" && (progressRollup !== null || progressMode === "zero-when-no-contribution");
3752
- if (shouldWriteProgress) {
3753
- adapter.setActivityField(
3754
- parentId,
3755
- "progress",
3756
- roundProgressPerLevel(progressRollup ?? 0)
3757
- );
3483
+ if (progressMode !== "skip") {
3484
+ applyWeightedProgressRollup(parentId, aggregate.weightedChildren, adapter);
3758
3485
  }
3759
3486
  if (recomputeRealWork) {
3760
3487
  adapter.setActivityField(parentId, "realWorkHours", aggregate.realWorkSum);
@@ -3777,10 +3504,10 @@ function collectParentIds(adapter) {
3777
3504
  }
3778
3505
  function computeDepths(ids, adapter) {
3779
3506
  const depths = /* @__PURE__ */ new Map();
3780
- for (const id of ids) depths.set(id, depthOf(id, adapter));
3507
+ for (const id of ids) depths.set(id, depthOf2(id, adapter));
3781
3508
  return depths;
3782
3509
  }
3783
- function depthOf(id, adapter) {
3510
+ function depthOf2(id, adapter) {
3784
3511
  let depth = 0;
3785
3512
  let current = id;
3786
3513
  while (current && !isRootParent(current)) {
@@ -3906,7 +3633,7 @@ var descriptionPipeline = {
3906
3633
 
3907
3634
  // src/columns/shared/activity-predicates.ts
3908
3635
  function effectiveConstraintType(activity) {
3909
- return activity.constraintType ?? CONSTRAINT_TYPE2.ASAP;
3636
+ return activity.constraintType ?? CONSTRAINT_TYPE.ASAP;
3910
3637
  }
3911
3638
  var PROGRESS_COMPLETE = COMPLETED_PROGRESS;
3912
3639
  function isCompletedActivity(activity) {
@@ -4263,6 +3990,7 @@ function buildAncestorRollupCascade(activity, newValue, hierarchy) {
4263
3990
  const mutations = [];
4264
3991
  for (const id of chain) {
4265
3992
  const rollup = computeWeightedProgress(id, overlay, hierarchy);
3993
+ if (rollup === null) continue;
4266
3994
  overlay.set(id, rollup);
4267
3995
  mutations.push({
4268
3996
  activityId: id,
@@ -4276,7 +4004,8 @@ function computeWeightedProgress(parentId, overlay, hierarchy) {
4276
4004
  progress: overlay.has(child.id) ? overlay.get(child.id) : child.progress,
4277
4005
  ponderator: child.ponderator ?? 0
4278
4006
  }));
4279
- return Number((computeWeightedProgressRollup(children) ?? 0).toFixed(2));
4007
+ const rollup = computeWeightedProgressRollup(children);
4008
+ return rollup === null ? null : Number(rollup.toFixed(2));
4280
4009
  }
4281
4010
  function buildRecursiveTrackingEvents(activity, newValue) {
4282
4011
  return [
@@ -4644,7 +4373,7 @@ function checkIsWorkingDay(newDate, activity, ctx) {
4644
4373
  return null;
4645
4374
  }
4646
4375
  function detectEndDateConstraintWarning(activity, newEndDate) {
4647
- const cannotViolateStartPin = effectiveConstraintType(activity) === CONSTRAINT_TYPE2.MSO;
4376
+ const cannotViolateStartPin = effectiveConstraintType(activity) === CONSTRAINT_TYPE.MSO;
4648
4377
  if (cannotViolateStartPin) return void 0;
4649
4378
  const projectedDate = pickProjectedDateForConstraint(
4650
4379
  effectiveConstraintType(activity),
@@ -4694,7 +4423,7 @@ function calculateNewDuration(activity, newEndDate, ctx) {
4694
4423
 
4695
4424
  // src/columns/constraint-type/constraint-type-pipeline.ts
4696
4425
  var VALID_CONSTRAINT_TYPES = new Set(
4697
- Object.values(CONSTRAINT_TYPE2)
4426
+ Object.values(CONSTRAINT_TYPE)
4698
4427
  );
4699
4428
  var constraintTypePipeline = {
4700
4429
  targetField: "constraintType",
@@ -5475,6 +5204,12 @@ function runExpectedProgressBase(state, now, defaultBaseCalendarId = null) {
5475
5204
  return computeExpectedProgress(rootIds, now, adapter, defaultBaseCalendarId);
5476
5205
  }
5477
5206
 
5207
+ // src/columns/status/constants.ts
5208
+ var DEFAULT_STATUS_CRITERIA = "Baseline";
5209
+ function resolveStatusCriteria(value) {
5210
+ return value === "Actual" ? "Actual" : DEFAULT_STATUS_CRITERIA;
5211
+ }
5212
+
5478
5213
  // src/columns/status/compute.ts
5479
5214
  var roundToTwoDecimals = (value) => Math.round(value * 100) / 100;
5480
5215
  function resolveExpectedForStatus(activity, criteria) {
@@ -5502,7 +5237,7 @@ function applyStatusPass(state, criteria) {
5502
5237
  resolveExpectedForStatus(activity, criteria)
5503
5238
  );
5504
5239
  if (activity.status !== status) {
5505
- setActivityFieldDynamic(state, id, STATUS_COLUMN, status);
5240
+ setActivityFieldDynamic(state, id, COLUMN.STATUS, status);
5506
5241
  changed.push(id);
5507
5242
  }
5508
5243
  });
@@ -9275,10 +9010,10 @@ var FreeFloat = class {
9275
9010
  this.durationCache = /* @__PURE__ */ new Map();
9276
9011
  }
9277
9012
  getFreeFloat() {
9278
- const PROGRESS2 = Number(this.activity.progress);
9013
+ const PROGRESS = Number(this.activity.progress);
9279
9014
  const CONSTRAINT = this.getConstraintType();
9280
9015
  this.calculateFreeFloatOfLink(CONSTRAINT);
9281
- const activityFreeFloat = PROGRESS2 === 100 ? 0 : this.getFreeFloatBaseConstraint(CONSTRAINT);
9016
+ const activityFreeFloat = PROGRESS === 100 ? 0 : this.getFreeFloatBaseConstraint(CONSTRAINT);
9282
9017
  return {
9283
9018
  activityFreeFloat,
9284
9019
  linksFreeFloat: this.linksFreeFloatObject
@@ -9437,11 +9172,11 @@ var FreeFloat = class {
9437
9172
  };
9438
9173
  }
9439
9174
  calculateMsoMfo(constraint) {
9440
- const CONSTRAINT_DATE2 = this.activity.constraint_date || /* @__PURE__ */ new Date();
9175
+ const CONSTRAINT_DATE = this.activity.constraint_date || /* @__PURE__ */ new Date();
9441
9176
  const calculation = this.forwardMap.get(this.activity.id);
9442
9177
  const DATE_TO_CALCULATE = constraint === CONSTRAINT_TYPES.MSO ? calculation?.es : calculation?.ef;
9443
9178
  const freeSlack = this.calculationWithCalendar(
9444
- CONSTRAINT_DATE2,
9179
+ CONSTRAINT_DATE,
9445
9180
  DATE_TO_CALCULATE
9446
9181
  );
9447
9182
  return Math.max(0, this.parseHoursToDays(freeSlack));
@@ -10221,24 +9956,82 @@ function resolveDerivedPasses(action, autoSchedule, options) {
10221
9956
 
10222
9957
  // src/dispatch/shared/post-mutation.ts
10223
9958
  async function runPostMutation(deps, args) {
10224
- const passes = resolvePasses(args);
9959
+ const progressInvalidation = resolveProgressInvalidation(deps.adapter);
9960
+ const passes = resolvePasses(args, progressInvalidation);
10225
9961
  if (passes.has(DERIVED_PASS.AUTOSCHEDULE)) {
10226
9962
  await refreshMutatedParentBounds(deps.adapter, args);
10227
9963
  }
10228
9964
  const schedule = passes.has(DERIVED_PASS.AUTOSCHEDULE) ? await runAutoschedule(deps, args.autoscheduleFrom) : emptySchedule();
10229
- await rollUpParentBounds(deps.adapter, args, schedule.scheduledIds, passes);
9965
+ await rollUpParentBounds(
9966
+ deps.adapter,
9967
+ args,
9968
+ schedule.scheduledIds,
9969
+ passes,
9970
+ progressInvalidation
9971
+ );
10230
9972
  await runDerivedRecomputes(deps, passes, args.now);
10231
9973
  return schedule;
10232
9974
  }
10233
- function resolvePasses(args) {
9975
+ function resolvePasses(args, progressInvalidation) {
10234
9976
  const requested = args.autoscheduleFrom !== null;
10235
9977
  const ran = !args.options.skipAutoSchedule && requested;
10236
9978
  const passes = new Set(
10237
9979
  resolveDerivedPasses(args.action, { ran }, args.options)
10238
9980
  );
10239
- if (args.recomputeParentProgress) passes.add(DERIVED_PASS.PARENT_PROGRESS);
9981
+ if (args.recomputeParentProgress || progressInvalidation.affectedParents.size > 0) {
9982
+ passes.add(DERIVED_PASS.PARENT_PROGRESS);
9983
+ }
10240
9984
  return passes;
10241
9985
  }
9986
+ function resolveProgressInvalidation(adapter) {
9987
+ const affectedParents = /* @__PURE__ */ new Set();
9988
+ const dirtySeeds = /* @__PURE__ */ new Set();
9989
+ const journal = adapter.peekWriteCapture();
9990
+ if (!journal) return { affectedParents, dirtySeeds };
9991
+ const affectParent = (parentId) => {
9992
+ if (parentId === null || isRootParent(parentId)) return;
9993
+ const normalized2 = String(parentId);
9994
+ affectedParents.add(normalized2);
9995
+ const childId = adapter.getChildren(normalized2)[0];
9996
+ if (childId !== void 0) dirtySeeds.add(childId);
9997
+ };
9998
+ collectStructuralProgressInvalidations(adapter, journal.ops, affectParent);
9999
+ collectFieldProgressInvalidations(
10000
+ adapter,
10001
+ journal.before,
10002
+ dirtySeeds,
10003
+ affectParent
10004
+ );
10005
+ return { affectedParents, dirtySeeds };
10006
+ }
10007
+ function collectStructuralProgressInvalidations(adapter, operations, affectParent) {
10008
+ for (const operation of operations) {
10009
+ if (operation.kind === "insert") {
10010
+ const inserted = adapter.getActivity(operation.activityId);
10011
+ if (inserted) affectParent(inserted.parentId);
10012
+ } else if (operation.kind === "remove") {
10013
+ affectParent(operation.before.parentId);
10014
+ }
10015
+ }
10016
+ }
10017
+ function collectFieldProgressInvalidations(adapter, beforeById, dirtySeeds, affectParent) {
10018
+ for (const [activityId, before] of beforeById) {
10019
+ const after = adapter.getActivity(activityId);
10020
+ if (!after) continue;
10021
+ const progressChanged = !Object.is(before.progress, after.progress);
10022
+ const ponderatorChanged = !Object.is(before.ponderator, after.ponderator);
10023
+ const parentChanged = !Object.is(before.parentId, after.parentId);
10024
+ if (progressChanged || ponderatorChanged) {
10025
+ affectParent(after.parentId);
10026
+ dirtySeeds.add(String(activityId));
10027
+ }
10028
+ if (parentChanged) {
10029
+ affectParent(before.parentId);
10030
+ affectParent(after.parentId);
10031
+ dirtySeeds.add(String(activityId));
10032
+ }
10033
+ }
10034
+ }
10242
10035
  async function runAutoschedule(deps, autoscheduleFrom) {
10243
10036
  const options = autoscheduleFrom === "roots" ? {} : { triggerId: autoscheduleFrom };
10244
10037
  const result = await deps.scheduler.schedule(options);
@@ -10266,18 +10059,36 @@ async function refreshMutatedParentBounds(adapter, args) {
10266
10059
  if (mutated.size === 0) return;
10267
10060
  await updateParentBoundsFromChildren(adapter, mutated, false);
10268
10061
  }
10269
- async function rollUpParentBounds(adapter, args, scheduledIds, passes) {
10062
+ async function rollUpParentBounds(adapter, args, scheduledIds, passes, progressInvalidation) {
10063
+ if (passes.has(DERIVED_PASS.PARENT_PROGRESS)) {
10064
+ restoreDemotedProgressFallbacks(
10065
+ adapter,
10066
+ progressInvalidation.affectedParents
10067
+ );
10068
+ }
10270
10069
  const dirty = args.collectDirty ? args.collectDirty(scheduledIds) : collectDirtyForStructural(args.recomputeParentsFrom, scheduledIds);
10070
+ for (const activityId of progressInvalidation.dirtySeeds) {
10071
+ dirty.add(activityId);
10072
+ }
10271
10073
  await updateParentBoundsFromChildren(
10272
10074
  adapter,
10273
10075
  dirty,
10274
10076
  passes.has(DERIVED_PASS.PARENT_PROGRESS),
10275
10077
  {
10276
10078
  recomputeRealWork: passes.has(DERIVED_PASS.REAL_WORK),
10277
- progressMode: args.recomputeParentProgress ? "zero-when-no-contribution" : "freeze-when-no-contribution"
10079
+ progressMode: "freeze-when-no-contribution"
10278
10080
  }
10279
10081
  );
10280
10082
  }
10083
+ function restoreDemotedProgressFallbacks(adapter, affectedParents) {
10084
+ for (const parentId of affectedParents) {
10085
+ if (adapter.hasChildren(parentId)) continue;
10086
+ const fallback = adapter.getProgressFallback(parentId);
10087
+ if (fallback === null) continue;
10088
+ adapter.setActivityField(parentId, "progress", fallback);
10089
+ adapter.setProgressFallback(parentId, null);
10090
+ }
10091
+ }
10281
10092
  async function runDerivedRecomputes(deps, passes, now) {
10282
10093
  if (passes.has(DERIVED_PASS.EXPECTED_PROGRESS) && now !== null) {
10283
10094
  if (hasActiveBaseline(deps.adapter)) {
@@ -11504,9 +11315,8 @@ function buildParentMutations(input) {
11504
11315
  if (wasPromotableLeaf) {
11505
11316
  fields.type = PROMOTION_TARGET_TYPE;
11506
11317
  if ((input.promotionSource ?? "create") === "create") {
11507
- fields.constraintType = CONSTRAINT_TYPE2.ASAP;
11318
+ fields.constraintType = CONSTRAINT_TYPE.ASAP;
11508
11319
  fields.constraintDate = null;
11509
- fields.progress = 0;
11510
11320
  }
11511
11321
  }
11512
11322
  return {
@@ -12645,8 +12455,6 @@ function dispatchSirSync(action, deps) {
12645
12455
  id,
12646
12456
  kind: "updated",
12647
12457
  fields: { pendingRequestIds: { before, after } },
12648
- // ISSUE-025: `after` must be the COMPLETE post-change entity; a partial
12649
- // object crashes any consumer that projects the full snapshot.
12650
12458
  after: structuredCloneActivity(updated)
12651
12459
  };
12652
12460
  return {
@@ -12661,60 +12469,6 @@ function dispatchSirSync(action, deps) {
12661
12469
  };
12662
12470
  }
12663
12471
 
12664
- // src/propagations/upward/progress-rollup.ts
12665
- function recomputeAllProgressRollup(adapter) {
12666
- for (const parentId of parentsDeepestFirst(adapter)) {
12667
- rollupParent(parentId, adapter);
12668
- }
12669
- }
12670
- function recomputeProgressRollupForParents(parentIdsDeepestFirst, adapter) {
12671
- for (const parentId of parentIdsDeepestFirst) {
12672
- rollupParent(parentId, adapter);
12673
- }
12674
- }
12675
- function parentsDeepestFirst(adapter) {
12676
- const parents = /* @__PURE__ */ new Set();
12677
- adapter.forEachActivity((_activity, id) => {
12678
- if (adapter.getChildren(id).length > 0) parents.add(id);
12679
- });
12680
- const depthById = /* @__PURE__ */ new Map();
12681
- for (const id of parents) depthById.set(id, depthOf2(id, adapter));
12682
- return [...parents].sort(
12683
- (a, b) => (depthById.get(b) ?? 0) - (depthById.get(a) ?? 0)
12684
- );
12685
- }
12686
- function depthOf2(id, adapter) {
12687
- let depth = 0;
12688
- let current = id;
12689
- while (current && current !== "0") {
12690
- if (!adapter.getActivity(current)) break;
12691
- depth += 1;
12692
- current = adapter.getParentId(current);
12693
- }
12694
- return depth;
12695
- }
12696
- function rollupParent(parentId, adapter) {
12697
- const childIds = adapter.getChildren(parentId);
12698
- if (childIds.length === 0) return;
12699
- const children = [];
12700
- for (const childId of childIds) {
12701
- const child = adapter.getActivity(childId);
12702
- if (!child) continue;
12703
- children.push({
12704
- progress: Number(child.progress ?? 0),
12705
- ponderator: Number(child.ponderator ?? 0)
12706
- });
12707
- }
12708
- const rollup = computeWeightedProgressRollup(children);
12709
- if (rollup !== null) {
12710
- adapter.setActivityField(
12711
- parentId,
12712
- COLUMN.PROGRESS,
12713
- roundProgressPerLevel(rollup)
12714
- );
12715
- }
12716
- }
12717
-
12718
12472
  // src/dispatch/handlers/persistence-acknowledge.ts
12719
12473
  function persistedOwnersOf(entities) {
12720
12474
  const owners = /* @__PURE__ */ new Map();
@@ -13781,6 +13535,11 @@ async function dispatchActivitySetProgress(action, options, deps) {
13781
13535
  const touchedIds = collectTouchedIds(action.activityId, changes);
13782
13536
  const beforeSnap = await snapshotActivities(adapter, touchedIds);
13783
13537
  applyFieldChanges(adapter, action.activityId, changes);
13538
+ setExplicitSummaryProgressFallbacks(
13539
+ action.activityId,
13540
+ action.newValue,
13541
+ adapter
13542
+ );
13784
13543
  runPostProcessorsOnAdapter(
13785
13544
  action.activityId,
13786
13545
  changes.postProcessors ?? [],
@@ -17644,7 +17403,8 @@ var WriteCapture = class {
17644
17403
  ops: [],
17645
17404
  linkFieldCaptured: /* @__PURE__ */ new Set(),
17646
17405
  correlativeBefore: /* @__PURE__ */ new Map(),
17647
- scheduledBefore: /* @__PURE__ */ new Map()
17406
+ scheduledBefore: /* @__PURE__ */ new Map(),
17407
+ progressFallbackBefore: /* @__PURE__ */ new Map()
17648
17408
  };
17649
17409
  }
17650
17410
  peek() {
@@ -17707,6 +17467,11 @@ var WriteCapture = class {
17707
17467
  before: cloneLink(current)
17708
17468
  });
17709
17469
  }
17470
+ noteProgressFallback(activityId, current) {
17471
+ const journal = this._journal;
17472
+ if (!journal || journal.progressFallbackBefore.has(activityId)) return;
17473
+ journal.progressFallbackBefore.set(activityId, current);
17474
+ }
17710
17475
  };
17711
17476
  function cloneLink(link) {
17712
17477
  return { ...link };
@@ -17876,11 +17641,83 @@ var HierarchyIndex = class {
17876
17641
  }
17877
17642
  };
17878
17643
 
17644
+ // src/internal/state/view-state.ts
17645
+ var ViewStateStore = class {
17646
+ checkedSet = /* @__PURE__ */ new Set();
17647
+ hiddenSet = /* @__PURE__ */ new Set();
17648
+ activeFilter = null;
17649
+ activeOrder = null;
17650
+ isChecked(activityId) {
17651
+ return this.checkedSet.has(activityId);
17652
+ }
17653
+ isVisible(activityId) {
17654
+ return !this.hiddenSet.has(activityId);
17655
+ }
17656
+ hiddenIds() {
17657
+ return [...this.hiddenSet];
17658
+ }
17659
+ getActiveFilter() {
17660
+ return this.activeFilter;
17661
+ }
17662
+ setActiveFilter(nextFilter) {
17663
+ this.activeFilter = nextFilter;
17664
+ }
17665
+ getActiveOrder() {
17666
+ return this.activeOrder;
17667
+ }
17668
+ setActiveOrder(nextOrder) {
17669
+ this.activeOrder = nextOrder;
17670
+ }
17671
+ checkedIds() {
17672
+ return [...this.checkedSet];
17673
+ }
17674
+ checkedCount() {
17675
+ return this.checkedSet.size;
17676
+ }
17677
+ setChecked(activityId, nextChecked) {
17678
+ if (nextChecked === this.checkedSet.has(activityId)) return false;
17679
+ if (nextChecked) this.checkedSet.add(activityId);
17680
+ else this.checkedSet.delete(activityId);
17681
+ return true;
17682
+ }
17683
+ setVisible(activityId, nextVisible) {
17684
+ if (nextVisible === !this.hiddenSet.has(activityId)) return false;
17685
+ if (nextVisible) this.hiddenSet.delete(activityId);
17686
+ else this.hiddenSet.add(activityId);
17687
+ return true;
17688
+ }
17689
+ forget(activityId) {
17690
+ this.checkedSet.delete(activityId);
17691
+ this.hiddenSet.delete(activityId);
17692
+ }
17693
+ hydrate(seed2) {
17694
+ this.checkedSet = new Set(seed2.checkedIds ?? []);
17695
+ this.hiddenSet = new Set(seed2.hiddenIds ?? []);
17696
+ this.activeFilter = null;
17697
+ this.activeOrder = null;
17698
+ }
17699
+ snapshot() {
17700
+ return {
17701
+ checked: new Set(this.checkedSet),
17702
+ hidden: new Set(this.hiddenSet),
17703
+ activeFilter: this.activeFilter,
17704
+ activeOrder: this.activeOrder
17705
+ };
17706
+ }
17707
+ restore(snapshot) {
17708
+ this.checkedSet = new Set(snapshot.checked);
17709
+ this.hiddenSet = new Set(snapshot.hidden);
17710
+ this.activeFilter = snapshot.activeFilter;
17711
+ this.activeOrder = snapshot.activeOrder;
17712
+ }
17713
+ };
17714
+
17879
17715
  // src/internal/state/schedule-state.ts
17880
17716
  var ScheduleState = class _ScheduleState {
17881
17717
  _activities = /* @__PURE__ */ new Map();
17882
17718
  _lastStartDate = /* @__PURE__ */ new Map();
17883
17719
  _promotionSnapshot = /* @__PURE__ */ new Map();
17720
+ _progressFallback = /* @__PURE__ */ new Map();
17884
17721
  _links = /* @__PURE__ */ new Map();
17885
17722
  _outgoing = /* @__PURE__ */ new Map();
17886
17723
  _incoming = /* @__PURE__ */ new Map();
@@ -17941,6 +17778,7 @@ var ScheduleState = class _ScheduleState {
17941
17778
  cloneDomainValue(snapshot)
17942
17779
  ])
17943
17780
  );
17781
+ fork._progressFallback = new Map(this._progressFallback);
17944
17782
  fork._flags = cloneDomainValue(this._flags);
17945
17783
  fork._filterProjection = cloneDomainValue(this._filterProjection);
17946
17784
  return fork;
@@ -17994,6 +17832,17 @@ var ScheduleState = class _ScheduleState {
17994
17832
  this._capturePromotionSnapshotOnce();
17995
17833
  this._promotionSnapshot.set(String(activityId), snapshot);
17996
17834
  }
17835
+ getProgressFallback(activityId) {
17836
+ return this._progressFallback.get(String(activityId)) ?? null;
17837
+ }
17838
+ setProgressFallback(activityId, value) {
17839
+ const key = String(activityId);
17840
+ const current = this._progressFallback.get(key) ?? null;
17841
+ if (Object.is(current, value)) return;
17842
+ this._writeCapture.noteProgressFallback(key, current);
17843
+ if (value === null) this._progressFallback.delete(key);
17844
+ else this._progressFallback.set(key, value);
17845
+ }
17997
17846
  _capturePromotionSnapshotOnce() {
17998
17847
  if (this._writeCapture.peek() === null) return;
17999
17848
  if (this._promotionSnapshotBefore === null) {
@@ -18054,28 +17903,6 @@ var ScheduleState = class _ScheduleState {
18054
17903
  this._viewState.setActiveOrder(nextOrder);
18055
17904
  this.invalidateDerivedOrder();
18056
17905
  }
18057
- setRelativePlacementPin(activityId, pin) {
18058
- this._viewState.setRelativePlacementPin(String(activityId), pin);
18059
- this.invalidateVisualOrderIds();
18060
- }
18061
- getRelativePlacementPins() {
18062
- return this._viewState.getRelativePlacementPins();
18063
- }
18064
- restoreRelativePlacementPins(pins) {
18065
- this._viewState.restoreRelativePlacementPins(pins);
18066
- this._filterProjection = null;
18067
- this.invalidateVisualOrderIds();
18068
- }
18069
- /**
18070
- * Rebuild-on-demand, memoized: at most one build per invalidation, never one
18071
- * per comparison. getChildrenInVisualOrder asks for this once and then sorts a
18072
- * whole sibling group with it, and collectBranchOrder walks every branch, so a
18073
- * build per read would be thousands per dispatch.
18074
- *
18075
- * The factory reads hoursPerDay and the collation locale at build time, so a
18076
- * rebuilt comparator always reflects the current context — that is what keeps
18077
- * ordering and filtering from seeing two different hoursPerDay.
18078
- */
18079
17906
  getOrderComparator() {
18080
17907
  const activeOrder = this._viewState.getActiveOrder();
18081
17908
  if (activeOrder === null) {
@@ -18086,12 +17913,6 @@ var ScheduleState = class _ScheduleState {
18086
17913
  this._cache.orderComparator = this._buildOrderComparator(activeOrder);
18087
17914
  return this._cache.orderComparator;
18088
17915
  }
18089
- /**
18090
- * Both order caches, always together. The comparator decides the sequence, so
18091
- * a rebuilt comparator with a surviving sequence would paint the old order.
18092
- * invalidateVisualOrderIds stays separate on purpose: a structural change
18093
- * moves rows without touching the rules, so the comparator is still valid.
18094
- */
18095
17916
  invalidateDerivedOrder() {
18096
17917
  this._cache.orderComparator = null;
18097
17918
  this._cache.visualOrderIds = null;
@@ -18272,11 +18093,8 @@ var ScheduleState = class _ScheduleState {
18272
18093
  const activity = this.getActivity(activityId);
18273
18094
  if (activity) this._writeCapture.noteRemove(String(activityId), activity);
18274
18095
  const key = String(activityId);
18275
- const relativePlacementPins = this._viewState.getRelativePlacementPins();
18276
- const hasDependentPin = [...relativePlacementPins.values()].some(
18277
- ({ referenceId }) => referenceId === key
18278
- );
18279
- if (this._viewState.isChecked(key) || !this._viewState.isVisible(key) || relativePlacementPins.has(key) || hasDependentPin) {
18096
+ this.setProgressFallback(key, null);
18097
+ if (this._viewState.isChecked(key) || !this._viewState.isVisible(key)) {
18280
18098
  this._captureViewStateOnce();
18281
18099
  }
18282
18100
  this._viewState.forget(key);
@@ -18300,7 +18118,13 @@ var ScheduleState = class _ScheduleState {
18300
18118
  restoreFromCapture() {
18301
18119
  const journal = this._writeCapture.peek();
18302
18120
  if (!journal) return;
18303
- const { ops, before, correlativeBefore, scheduledBefore } = journal;
18121
+ const {
18122
+ ops,
18123
+ before,
18124
+ correlativeBefore,
18125
+ scheduledBefore,
18126
+ progressFallbackBefore
18127
+ } = journal;
18304
18128
  this._undoLinkAdds(ops);
18305
18129
  this._undoInserts(ops);
18306
18130
  this._reInsertRemovedActivities(ops);
@@ -18309,6 +18133,7 @@ var ScheduleState = class _ScheduleState {
18309
18133
  this._restoreCorrelatives(correlativeBefore);
18310
18134
  this._restoreActivityFields(scheduledBefore);
18311
18135
  this._restoreActivityFields(before);
18136
+ this._restoreProgressFallbacks(progressFallbackBefore);
18312
18137
  if (this._viewStateBefore !== null) {
18313
18138
  this._viewState.restore(this._viewStateBefore);
18314
18139
  this._filterProjection = null;
@@ -18324,6 +18149,12 @@ var ScheduleState = class _ScheduleState {
18324
18149
  this._cache.visualOrderIds = null;
18325
18150
  this._hierarchy.markDirty();
18326
18151
  }
18152
+ _restoreProgressFallbacks(before) {
18153
+ for (const [activityId, value] of before) {
18154
+ if (value === null) this._progressFallback.delete(activityId);
18155
+ else this._progressFallback.set(activityId, value);
18156
+ }
18157
+ }
18327
18158
  _restoreCorrelatives(correlativeBefore) {
18328
18159
  for (const [id, before] of correlativeBefore) {
18329
18160
  const live = this._activities.get(id);
@@ -18537,15 +18368,15 @@ function buildHistoryChangeSet(changes) {
18537
18368
  links: linksWithoutAfter
18538
18369
  });
18539
18370
  }
18540
- function buildUndoEntry(changeSet, beforeSnap, beforeLinks, afterSnap, afterLinks, relativePlacementPinsBefore, relativePlacementPinsAfter) {
18371
+ function buildUndoEntry(changeSet, beforeSnap, beforeLinks, afterSnap, afterLinks, progressFallbackBefore, progressFallbackAfter) {
18541
18372
  return {
18542
18373
  changeSet,
18543
18374
  beforeSnap,
18544
18375
  beforeLinks: resetDerivedLinkSnapshots(beforeLinks),
18545
18376
  afterSnap,
18546
18377
  afterLinks: resetDerivedLinkSnapshots(afterLinks),
18547
- relativePlacementPinsBefore,
18548
- relativePlacementPinsAfter
18378
+ progressFallbackBefore,
18379
+ progressFallbackAfter
18549
18380
  };
18550
18381
  }
18551
18382
  function resetDerivedLinkSnapshots(snapshots) {
@@ -18848,8 +18679,10 @@ function applyChangeSetSide(state, entry, side) {
18848
18679
  );
18849
18680
  }
18850
18681
  }
18851
- const pins = side === "before" ? entry.relativePlacementPinsBefore : entry.relativePlacementPinsAfter;
18852
- if (pins !== void 0) state.restoreRelativePlacementPins(pins);
18682
+ const progressFallbacks = side === "before" ? entry.progressFallbackBefore : entry.progressFallbackAfter;
18683
+ for (const [activityId, value] of progressFallbacks ?? []) {
18684
+ state.setProgressFallback(activityId, value);
18685
+ }
18853
18686
  }
18854
18687
 
18855
18688
  // src/dispatch/undo/undo-recorder.ts
@@ -20741,6 +20574,7 @@ async function runInitialPasses(deps) {
20741
20574
  normalizeLoadedConstraintDates(state);
20742
20575
  recomputeCanonicalRealWork(state);
20743
20576
  if (skipAutoSchedule) {
20577
+ recomputeAllProgressRollup(state);
20744
20578
  if (now) {
20745
20579
  applyExpectedProgressLive(state, now);
20746
20580
  applyStatusPass(state, sector.statusCriteria);
@@ -20752,6 +20586,7 @@ async function runInitialPasses(deps) {
20752
20586
  normalizeMilestoneConstraintDatesForDisplay(state);
20753
20587
  recomputeSkippedLeafEnds(state);
20754
20588
  await updateParentBoundsFromChildren(state, void 0, false);
20589
+ recomputeAllProgressRollup(state);
20755
20590
  if (now) {
20756
20591
  applyExpectedProgressLive(state, now);
20757
20592
  applyStatusPass(state, sector.statusCriteria);
@@ -21194,41 +21029,12 @@ function collectChangedActivities(changes, adapter) {
21194
21029
  }
21195
21030
  return activities;
21196
21031
  }
21197
- function changesAffectRelativePinProjection(changes, changedActivities, adapter, pins) {
21198
- if (pins.size === 0) return false;
21199
- if (changes.activities.some(
21200
- (change) => change.kind === "created" && pins.has(String(change.id))
21201
- )) {
21202
- return true;
21203
- }
21204
- const referenceIds = new Set(
21205
- [...pins.values()].map(({ referenceId }) => String(referenceId))
21206
- );
21207
- for (const activity of changedActivities) {
21208
- let activityId = String(activity.id);
21209
- while (activityId !== null) {
21210
- if (referenceIds.has(activityId)) return true;
21211
- activityId = adapter.getParentId(activityId);
21212
- }
21213
- }
21214
- return false;
21215
- }
21216
21032
  function isCurrentFilterProjection(projection, filter, context) {
21217
21033
  if (projection === null || projection.filter !== filter) return false;
21218
21034
  const hasCurrentHours = projection.context.hoursPerDay === context.hoursPerDay;
21219
21035
  const hasCurrentLocale = projection.context.locale === context.locale;
21220
21036
  return hasCurrentHours && hasCurrentLocale;
21221
21037
  }
21222
- function relativePlacementPinsEqual(left, right) {
21223
- if (left.size !== right.size) return false;
21224
- for (const [activityId, pin] of left) {
21225
- const other = right.get(activityId);
21226
- if (other === void 0 || other.referenceId !== pin.referenceId || other.branchParentId !== pin.branchParentId || other.side !== pin.side) {
21227
- return false;
21228
- }
21229
- }
21230
- return true;
21231
- }
21232
21038
  var ScheduleCore = class _ScheduleCore {
21233
21039
  _activityClipboard = { value: null };
21234
21040
  _status = SCHEDULE_CORE_STATUS.READY;
@@ -21237,29 +21043,6 @@ var ScheduleCore = class _ScheduleCore {
21237
21043
  _undo = new UndoRecorder();
21238
21044
  _opQueue = Promise.resolve();
21239
21045
  _scheduleRevision = 0;
21240
- /**
21241
- * Revision counting mutations that have been ADMITTED, whether or not they
21242
- * have changed anything yet. `_scheduleRevision` counts the ones that
21243
- * actually did.
21244
- *
21245
- * INVARIANT: `_pendingScheduleRevision >= _scheduleRevision`, and equality
21246
- * means no mutation is in flight.
21247
- *
21248
- * The two exist separately because the useful instant and the knowable
21249
- * instant are not the same one. A critical-path calculation becomes garbage
21250
- * the moment the next mutation is admitted, but whether that mutation is
21251
- * substantive is only knowable once its ChangeSet exists, which is hundreds
21252
- * of milliseconds later at project scale. Measured live on a 12268-activity
21253
- * project: the cancellation flag flipped 929 ms into a 930 ms calculation,
21254
- * 812 into 812 and 785 into 785, because the job's apply block queues behind
21255
- * the very dispatch whose completion cancels it. Cancellation and job end
21256
- * were the same event, so every discarded job ran the whole calculation.
21257
- *
21258
- * Splitting the counter buys the early signal without moving the meaning of
21259
- * `_scheduleRevision`, whose three readers (`isCriticalPathSettled`,
21260
- * `createDateGesturePreview` and the job's own `isCurrent`) are all written
21261
- * against "the state actually changed".
21262
- */
21263
21046
  _pendingScheduleRevision = 0;
21264
21047
  _criticalPathRevision = -1;
21265
21048
  _activeCriticalPath = null;
@@ -21372,13 +21155,6 @@ var ScheduleCore = class _ScheduleCore {
21372
21155
  this.assertReady();
21373
21156
  return readAllActivities(this.coreRuntime.state);
21374
21157
  }
21375
- /**
21376
- * Read del plan de indent (ISSUE-080): los padres destino DISTINTOS bajo los
21377
- * que quedarían las actividades si se indentaran ahora. Es exactamente la
21378
- * planificación que ejecuta el handler (`planIndentMoves`, autoridad única),
21379
- * expuesta para que la frontera pueda correr el protocolo de conversión a
21380
- * madre (chequeo backend + modal) ANTES de despachar. Solo lecturas.
21381
- */
21382
21158
  getIndentTargetParentIds(activityIds) {
21383
21159
  this.assertReady();
21384
21160
  const plan = planIndentMoves(
@@ -21531,7 +21307,6 @@ var ScheduleCore = class _ScheduleCore {
21531
21307
  this.assertReady();
21532
21308
  return this._saveTracker.hasUnsavedChanges();
21533
21309
  }
21534
- /** Read-only revision; moves once per substantive mutation. */
21535
21310
  getScheduleRevision() {
21536
21311
  return this._scheduleRevision;
21537
21312
  }
@@ -21577,24 +21352,10 @@ var ScheduleCore = class _ScheduleCore {
21577
21352
  );
21578
21353
  return workPromise;
21579
21354
  }
21580
- /**
21581
- * Marks a mutation as admitted and invalidates the calculation in flight.
21582
- *
21583
- * Called BEFORE `_enqueue`, which is the point of the whole thing: the wait
21584
- * in the operation queue is part of the window a running calculation wastes,
21585
- * and it is the longest part of it when several mutations are already
21586
- * queued.
21587
- *
21588
- * `dispatchChangesSchedulingState` is a pure function of the action, so this
21589
- * decision needs no state and cannot be wrong about the action's nature. What
21590
- * it cannot know yet is whether the mutation will produce anything, which is
21591
- * what `_settleScheduleMutation` reconciles afterwards.
21592
- */
21593
21355
  _beginScheduleMutation(action) {
21594
21356
  if (!dispatchChangesSchedulingState(action)) return false;
21595
21357
  return this._admitScheduleMutation();
21596
21358
  }
21597
- /** Undo and redo have no action to classify: reaching them IS the mutation. */
21598
21359
  _admitScheduleMutation() {
21599
21360
  this._pendingScheduleRevision++;
21600
21361
  if (this._activeCriticalPath) {
@@ -21602,30 +21363,6 @@ var ScheduleCore = class _ScheduleCore {
21602
21363
  }
21603
21364
  return true;
21604
21365
  }
21605
- /**
21606
- * Closes a mutation admitted by `_beginScheduleMutation`, on EVERY exit path.
21607
- *
21608
- * `changedState` false means the mutation was admitted and produced nothing
21609
- * (rejected, non-substantive, or rolled back). The pending revision walks
21610
- * back so the invariant holds and `isCriticalPathSettled` keeps telling the
21611
- * truth, and the calculation this mutation killed for nothing is re-armed.
21612
- *
21613
- * The re-arm goes through the queue and that is not incidental. Started
21614
- * outside it, the calculation would run immediately while the next queued
21615
- * mutation is still waiting, and that mutation would kill it again on
21616
- * admission: a cancel-and-restart treadmill burning a fresh project-wide deep
21617
- * copy per lap. Inside the queue the restart cannot happen until the queue
21618
- * drains, so at most one calculation is armed per settled mutation. The burst
21619
- * scenario will NOT catch a regression here, because there every dispatch
21620
- * succeeds and this path is never taken.
21621
- *
21622
- * It re-arms through `recomputeCriticalPath` rather than enqueueing the job
21623
- * directly, because the queue slot must NOT await the job: the job's own
21624
- * apply block needs a later slot on this same queue, so awaiting it from
21625
- * inside a slot deadlocks the core. `recomputeCriticalPath` already has the
21626
- * shape that returns the job's promise out of the slot instead of awaiting
21627
- * it, and it keeps `_criticalPathReady` pointing at the live calculation.
21628
- */
21629
21366
  _settleScheduleMutation(admitted, changedState) {
21630
21367
  if (!admitted) return;
21631
21368
  if (changedState) return;
@@ -21676,8 +21413,6 @@ var ScheduleCore = class _ScheduleCore {
21676
21413
  dateFormat: this.coreRuntime.sector.dateFormat
21677
21414
  });
21678
21415
  const historyPolicy = getDispatchHistoryPolicy(action);
21679
- const capturesRelativePlacementPins = action.kind === "activity-create" || action.kind === "activity-delete" || action.kind === "activity-batch" && action.mode === "replace";
21680
- const relativePlacementPinsBefore = capturesRelativePlacementPins ? new Map(this.coreRuntime.state.getRelativePlacementPins()) : void 0;
21681
21416
  const generatorSnapshot = [
21682
21417
  this.coreRuntime.activityIdGenerator.snapshot(),
21683
21418
  this.coreRuntime.linkIdGenerator.snapshot(),
@@ -21686,6 +21421,7 @@ var ScheduleCore = class _ScheduleCore {
21686
21421
  this.coreRuntime.state.beginWriteCapture();
21687
21422
  this.coreRuntime.customIdTracker.beginCustomIdTransaction();
21688
21423
  let result;
21424
+ let progressFallbackBefore;
21689
21425
  try {
21690
21426
  result = await performDispatch(action, options, {
21691
21427
  adapter: this.coreRuntime.state,
@@ -21706,6 +21442,10 @@ var ScheduleCore = class _ScheduleCore {
21706
21442
  )?.id ?? null,
21707
21443
  now: this.coreRuntime.clock ? endOfLocalDay(this.coreRuntime.clock()) : null
21708
21444
  });
21445
+ const fallbackBefore = this.coreRuntime.state.peekWriteCapture()?.progressFallbackBefore;
21446
+ if (fallbackBefore && fallbackBefore.size > 0) {
21447
+ progressFallbackBefore = new Map(fallbackBefore);
21448
+ }
21709
21449
  if (!result.ok) this._rollback(generatorSnapshot);
21710
21450
  } catch (error) {
21711
21451
  this._rollback(generatorSnapshot);
@@ -21714,17 +21454,17 @@ var ScheduleCore = class _ScheduleCore {
21714
21454
  this.coreRuntime.state.endWriteCapture();
21715
21455
  this.coreRuntime.customIdTracker.commitCustomIdTransaction();
21716
21456
  }
21717
- if (result.ok) this._recordRelativePlacementPin(action, result.changes);
21718
21457
  if (result.ok && historyPolicy === "record" && changeSetIsSubstantive(result.changes)) {
21719
21458
  const created = captureCreatedSnapshots(
21720
21459
  this.coreRuntime.state,
21721
21460
  result.changes
21722
21461
  );
21723
- const relativePlacementPinsAfter = capturesRelativePlacementPins ? new Map(this.coreRuntime.state.getRelativePlacementPins()) : void 0;
21724
- const relativePlacementPinsChanged = relativePlacementPinsBefore !== void 0 && relativePlacementPinsAfter !== void 0 && !relativePlacementPinsEqual(
21725
- relativePlacementPinsBefore,
21726
- relativePlacementPinsAfter
21727
- );
21462
+ const progressFallbackAfter = progressFallbackBefore ? new Map(
21463
+ Array.from(progressFallbackBefore.keys(), (activityId) => [
21464
+ activityId,
21465
+ this.coreRuntime.state.getProgressFallback(activityId)
21466
+ ])
21467
+ ) : void 0;
21728
21468
  this._undo.record(
21729
21469
  buildUndoEntry(
21730
21470
  buildHistoryChangeSet(result.changes),
@@ -21732,8 +21472,8 @@ var ScheduleCore = class _ScheduleCore {
21732
21472
  result.__beforeLinks,
21733
21473
  created.afterSnap,
21734
21474
  created.afterLinks,
21735
- relativePlacementPinsChanged ? relativePlacementPinsBefore : void 0,
21736
- relativePlacementPinsChanged ? relativePlacementPinsAfter : void 0
21475
+ progressFallbackBefore,
21476
+ progressFallbackAfter
21737
21477
  ),
21738
21478
  computeCoalesceKey(action),
21739
21479
  Date.now()
@@ -21781,11 +21521,7 @@ var ScheduleCore = class _ScheduleCore {
21781
21521
  return operation;
21782
21522
  }
21783
21523
  isCriticalPathSettled() {
21784
- return this._criticalPathRevision === this._scheduleRevision && // An admitted mutation has not landed yet, so whatever is applied now is
21785
- // about to be stale. Without this term `whenCriticalPathSettled` would
21786
- // spin: it would keep arming calculations that `isCurrent` kills on their
21787
- // first check, each one paying the project-wide deep copy first.
21788
- this._pendingScheduleRevision === this._scheduleRevision && this._activeCriticalPath === null;
21524
+ return this._criticalPathRevision === this._scheduleRevision && this._pendingScheduleRevision === this._scheduleRevision && this._activeCriticalPath === null;
21789
21525
  }
21790
21526
  async whenCriticalPathSettled() {
21791
21527
  while (!this.isCriticalPathSettled()) {
@@ -21801,34 +21537,6 @@ var ScheduleCore = class _ScheduleCore {
21801
21537
  const merged = this._reapplyViewState(result.changes);
21802
21538
  return merged === null ? result : { ...result, changes: merged };
21803
21539
  }
21804
- _recordRelativePlacementPin(action, changes) {
21805
- const state = this.coreRuntime.state;
21806
- if (action.kind !== "activity-create") return;
21807
- const referenceId = action.afterSiblingId ?? action.beforeSiblingId ?? (action.eventSource === "Creation button (+)" && action.parentId !== ROOT_PARENT_ID ? action.parentId : void 0);
21808
- if (referenceId === void 0) return;
21809
- const created = changes.activities.find(({ kind }) => kind === "created");
21810
- if (created === void 0) return;
21811
- state.setRelativePlacementPin(created.id, {
21812
- referenceId,
21813
- branchParentId: action.parentId,
21814
- side: action.beforeSiblingId === void 0 ? "after" : "before"
21815
- });
21816
- }
21817
- /**
21818
- * The single place the two view-state passes are chained. Dispatch, undo and
21819
- * redo all route through here: when this existed only inside the dispatch
21820
- * path, undo and redo reapplied the filter and forgot the order, so an edit
21821
- * that moved a row and was then undone left the row in its new position.
21822
- *
21823
- * The sequence between the passes is indifferent. Filter and order are
21824
- * orthogonal projections over the same tree — the filter decides which rows
21825
- * exist on screen and never reads the order; the order sequences every
21826
- * sibling group from the hierarchy index and never reads visibility. Either
21827
- * sequence produces the same ChangeSet.
21828
- *
21829
- * Both passes answer null while their state is inactive, so an unfiltered,
21830
- * unsorted schedule pays two property reads.
21831
- */
21832
21540
  _reapplyViewState(changes) {
21833
21541
  const withFilter = this._reapplyActiveFilter(changes);
21834
21542
  const sequenced = this._emitTouchedBranchOrder(withFilter ?? changes);
@@ -21838,24 +21546,6 @@ var ScheduleCore = class _ScheduleCore {
21838
21546
  );
21839
21547
  return withOrder ?? sequenced.changes ?? withFilter;
21840
21548
  }
21841
- /**
21842
- * Emits `order` for the branches this mutation resequenced, when no user order
21843
- * is active.
21844
- *
21845
- * The contract is that `order` reports a CHANGED SEQUENCE, not the presence of
21846
- * a sort. Tying emission to the cause instead of the effect is what produced
21847
- * the undo bug and, later, the reparent ones: without an active order a move,
21848
- * an indent, an outdent or the undo of any of them rearranged rows and said
21849
- * nothing, so an incremental consumer kept the old sequence. The undo of a
21850
- * reparent was the worst of them — it carried neither `order` nor a single
21851
- * correlativeId, so the position was not recoverable by any consumer.
21852
- *
21853
- * The touched branches are derived from the ChangeSet rather than accumulated
21854
- * in the state: an entity whose parentId or correlativeId moved, plus the
21855
- * parents of created and deleted rows, is exactly the set of branches whose
21856
- * sequence can differ. That keeps this linear in the blast radius, adds
21857
- * nothing to the write path, and cannot leak across dispatches.
21858
- */
21859
21549
  _emitTouchedBranchOrder(changes) {
21860
21550
  const adapter = this.coreRuntime.state;
21861
21551
  const touchedParents = collectResequencedParents(
@@ -21872,15 +21562,6 @@ var ScheduleCore = class _ScheduleCore {
21872
21562
  if (order.length === 0) return { changes: null, touchedParents };
21873
21563
  return { changes: { ...changes, order }, touchedParents };
21874
21564
  }
21875
- /**
21876
- * Re-sequences the grid after any mutation that could have changed a value the
21877
- * active order sorts by. Ordering is a view over the data, so an edit that
21878
- * moves a row past its sibling must move the row, exactly as the filter makes
21879
- * a no-longer-matching row disappear.
21880
- *
21881
- * Production only re-sorts after a bar drag; diverging from that is a
21882
- * deliberate product decision, not an oversight.
21883
- */
21884
21565
  _reapplyActiveOrder(changes, resequencedParents) {
21885
21566
  const adapter = this.coreRuntime.state;
21886
21567
  if (adapter.getActiveOrder() === null) return null;
@@ -21915,22 +21596,15 @@ var ScheduleCore = class _ScheduleCore {
21915
21596
  context
21916
21597
  );
21917
21598
  const changedActivities = changesRequireFilterProjectionRebuild(changes) ? null : collectChangedActivities(changes, adapter);
21918
- const relativePlacementPins = adapter.getRelativePlacementPins();
21919
21599
  let affectedIds;
21920
21600
  let visibleIds;
21921
- const needsProjectionRebuild = changedActivities === null || !projectionIsCurrent || changesAffectRelativePinProjection(
21922
- changes,
21923
- changedActivities,
21924
- adapter,
21925
- relativePlacementPins
21926
- );
21601
+ const needsProjectionRebuild = changedActivities === null || !projectionIsCurrent;
21927
21602
  if (needsProjectionRebuild) {
21928
21603
  const rebuiltProjection = buildFilterProjection({
21929
21604
  activities: adapter.getAllActivities(),
21930
21605
  parentOf: (activityId) => adapter.getParentId(activityId),
21931
21606
  filter,
21932
- context,
21933
- relativePlacementPins
21607
+ context
21934
21608
  });
21935
21609
  adapter.setFilterProjection(rebuiltProjection);
21936
21610
  visibleIds = rebuiltProjection.visibleIds;
@@ -21986,11 +21660,7 @@ var ScheduleCore = class _ScheduleCore {
21986
21660
  this.coreRuntime.baseCalendars
21987
21661
  )
21988
21662
  );
21989
- const isCurrent = () => !cancellation.cancelled && this._status !== SCHEDULE_CORE_STATUS.DESTROYED && this._scheduleRevision === revision && // A mutation admitted but not yet settled is enough to make this result
21990
- // garbage. Without this term the calculation only learns it is obsolete
21991
- // when the mutation finishes, which measured live is the same instant the
21992
- // job itself ends.
21993
- this._pendingScheduleRevision === this._scheduleRevision;
21663
+ const isCurrent = () => !cancellation.cancelled && this._status !== SCHEDULE_CORE_STATUS.DESTROYED && this._scheduleRevision === revision && this._pendingScheduleRevision === this._scheduleRevision;
21994
21664
  const promise = (async () => {
21995
21665
  const calculatedFields = await computeCriticalPathFields(
21996
21666
  isolatedState,
@@ -22340,6 +22010,6 @@ var CREATION_KIND = {
22340
22010
  ROOT: "root"
22341
22011
  };
22342
22012
 
22343
- export { ACTIVITY_TYPE, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE2 as CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, NEW_ACTIVITY_DEFAULTS, REJECTION_REASON, ROOT_PARENT_ID, SCHEDULE_CORE_STATUS, STATUS, ScheduleCore, WORK_TIME_DIRECTION, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
22013
+ export { ACTIVITY_TYPE, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, NEW_ACTIVITY_DEFAULTS, REJECTION_REASON, ROOT_PARENT_ID, SCHEDULE_CORE_STATUS, STATUS, ScheduleCore, WORK_TIME_DIRECTION, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
22344
22014
  //# sourceMappingURL=index.js.map
22345
22015
  //# sourceMappingURL=index.js.map