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