@outbuild-company/schedule-core 1.2.0 → 1.3.0

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.d.cts CHANGED
@@ -1,95 +1,9 @@
1
- /**
2
- * Shared types for the custom AutoScheduler and its satellite packages
3
- * (recording, replay, shadow, discovery).
4
- *
5
- * This file is the single seam between all components. All scheduler code
6
- * depends ONLY on these types — never on the DHTMLX gantt object directly.
7
- */
8
- type ActivityId$1 = string;
9
- type LinkId$1 = string;
10
- /** Value constants for link types. Matches `gantt.config.links`. */
11
1
  declare const LINK_TYPE: {
12
2
  readonly FINISH_TO_START: "0";
13
3
  readonly START_TO_START: "1";
14
4
  readonly FINISH_TO_FINISH: "2";
15
5
  readonly START_TO_FINISH: "3";
16
6
  };
17
- interface LinkSnapshot {
18
- id: LinkId$1;
19
- source: ActivityId$1;
20
- target: ActivityId$1;
21
- type: LinkType;
22
- lag: number;
23
- /**
24
- * Precomputed lag components for a VIRTUAL summary-expanded link. When
25
- * present, the link calculator uses them verbatim instead of deriving the
26
- * components from leaf durations (the normal `normalize` switch).
27
- *
28
- * These exist so an FF/SF link INTO a summary can carry the SUMMARY's
29
- * duration (`_targetLag = -summary.duration`) and the per-leaf offset from
30
- * the summary's start (`_trueLag = userLag + off_tgt(leaf)`), matching legacy
31
- * `_getImplicitLinks`/`_convertToFinishToStartLink` under
32
- * `auto_scheduling_move_projects`. `_targetLag`/`_trueLag` are computed once
33
- * at expansion time, matching legacy relations frozen per run, and the
34
- * per-leaf offset in `_trueLag` applies only to leaves with no incoming
35
- * links (legacy `!c.$target.length` — a leaf with its own driver keeps
36
- * offset 0, otherwise the frozen offset compounds on every settle).
37
- * `_sourceLag` is the expansion-time fallback: when `_summarySourceId` is
38
- * present (SS/SF out of a summary), the ASAP calculator re-derives it at
39
- * evaluation time from the summary's CURRENT derived bounds so the virtual
40
- * constraint stays anchored at the summary start even after the anchor leaf
41
- * moves within the same run.
42
- *
43
- * `_summaryTargetId` marks a virtual link whose `_trueLag` bakes a NON-ZERO
44
- * leaf offset measured from the target summary's expansion-time bounds: the
45
- * ASAP pass refuses to pull the leaf back through such a link unless the
46
- * predecessor itself moved in the run (a stale frozen offset must never
47
- * drag its own leaf earlier — that is the summary-link drift bug).
48
- *
49
- * The three lag components must be set together (or none): a half-populated
50
- * link would mix precomputed and derived components. Every non-virtual link
51
- * leaves them undefined and hits the fast path.
52
- */
53
- _sourceLag?: number;
54
- _targetLag?: number;
55
- _trueLag?: number;
56
- _summarySourceId?: ActivityId$1;
57
- _summaryTargetId?: ActivityId$1;
58
- ganttId?: number;
59
- sectorId?: number;
60
- /** BE row id (distinct from the schedule-level `id`). */
61
- proplannerId?: number;
62
- }
63
-
64
- /**
65
- * Constraint Rules — single source of truth.
66
- *
67
- * This module owns the definition of constraint types and the rules that
68
- * govern them. Both the autoscheduler and the manual scheduling pipelines
69
- * (column edits, drag, bulk) consume the same primitives from here.
70
- *
71
- * Two surfaces:
72
- *
73
- * (a) Manual scheduling — pipelines call `checkConstraintViolation()` after
74
- * a user types a new value, to decide whether to emit a warning.
75
- * Surface: scalar Date inputs, no adapter needed.
76
- *
77
- * (b) Autoscheduler — `computeConstraintBounds()` derives the four date
78
- * boundaries (earliest/latest × start/end) an activity's constraint
79
- * imposes. `isStartWithinBounds()` and `isEndWithinBounds()` are used
80
- * by `limitPlanDates()` to decide whether a candidate date (from a
81
- * link) respects the constraint.
82
- *
83
- * Domain invariant the autoscheduler must respect:
84
- * The autoscheduler never moves an activity to a date that violates that
85
- * activity's own constraint. If a cascade via a link would push the
86
- * activity outside its constraint bounds, the activity stays put.
87
- *
88
- * Manual scheduling does NOT enforce this — a user is allowed to type a
89
- * value that violates their own constraint. The pipeline applies the edit
90
- * and emits a warning; the UI decides what to do with the warning (typically
91
- * shows a "constraint violation detected" modal and offers to revert).
92
- */
93
7
 
94
8
  type ConstraintType = 'asap' | 'alap' | 'snet' | 'snlt' | 'fnet' | 'fnlt' | 'mso' | 'mfo';
95
9
  declare const CONSTRAINT_TYPE: {
@@ -103,104 +17,44 @@ declare const CONSTRAINT_TYPE: {
103
17
  readonly MFO: "mfo";
104
18
  };
105
19
 
106
- /**
107
- * Public internal model types — the normalized form the core works with
108
- * after parsing the backend payload.
109
- *
110
- * Field naming follows snake_case for entity fields (matches backend +
111
- * DHTMLX convention) and camelCase for everything else, per Q14 in
112
- * SCHEDULE_CORE_API.md.
113
- */
114
20
  type ActivityId = string;
115
21
  type LinkId = string;
116
22
  type CalendarId = string;
117
23
  type ActivityType = 'project' | 'task' | 'milestone';
118
24
 
119
- /** DHTMLX link kinds: 0 = FS, 1 = SS, 2 = FF, 3 = SF. */
120
25
  type LinkType = '0' | '1' | '2' | '3';
121
- /** Direction for closest-work-time snapping. */
122
26
  type WorkTimeDirection = 'future' | 'past';
123
27
  declare const WORK_TIME_DIRECTION: {
124
28
  readonly FUTURE: "future";
125
29
  readonly PAST: "past";
126
30
  };
127
- /** Calendar query granularity (the engine boundary adds 'minute'/'any'). */
128
31
  type CalendarUnit = 'hour' | 'day';
129
32
  declare const CALENDAR_UNIT: {
130
33
  readonly HOUR: "hour";
131
34
  readonly DAY: "day";
132
35
  };
133
- /**
134
- * Project-level criterion used to redistribute `ponderator` (weight) among
135
- * sibling activities. Backend stores it lowercase on `projects.activity_creter`
136
- * (`'duration' | 'cost' | 'hh'`, default `'duration'`); the core normalizes
137
- * to uppercase. See `getActiveBaseline` / `recomputePonderatorsForParent`.
138
- */
139
36
  type ActivityCreter = 'DURATION' | 'COST' | 'HH';
140
- /** Criterion for the `status` column: compare progress against baseline or live expected. */
141
37
  type StatusCriteria = 'Baseline' | 'Actual';
142
- /**
143
- * A versioned container of baseline points for a sector
144
- * (backend `sectorbaselineversion` model). Exactly one version per sector
145
- * has `active: true` — that is the one the ponderator computation and the
146
- * baseline columns read.
147
- */
148
38
  interface BaselineVersion {
149
39
  id?: number;
150
40
  name?: string | null;
151
- /** Marks the live baseline. Exactly one active version per sector. */
152
41
  active?: boolean;
153
42
  visible?: boolean;
154
- /** Backend extras passed through opaquely. */
155
43
  [key: string]: unknown;
156
44
  }
157
- /**
158
- * A single baseline data point for an activity, as stored by the backend
159
- * (`sectorbaselinepoint` model) and carried verbatim through the core.
160
- * Snapshots the activity's planned start/end/duration/cost/work at the
161
- * moment a baseline version was saved. The schedule reads the ACTIVE point
162
- * (see `getActiveBaseline`) to derive `ponderator` (baseline-only, no
163
- * fallback — see memory `project_ponderator_baseline_fix`).
164
- *
165
- * Casing note: the frontend baseline columns (`startBaseCol` …
166
- * `durationBaseCol`) read these fields directly off the activity, so the
167
- * core preserves the backend snake_case exactly (passthrough, not parsed).
168
- *
169
- * Unit trap: `duration` is in DAYS (backend unit), unlike the activity's
170
- * `duration` which the core models in working hours — convert with the
171
- * sector's hours-per-day before arithmetic (legacy `transformDaysToHours`).
172
- */
173
45
  interface BaselinePoint {
174
- /** Planned start, backend date string `"YYYY/MM/DD H:MM"` or ISO. */
175
46
  start_date: string;
176
- /** Planned end (inclusive). */
177
47
  end_date: string;
178
- /** Planned duration in DAYS (backend unit). */
179
48
  duration: number;
180
- /** Planned cost. May arrive as a numeric string. */
181
49
  cost: number | string;
182
- /** Planned work hours (HH). May arrive as a numeric string. */
183
50
  hh_work: number | string;
184
- /** The version this point belongs to; `.active` selects the live one. */
185
51
  sectorbaselineversion?: BaselineVersion;
186
52
  sectorbaselineversionId?: number;
187
53
  baseCalendarId?: number;
188
54
  hoursPerDay?: number;
189
55
  hoursPerWeek?: number;
190
- /** Backend extras passed through opaquely. */
191
56
  [key: string]: unknown;
192
57
  }
193
- /**
194
- * Pure-domain activity model produced by `parseFromBackend`. Snake_case
195
- * matches the backend convention. No index signature — every field is
196
- * explicit. New backend fields must be added here and preserved by
197
- * `normalize-activity.ts`.
198
- */
199
- /**
200
- * A pending SIR freezing an activity, reduced to the only field the core
201
- * needs: its id (equal to the backend SIR id and the front's Redux item id).
202
- * The schedule load already filters to pending, so presence ⇒ pending.
203
- */
204
58
  interface PendingRequest {
205
59
  id: string;
206
60
  }
@@ -234,7 +88,6 @@ interface CoreBaselinePoint {
234
88
  workHours: number;
235
89
  versionId: number | null;
236
90
  isActiveVersion: boolean;
237
- /** Whether this version is the one selected for visual comparison. */
238
91
  isVisibleVersion: boolean;
239
92
  baseCalendarId: CalendarId | null;
240
93
  hoursPerDay: number | null;
@@ -294,79 +147,25 @@ interface Link {
294
147
  lag: number;
295
148
  ganttId?: number;
296
149
  sectorId?: number;
297
- /** BE row id (distinct from the schedule-level `id`/`unique_id`). */
298
150
  proplannerId?: number;
299
151
  }
300
152
  interface CalendarWorktime {
301
- /**
302
- * Per-weekday working hours, length 7 — index 0 = Sunday.
303
- *
304
- * - `false` → non-working day
305
- * - `string[]` → working day with these `"HH:MM-HH:MM"` windows
306
- * (supports per-day variation, e.g. a short Friday,
307
- * and multi-shift split days like `["8-12","13-17"]`)
308
- *
309
- * Single source of truth: this is exactly what `parseFromBackend` emits. A
310
- * usable working day carries its resolved windows; `[]` is reserved for a
311
- * catalog calendar that declares the day as working but provides no usable
312
- * shift anywhere. Those catalog-only calendars remain selectable/persistable
313
- * but are deliberately not registered as arithmetic calendars. The vendored
314
- * work-calendar engine natively also accepts a boolean `true` per day (DHTMLX
315
- * contract), but that form never appears in the domain `Calendar`; it is
316
- * reserved for the engine-facing internals (`SnapshotWorktime`,
317
- * synthetic replay fixtures).
318
- *
319
- * The work-calendar engine consumes each weekday independently, so
320
- * different days can carry different windows.
321
- */
322
153
  days: Array<false | string[]>;
323
- /**
324
- * Default working-hour windows (`"HH:MM-HH:MM"`). Used as the engine's
325
- * global fallback. `parseFromBackend` sets it to the first usable working
326
- * day's windows, or `[]` when the calendar has no usable shift.
327
- */
328
154
  hours: string[];
329
- /**
330
- * Per-date exceptions: holidays (non-working) and date-level shift
331
- * overrides. Keys are `Date.UTC(year, month, day)` (UTC midnight epoch
332
- * ms) stringified — this matches the lookup the work-calendar engine
333
- * performs internally at `calendar.dates[dateValue]`.
334
- *
335
- * Value semantics:
336
- * - `false` → the day is non-working
337
- * - `string[]` → use these `"HH:MM-HH:MM"` windows instead
338
- * of the default `hours`
339
- *
340
- * Source: `BackendCalendarInput.exceptiondays[]` after the adapter expands
341
- * `from_date`/`to_date` ranges into individual UTC days.
342
- */
343
155
  dates?: Record<string, false | string[]>;
344
- /** Optional per-week overrides for advanced calendars. */
345
156
  customWeeks?: Record<string, unknown>;
346
157
  }
347
158
  interface Calendar {
348
159
  id: CalendarId;
349
- /** Display name. Used by the DHTMLX calendar-selector column. */
350
160
  name: string;
351
161
  worktime: CalendarWorktime;
352
162
  is_default: boolean;
353
- /**
354
- * True for "base" calendars (baseline comparison). The backend ships
355
- * these with an `id` suffixed `-base` and `baseDefault` instead of
356
- * `is_default`. The bridge uses this to set `gantt.defaultBaseCalendar`
357
- * and to keep base calendars out of the selector dropdown.
358
- */
359
163
  baseDefault?: boolean;
360
- /**
361
- * Date-keyed map of exceptions (holidays, working overrides).
362
- * Keys are `YYYY-MM-DD` UTC.
363
- */
364
164
  exceptions?: Record<string, {
365
165
  working: boolean;
366
166
  hours?: string[];
367
167
  }>;
368
168
  }
369
- /** Sector metadata the core respects from the backend. */
370
169
  interface SectorMetadata {
371
170
  id: string;
372
171
  name: string;
@@ -375,48 +174,12 @@ interface SectorMetadata {
375
174
  dateFormat: string;
376
175
  projectId: number;
377
176
  companyId: number;
378
- /**
379
- * Project's `custom_id_prefix` (Outbuild project setting). Used by the
380
- * CustomIdTracker to generate ids for new activities. Falls back to
381
- * `'A'` if not provided.
382
- */
383
177
  customIdPrefix?: string;
384
- /**
385
- * Project's `custom_id_increment` (Outbuild project setting), as a
386
- * string so leading zeros are preserved. Used by the CustomIdTracker
387
- * to choose the next suffix step. Falls back to `'10'` if not provided.
388
- */
389
178
  customIdIncrement?: string;
390
- /**
391
- * Legacy Primavera-import flag. When true, the core recomputes each
392
- * activity's `duration` from its start/end against its calendar at load
393
- * (Primavera stores end-driven durations that may not match the
394
- * calendar). Mirrors the legacy `fixPrimaveraDurations`. See CALENDARS.md.
395
- */
396
179
  updateDurationForPrimaveraEndDate?: boolean;
397
- /**
398
- * Project's `activity_creter` — criterion for ponderator redistribution
399
- * (`'DURATION' | 'COST' | 'HH'`). Lives at the project level; arrives via
400
- * the sector payload's pass-through bucket. `parseSector` always sets it,
401
- * defaulting to `'DURATION'` (the backend default) when absent/unknown.
402
- */
403
180
  activityCreter?: ActivityCreter;
404
- /**
405
- * Project's status criterion for the `status` column — `'Baseline'` compares
406
- * `progress` against `expected_progress_base`, `'Actual'` against
407
- * `expected_progress`. Mutable at runtime via `ScheduleCore.setStatusCriteria`
408
- * (the Baseline↔Actual toggle). Not a backend field (client-only session
409
- * setting); a complete sector always carries it — `parseSector` sets the
410
- * default and `resolveStatusCriteria` normalizes the client override at init.
411
- * See `docs/domain/columns/COLUMN_status.md`.
412
- */
413
181
  statusCriteria: StatusCriteria;
414
182
  }
415
- /**
416
- * A single entity mutation record emitted by a dispatch ChangeSet.
417
- * Layer-neutral: contains no dispatch-specific dependencies and is used
418
- * both by `dispatch/` and by `internal/tracking/`.
419
- */
420
183
  interface EntityChange<T> {
421
184
  id: string;
422
185
  kind: 'created' | 'updated' | 'deleted';
@@ -470,45 +233,33 @@ declare const COLUMN: {
470
233
  };
471
234
  type ColumnName = (typeof COLUMN)[keyof typeof COLUMN];
472
235
 
473
- /**
474
- * Task creation pipeline — types.
475
- *
476
- * Ports the pure parts of `src/assets/js/custom_actions/createTask/` into a
477
- * functional pipeline that replay tests (and eventually prod) can invoke
478
- * against an adapter instead of the live gantt.
479
- */
480
-
481
- /**
482
- * Closed, canonical contract for create/paste overrides: camelCase, working
483
- * hours, limited to the fields a caller may seed on a new activity. Identity
484
- * and structure (`id`/`parentId`/`correlativeId`/`uniqueCorrelativeId`),
485
- * derived/CP/baseline outputs and view-state are NOT overridable. PP2 adapts
486
- * its own vocabulary to this contract in step 9.
487
- */
488
236
  declare const CREATION_OVERRIDE_FIELD_LIST: readonly ["name", "description", "type", "startDate", "durationHours", "constraintType", "constraintDate", "calendarId", "assignedDefaultCalendar", "autoScheduling", "progress", "cost", "usedCost", "realCost", "workHours", "realWorkHours", "ponderator", "hasCustomPonderator", "customId", "subcontractId", "responsableIds", "tagIds", "isLookahead"];
489
237
  type CreationOverrideField = (typeof CREATION_OVERRIDE_FIELD_LIST)[number];
490
238
  type CreationOverrides = Partial<Pick<CoreActivity, CreationOverrideField>>;
491
239
 
492
- /**
493
- * Public dispatch types see Q8/Q9/Q10 in SCHEDULE_CORE_API.md.
494
- *
495
- * Supported kinds:
496
- * - `inline-edit` — edit a single column on an activity.
497
- * - `link-create` — add a link between two activities.
498
- * - `link-update` — change the lag and/or type of an existing link.
499
- * - `link-delete` — remove a link by id.
500
- * - `activity-create` — create a new activity (root, child, or sibling).
501
- * - `activity-delete` cascade-delete activities + incident links.
502
- * - `activity-move` — reorder among siblings or move to a different parent.
503
- * - `activity-indent` multi-select indent (children of previous non-selected sibling).
504
- * - `activity-outdent` — multi-select outdent (move to grandparent level).
505
- * - `activity-set-progress` — complete/uncomplete (0/100) with cascade.
506
- * - `selection-toggle` / `selection-replace` — checkbox selection (prop-only).
507
- * - `activity-paste` — bulk paste (N activities + M links, one atomic ChangeSet).
508
- * - `dates-batch` — K date edits as one unit of work (multi-drag).
509
- * - `bulk-edit` — K edits on any pipeline column as one unit of work (bulk editors).
510
- * - `links-batch` — K link create/update/delete as one atomic unit (mass link/unlink buttons).
511
- */
240
+ type StringOperator = 'includes' | 'notIncludes' | 'is' | 'isNot';
241
+ type NumberOperator = 'equals' | 'notEquals' | 'greaterThan' | 'lessThan' | 'greaterOrEqual' | 'lessOrEqual';
242
+ type DateOperator = 'after' | 'before';
243
+ type MembershipOperator = 'someOf' | 'notSomeOf';
244
+ type FilterOperator = StringOperator | NumberOperator | DateOperator | MembershipOperator;
245
+ type FilterField = 'name' | 'description' | 'customId' | 'correlativeId' | 'uniqueCorrelativeId' | 'progress' | 'durationDays' | 'calendarDuration' | 'cost' | 'usedCost' | 'realCost' | 'workHours' | 'realWorkHours' | 'ponderator' | 'freeSlackDays' | 'totalSlackDays' | 'expectedProgressBaseline' | 'baselineDurationDays' | 'baselineCost' | 'baselineWorkHours' | 'startDate' | 'endDate' | 'constraintDate' | 'baselineStartDate' | 'baselineEndDate' | 'earlyStart' | 'earlyFinish' | 'lateStart' | 'lateFinish' | 'responsableIds' | 'tagIds' | 'status' | 'constraintType' | 'calendarId' | 'subcontractId' | 'isCritical';
246
+ type FilterScalarValue = string | number | Date;
247
+ type FilterCriterionValue = FilterScalarValue | ReadonlyArray<string | number>;
248
+ interface FilterCriterion {
249
+ readonly field: FilterField;
250
+ readonly operator: FilterOperator;
251
+ readonly value: FilterCriterionValue;
252
+ }
253
+ interface FilterDateRange {
254
+ readonly start: Date;
255
+ readonly end: Date;
256
+ }
257
+ interface FilterState {
258
+ readonly criteria: ReadonlyArray<FilterCriterion>;
259
+ readonly logic: 'and' | 'or';
260
+ readonly dateRange?: FilterDateRange;
261
+ }
262
+
512
263
  type LinksBatchOperation = {
513
264
  kind: 'create';
514
265
  source: ActivityId;
@@ -549,13 +300,6 @@ type DispatchAction = {
549
300
  column: string;
550
301
  newValue: unknown;
551
302
  } | {
552
- /**
553
- * K ediciones de fecha como UNA unidad de trabajo (multi-drag): cada
554
- * edit corre su pipeline individual (verdict por barra), pero el
555
- * post-mutation (autoscheduler → bounds → CP) corre UNA sola vez y
556
- * sale UN ChangeSet. Patrón hermano de `activity-paste` /
557
- * `dispatchInlineEditLinks`. Track onAfterTaskDrag F2.2.
558
- */
559
303
  kind: 'dates-batch';
560
304
  edits: ReadonlyArray<{
561
305
  activityId: ActivityId;
@@ -563,21 +307,6 @@ type DispatchAction = {
563
307
  newValue: unknown;
564
308
  }>;
565
309
  } | {
566
- /**
567
- * K edits on ANY pipeline column as ONE unit of work — the
568
- * generalization of `dates-batch` to the 16 registry columns
569
- * (schedule bulk editors / modals; PLAN_convergencia_wiring §4.1).
570
- * Each edit resolves its pipeline via `findPipelineForColumn` and
571
- * runs phases 1-4 with an individual verdict (a rejected edit does
572
- * NOT abort the others — paste semantics). A column without a
573
- * pipeline (e.g. the link columns `custom_predecessors` /
574
- * `custom_sucessors`, which mutate the link graph and dispatch as
575
- * link intents) rejects ONLY that edit. The post-mutation pass
576
- * (autoscheduler → parent bounds → CP) runs ONCE at the end and a
577
- * single ChangeSet comes out = ONE undo step (never coalesces).
578
- * Supports both shapes: K activities × same column, and 1 activity
579
- * × several columns (atomic multi-field edit).
580
- */
581
310
  kind: 'bulk-edit';
582
311
  edits: ReadonlyArray<{
583
312
  activityId: ActivityId;
@@ -590,16 +319,12 @@ type DispatchAction = {
590
319
  source: ActivityId;
591
320
  target: ActivityId;
592
321
  type: LinkType;
593
- /** Lag in DAYS (public boundary unit; converted to working hours inside). */
594
322
  lag: number;
595
- /** Optional explicit id (tests pass deterministic ids). */
596
323
  linkId?: LinkId;
597
324
  } | {
598
325
  kind: 'link-update';
599
326
  linkId: LinkId;
600
- /** New type. Omit to preserve. */
601
327
  type?: LinkType;
602
- /** New lag in DAYS (public boundary unit). Omit to preserve. */
603
328
  lag?: number;
604
329
  } | {
605
330
  kind: 'link-delete';
@@ -609,254 +334,84 @@ type DispatchAction = {
609
334
  operations: ReadonlyArray<LinksBatchOperation>;
610
335
  } | {
611
336
  kind: 'activity-create';
612
- /** Where to put the new activity. Use `'0'` for root. */
613
337
  parentId: ActivityId | '0';
614
- /**
615
- * Optional — insert the new activity immediately AFTER this sibling.
616
- * If omitted (and `beforeSiblingId` also omitted), the new activity
617
- * is appended as the last child of `parentId`. The sibling must
618
- * currently be a child of `parentId`, otherwise the dispatch is
619
- * rejected. Mutually exclusive with `beforeSiblingId`.
620
- */
621
338
  afterSiblingId?: ActivityId | undefined;
622
- /**
623
- * Optional — insert the new activity immediately BEFORE this sibling.
624
- * Use case: "insert as first child" (`beforeSiblingId = currentFirstChild`)
625
- * or "insert at position N" (`beforeSiblingId = children[N]`).
626
- * The sibling must currently be a child of `parentId`, otherwise the
627
- * dispatch is rejected. Mutually exclusive with `afterSiblingId`.
628
- */
629
339
  beforeSiblingId?: ActivityId | undefined;
630
- /**
631
- * Canonical field overrides applied on top of the creation defaults —
632
- * camelCase, working hours, limited to creation-writable fields
633
- * (`CreationOverrides`). Identity/structure and derived outputs are not
634
- * overridable.
635
- */
636
340
  overrides?: CreationOverrides;
637
- /**
638
- * Deterministic activity id — used by tests and by replay flows
639
- * (paste, undo) that need to assign a specific id. If omitted, the
640
- * core's internal id generator allocates one.
641
- */
642
341
  activityId?: ActivityId;
643
- /**
644
- * Where this create originated from — passed through to the
645
- * `schedule_activity_creation` Amplitude event. Free-form string
646
- * (matches legacy `INSERT_EVENT_SOURCES`).
647
- */
648
342
  eventSource?: string | undefined;
649
343
  } | {
650
344
  kind: 'activity-delete';
651
- /**
652
- * IDs to delete. Each id triggers a cascade delete of its entire
653
- * subtree. Links incident to any deleted activity are removed too.
654
- * Operation is atomic — if any id is missing, the whole dispatch is
655
- * rejected before mutating anything.
656
- */
657
345
  activityIds: ReadonlyArray<ActivityId>;
658
346
  eventSource?: string;
659
347
  } | {
660
348
  kind: 'activity-move';
661
- /**
662
- * The activity to move. Single-id only — for batch reorder use
663
- * multiple dispatches (each one observes the post-previous state).
664
- */
665
349
  activityId: ActivityId;
666
- /** Destination parent. Same as current parent = reorder within. */
667
350
  parentId: ActivityId | '0';
668
- /**
669
- * Optional — position the moved activity immediately below this
670
- * sibling. If omitted, appended as the last child of `parentId`.
671
- * The sibling must currently be a child of `parentId`; otherwise
672
- * the dispatch is rejected. Mutually exclusive with `beforeSiblingId`.
673
- */
674
351
  afterSiblingId?: ActivityId;
675
352
  beforeSiblingId?: ActivityId;
676
353
  eventSource?: string;
677
354
  } | {
678
355
  kind: 'activity-indent';
679
- /**
680
- * IDs to indent. Multi-select honors chain semantics: each id is
681
- * moved under its closest previous sibling that is NOT in the
682
- * selected set, so consecutive selected siblings all land under the
683
- * same anchor (matches legacy `findPreviousNonSelectedSibling`).
684
- */
685
356
  activityIds: ReadonlyArray<ActivityId>;
686
357
  eventSource?: string;
687
358
  } | {
688
359
  kind: 'activity-outdent';
689
- /**
690
- * IDs to outdent. Multi-select preserves relative order among the
691
- * selected siblings via initial-index capture (matches legacy
692
- * `initialFirstSiblings` + `initialTaskIndexes` pre-loop setup).
693
- */
694
360
  activityIds: ReadonlyArray<ActivityId>;
695
361
  eventSource?: string;
696
362
  } | {
697
- /**
698
- * Set progress to 0 or 100 with recursive cascade semantics.
699
- *
700
- * Triggered by the schedule's "complete" / "uncomplete" buttons.
701
- * **Distinct intent from `inline-edit`** — bypasses the
702
- * `isSummaryActivity` gate of `progressPipeline.canEdit` because
703
- * the user is explicitly invoking the recursive operation
704
- * ("complete this parent and all its descendants"), not editing
705
- * a single cell.
706
- *
707
- * Inline edits on the progress column still use `inline-edit` with
708
- * full `canEdit`/`validate` gating (which blocks summary activities
709
- * by design). See [[complete activity business rules]] R8 and the
710
- * bifurcation rationale.
711
- *
712
- * Internally reuses the `progressPipeline.transform` so cascade,
713
- * rollup, tracking and visualization are identical to inline-edit
714
- * with newValue=0 or newValue=100.
715
- */
716
363
  kind: 'activity-set-progress';
717
364
  activityId: ActivityId;
718
- /** Strictly 0 (uncomplete) or 100 (complete). */
719
365
  newValue: 0 | 100;
720
366
  eventSource?: string;
721
367
  } | {
722
- /**
723
- * Checkbox selection toggle (2026-06-06). The core is the
724
- * authority of the check state (`checked` / `visibleChecked` /
725
- * `mustApplyVisibleChecked` written as a single bit): it runs the
726
- * full propagation (descendants cascade, frozen partition with
727
- * parent blocking, unified upward pass — see
728
- * `internal/selection/compute-selection-update.ts`) and emits a
729
- * prop-only ChangeSet the bridge applies in bulk to DHTMLX
730
- * (silent store write + viewport repaint). No autoscheduler, no
731
- * critical path.
732
- */
733
368
  kind: 'selection-toggle';
734
369
  activityId: ActivityId;
735
- /** Checkbox state AFTER the user click. */
736
370
  isChecked: boolean;
737
- /**
738
- * Ids frozen by pending SIRs, computed by the caller from the
739
- * runtime (DHTMLX task props — the core's own `pendingRequests`
740
- * is load-time-stale until TODO #10). When omitted, the core
741
- * falls back to its own snapshots.
742
- */
743
371
  frozenIds?: ReadonlyArray<ActivityId>;
744
- /**
745
- * Enable the upward auto-check of ancestors when all their
746
- * checkable children become selected. Mirrors the (inverted)
747
- * CHECKBOX_NOT_AUTOMATIC_PARENT_SELECTION flag. Default: false.
748
- */
749
372
  autoCheckParents?: boolean;
750
373
  eventSource?: string;
751
374
  } | {
752
- /**
753
- * Replace the whole selection with an exact id set — no
754
- * propagation. Used by clear / block-selection / external sync.
755
- * Unknown ids are ignored. Same prop-only ChangeSet contract as
756
- * `selection-toggle`.
757
- */
758
375
  kind: 'selection-replace';
759
376
  activityIds: ReadonlyArray<ActivityId>;
760
377
  eventSource?: string;
761
378
  } | {
762
- /**
763
- * View-model visibility set (Fase 1, 2026-07-04). ABSOLUTE set: the
764
- * UI sends the COMPLETE list of ids visible after its filters run;
765
- * the core diffs against the current `visible` bits (an id changes
766
- * when `(visible !== false) !== willBeVisible`) and emits a
767
- * prop-only ChangeSet carrying ONLY the `visible` field for the
768
- * activities that changed. Unknown ids are ignored. Same prop-only
769
- * contract as `selection-replace`: no autoscheduler, no critical
770
- * path, no undo step. Idempotent.
771
- */
772
379
  kind: 'visibility-set';
773
380
  visibleIds: ReadonlyArray<ActivityId>;
774
381
  eventSource?: string;
775
382
  } | {
776
- /**
777
- * Sync the pending-SIR set for one activity into the core
778
- * (2026-06-08). ABSOLUTE set: the bridge sends the activity's current
779
- * pending requests whenever a SIR is created/resolved in the front,
780
- * so the core's freeze bit stays fresh (closes TODO #10) and is the
781
- * standalone authority for selection gating + auto-reject. Prop-only
782
- * ChangeSet; no autoscheduler, no CP. Idempotent.
783
- */
383
+ kind: 'filter-set';
384
+ criteria: ReadonlyArray<FilterCriterion>;
385
+ logic: 'and' | 'or';
386
+ dateRange?: FilterDateRange;
387
+ eventSource?: string;
388
+ } | {
784
389
  kind: 'sir-sync';
785
390
  activityId: ActivityId;
786
391
  pendingRequests: ReadonlyArray<PendingRequest>;
787
392
  eventSource?: string;
788
393
  } | {
789
- /**
790
- * Synchronize the schedule-side flags owned by Lookahead task
791
- * creation/deletion. The Lookahead records remain at the application
792
- * boundary; the core owns these flags because they affect schedule
793
- * editing rules and the public activity view.
794
- */
795
394
  kind: 'activity-lookahead-sync';
796
395
  activityIds: ReadonlyArray<ActivityId>;
797
396
  isLookahead: boolean;
798
397
  hasLookaheadTasks: boolean;
799
398
  eventSource?: string;
800
399
  } | {
801
- /**
802
- * Bulk paste (2026-06-08). Creates N activities (preserving the
803
- * copied subtree hierarchy) plus M internal links in a SINGLE
804
- * atomic ChangeSet — the future unit of undo. Replaces the
805
- * DHTMLX-side paste that bypassed the core via `gantt.isPasting`.
806
- *
807
- * The caller (bridge) supplies, per activity, the field
808
- * `overrides` already shaped by the paste prepare step (resets +
809
- * validated catalogs). The core allocates ids/uids, re-hangs the
810
- * hierarchy via `originalId → newId`, promotes parents, runs the
811
- * hh/cost cascade + correlative sweep + autoscheduler ONCE at the
812
- * end of the batch (not per activity).
813
- *
814
- * See [[COPY_PASTE_CORE_MIGRATION_DESIGN]].
815
- */
816
400
  kind: 'activity-paste';
817
- /**
818
- * Where the pasted roots land. `{ parentId, index }` pastes INSIDE
819
- * the reference (index 0 = first child). `{ afterSiblingId }`
820
- * pastes as a sibling immediately below the reference; the parent
821
- * is derived from the sibling.
822
- */
823
401
  destination: {
824
402
  parentId: ActivityId | '0';
825
403
  index: number;
826
404
  } | {
827
405
  afterSiblingId: ActivityId;
828
406
  };
829
- /**
830
- * The activity the paste is anchored to (the single selected row).
831
- * Used as the custom_id reference for pasted children. Replaces
832
- * the legacy `gantt.pasteReferenceActivity` instance flag.
833
- */
834
407
  referenceActivityId: ActivityId;
835
- /**
836
- * Activities to create, in tree order (parents before children).
837
- * `originalId` / `originalParentId` are the ids from the copied
838
- * payload; the core remaps them to freshly allocated ids and uses
839
- * the remap to re-hang the hierarchy and the links. A pasted activity
840
- * may carry its visible baseline snapshot, but it is not enrolled in an
841
- * already-created historical baseline.
842
- */
843
408
  activities: ReadonlyArray<PastedActivityInput>;
844
- /**
845
- * Internal links to recreate. `source`/`target` reference the
846
- * ORIGINAL (copied) ids; links with an endpoint outside the
847
- * pasted set are dropped (dangling).
848
- */
849
409
  links: ReadonlyArray<PastedLinkInput>;
850
410
  eventSource?: string;
851
411
  };
852
412
  interface PastedActivityInput {
853
413
  readonly originalId: ActivityId;
854
414
  readonly originalParentId: ActivityId | '0';
855
- /**
856
- * Optional explicit destination id. Production normally omits it and lets
857
- * the adapter allocate; recorder replay supplies MAIN's observed id so the
858
- * whole-tree oracle can compare identity without an invented id remap.
859
- */
860
415
  readonly activityId?: ActivityId;
861
416
  readonly overrides: CreationOverrides;
862
417
  readonly baselineSnapshot?: ActivityBaselineSnapshot | null;
@@ -865,51 +420,20 @@ interface PastedLinkInput {
865
420
  readonly source: ActivityId;
866
421
  readonly target: ActivityId;
867
422
  readonly type: LinkType;
868
- /** Lag in DAYS (public boundary unit; converted to working hours inside). */
869
423
  readonly lag: number;
870
424
  readonly linkId?: LinkId;
871
425
  }
872
426
  interface DispatchOptions {
873
- /** Skip the autoscheduler pass after the mutation. Default: false. */
874
427
  skipAutoSchedule?: boolean;
875
- /** Skip the CP recompute after the mutation. Default: false. */
876
428
  skipCriticalPath?: boolean;
877
- /**
878
- * Unit of the INPUT `newValue` for unit-bearing inline-edit columns (today:
879
- * duration). Default 'days' (public contract; card + tests send days). The
880
- * DHTMLX grid editor produces the store unit (HOURS via formatter.parse), so
881
- * the bridge passes 'hours' for those edits → the pipeline skips the days→hours
882
- * conversion instead of double-converting.
883
- */
884
429
  inputUnit?: 'days' | 'hours';
885
430
  }
886
431
  type DispatchResult = {
887
432
  ok: true;
888
433
  changes: ChangeSet;
889
- /**
890
- * Solo los intents batch (`dates-batch` / `bulk-edit`): resultado
891
- * POR EDIT, en el orden de `edits`. Un edit rechazado no aborta a
892
- * los demás (semántica paste); el bridge re-proyecta solo los
893
- * rechazados.
894
- */
895
434
  verdicts?: ReadonlyArray<DatesBatchVerdict>;
896
435
  activityVerdicts?: ReadonlyArray<DatesBatchVerdict>;
897
436
  linkVerdicts?: ReadonlyArray<LinkBatchVerdict>;
898
- /**
899
- * INTERNAL (undo / B4). Full pre-mutation snapshot of every
900
- * deleted/structurally-removed activity row, keyed by STRING id. The
901
- * ChangeSet emits deleted rows as `{ kind:'deleted', after:null }` with no
902
- * `before`, so resurrection on undo needs this. Populated by structural
903
- * handlers (delete/move/paste); stripped before the result reaches the
904
- * bridge in the Wave-3 integration.
905
- */
906
- __beforeSnap?: ReadonlyMap<string, CoreActivity>;
907
- /**
908
- * INTERNAL (undo / B4). Incident links of deleted activities, keyed by
909
- * STRING id (EntityChange link ids are String()-normalized). Carries the
910
- * engine `LinkSnapshot` (lag in working hours) for unit-correct re-add.
911
- */
912
- __beforeLinks?: ReadonlyMap<string, LinkSnapshot>;
913
437
  } | {
914
438
  ok: false;
915
439
  reason: string;
@@ -940,34 +464,17 @@ interface TrackingEvent {
940
464
  name: string;
941
465
  properties: Record<string, unknown>;
942
466
  }
943
- /**
944
- * Side effects a dispatch emits that are NOT entity-field changes and NOT
945
- * Amplitude tracking — commands the bridge must act on. Today: SIR
946
- * auto-reject (a frozen activity's dates changed or it was deleted, so its
947
- * pending SIR must be rejected). The core owns the rule; the bridge consumes
948
- * the effect (Redux REJECTED; durable persistence rides the activity save).
949
- * See `SIR_MAP.md` §10.
950
- */
951
467
  type ScheduleEffect = {
952
468
  kind: 'sir-auto-reject';
953
469
  activityId: ActivityId;
954
- /** Backend SIR id == the front Redux item id (see parseFromBackend A1). */
955
470
  sirId: string;
956
471
  reason: 'date_changed' | 'activity_deleted';
957
472
  };
958
- /**
959
- * A direct user edit violated the activity's own (pre-edit) constraint. Per
960
- * the canonical model (PRD constraint violations, R2/R3): the edit APPLIES
961
- * anyway — the warning is data for the UI (constraint validation modal),
962
- * never a rejection, and it is only emitted from direct edits (the
963
- * autoscheduler never violates, R1). Decision D-A 2026-07-04.
964
- */
965
473
  interface ConstraintWarning {
966
474
  kind: 'constraint_violation';
967
475
  activityId: ActivityId;
968
476
  constraintType: ConstraintType;
969
477
  constraintDate: Date;
970
- /** The projected date that violated (start or end, per constraint type). */
971
478
  projectedDate: Date;
972
479
  messageKey: string;
973
480
  }
@@ -993,23 +500,12 @@ interface ChangeSet {
993
500
  calendars: ReadonlyArray<EntityChange<Calendar>>;
994
501
  trackingEvents: ReadonlyArray<TrackingEvent>;
995
502
  viewState?: ReadonlyArray<ViewStateChange>;
996
- /**
997
- * Optional — present only when the dispatch produced side effects. Builders
998
- * that emit none omit it; consumers read `changeSet.effects ?? []`. Kept
999
- * optional to avoid churning every ChangeSet construction site. SIR (A4).
1000
- */
1001
503
  effects?: ReadonlyArray<ScheduleEffect>;
1002
- /**
1003
- * Optional — present only when a direct edit violated a constraint (see
1004
- * `ConstraintWarning`). Consumers read `changeSet.warnings ?? []`.
1005
- */
1006
504
  warnings?: ReadonlyArray<ConstraintWarning>;
1007
505
  }
1008
506
 
1009
507
  interface ProjectWorkHours {
1010
- /** Earliest start of the working day across the week, `"HH:MM"`. */
1011
508
  startHour: string;
1012
- /** Latest end of the working day across the week, `"HH:MM"`. */
1013
509
  endHour: string;
1014
510
  }
1015
511
 
@@ -1118,102 +614,18 @@ interface BackendScheduleInput {
1118
614
  baseCalendars?: BackendCalendarInput[] | undefined;
1119
615
  }
1120
616
 
1121
- /**
1122
- * Injected reporting port. The core is UI-agnostic: it cannot `console.*`,
1123
- * show UI, or know about Sentry. When it hits a recoverable data-quality
1124
- * issue (`warn`) or a failure it could not complete (`error`), it calls this
1125
- * port; the consumer (react_client) implements it and decides the outcome
1126
- * (Sentry, toast, nothing). Same injected-port pattern as `AutoSchedulerPort`.
1127
- */
1128
617
  interface ScheduleCoreReporter {
1129
- /** Recoverable data-quality issue; the core continues with its fallback. */
1130
618
  warn(message: string, context?: unknown): void;
1131
- /** A real failure the core could not complete. */
1132
619
  error(message: string, cause?: unknown): void;
1133
620
  }
1134
621
 
1135
- /**
1136
- * initialize-core — the construction + load-pass orchestration extracted from
1137
- * the `ScheduleCore` constructor. `initializeCore(input)` performs the entire
1138
- * load sequence (parse → snapshot → state → demote → id generators →
1139
- * custom-id tracker → optional baseline block → initial passes) and returns
1140
- * the assembled internals (`CoreInternals`) for the facade to assign. The
1141
- * facade constructor is assignment-only.
1142
- *
1143
- * The load-pass ORDER here is a CONTRACT — see `CURSOR.md`
1144
- * §"Flujo de core.dispatch paso a paso" and the inline comments below. Do not
1145
- * reorder, merge, or drop any step.
1146
- *
1147
- * The public types (`ScheduleCoreStatus`, `ScheduleCoreInput`) live here so
1148
- * `initialize-core.ts` never imports from `scheduleCore.ts` (which imports
1149
- * `initializeCore`), avoiding an import cycle. `scheduleCore.ts` re-exports the
1150
- * two public types
1151
- * so `src/index.ts`'s existing `export type { ... } from './init/schedule-core.js'`
1152
- * keeps resolving unchanged.
1153
- */
1154
-
1155
622
  type ScheduleCoreStatus = 'ready' | 'destroyed' | 'poisoned';
1156
623
  interface ScheduleCoreInput extends BackendScheduleInput {
1157
- /**
1158
- * Skip the full autoscheduler pass that normally runs from the
1159
- * constructor (`runInitialPasses`).
1160
- *
1161
- * Backend data already ships with `start_date` / `end_date` / `duration`
1162
- * scheduled — running the full ASAP+ALAP graph at load is redundant and
1163
- * extremely slow on large schedules (thousands of activities + links).
1164
- *
1165
- * When `true`, the constructor still runs the lightweight bottom-up
1166
- * post-processors (`updateParentBoundsFromChildren`) so parent bounds
1167
- * and progress rollup are consistent. The autoscheduler runs lazily on
1168
- * the first `dispatch(...)` call.
1169
- *
1170
- * Default: `false` (preserve existing behaviour). The bridge in
1171
- * `react_client/` sets this to `true` for production loads.
1172
- */
1173
624
  skipInitialAutoSchedule?: boolean;
1174
- /**
1175
- * Baseline points (backend `sectorbaselinepoint[]`, the `activityponts`
1176
- * endpoint payload) to overlay at load. Each point maps to an activity by
1177
- * `point.activityId` → `activity.proplannerId`. When present, the constructor
1178
- * overlays them so the baseline columns + `expected_progress_base` can read the
1179
- * ACTIVE point. NB: ponderators are NOT recomputed at load (backend ships the
1180
- * value prod renders verbatim — recomputing diverges); `goCalculatePonderators`
1181
- * is a save-time op. Omit (or pass `[]`) for projects with no baseline.
1182
- */
1183
625
  baselinePoints?: readonly BaselinePoint[];
1184
- /**
1185
- * Clock port for time-dependent columns (`expected_progress*`). A FUNCTION
1186
- * (not a frozen instant): the core calls it FRESH each time it needs "today"
1187
- * (load + every dispatch that recomputes expected_progress), so the value
1188
- * tracks the real day even on a long-lived session — never congelado.
1189
- *
1190
- * Opt-in (no default): omit it → the core does NOT compute `expected_progress`
1191
- * (preserves the legacy path; keeps the test suite deterministic). Production
1192
- * injects `() => new Date()`; tests inject `() => fixedDate` for reproducibility.
1193
- * The core never calls `new Date()` itself — this port is the only time source.
1194
- */
1195
626
  clock?: () => Date;
1196
- /**
1197
- * Critical-path mode at LOAD. The CP is ALWAYS computed; this flag only
1198
- * decides WHEN `core.ready` is considered settled:
1199
- * - `false` (default — front): `core.ready` resolves WITHOUT the CP (fast
1200
- * load); the CP runs async and is delivered via `core.criticalPathReady`.
1201
- * - `true` (backend, no front): `core.ready` WAITS for the CP — it is part of
1202
- * the load (blocking). The caller does a single `await core.ready` and has
1203
- * the dates + CP fields ready.
1204
- */
1205
627
  criticalPathOnLoad?: boolean;
1206
- /**
1207
- * Initial status criterion (`'Baseline' | 'Actual'`) for the `status` column.
1208
- * Not a backend field (client-only view pref); the client passes the current
1209
- * value, defaulting to `'Baseline'`. Overrides the sector default when present.
1210
- * Changed at runtime via `ScheduleCore.setStatusCriteria`.
1211
- */
1212
628
  statusCriteria?: StatusCriteria;
1213
- /**
1214
- * Optional injected port for data-quality warnings and scheduling errors.
1215
- * Defaults to a no-op when absent (the core never touches console/Sentry).
1216
- */
1217
629
  reporter?: ScheduleCoreReporter;
1218
630
  }
1219
631
 
@@ -1242,13 +654,7 @@ declare class ScheduleCore {
1242
654
  forEachActivityId(visit: (id: string) => void): void;
1243
655
  getChildrenIds(parentId: ActivityId | '0'): ReadonlyArray<ActivityId>;
1244
656
  getSelectedActivityIds(): string[];
1245
- /**
1246
- * Every activity id in canonical DFS visual order (pre-order from roots,
1247
- * siblings by `correlative_id` ASC) — ALL activities; filtering by
1248
- * `visible` is the consumer's job. Memoized in the state and invalidated
1249
- * on any structural mutation (add/remove/reparent/renumber), so repeated
1250
- * reads between mutations are O(1).
1251
- */
657
+ getHiddenActivityIds(): string[];
1252
658
  getVisualOrderIds(): ReadonlyArray<ActivityId>;
1253
659
  hasChild(parentId: ActivityId | '0'): boolean;
1254
660
  getCalendar(id: CalendarId): Readonly<Calendar> | null;
@@ -1261,36 +667,21 @@ declare class ScheduleCore {
1261
667
  private _enqueue;
1262
668
  dispatch(action: DispatchAction, options?: DispatchOptions): Promise<DispatchResult>;
1263
669
  private _dispatchInner;
1264
- /**
1265
- * Starts or joins the Critical Path calculation for the current schedule
1266
- * revision. Snapshot capture and the final commit are serialized with user
1267
- * mutations, but the expensive calculation runs outside `_opQueue` against
1268
- * an isolated state. A newer revision aborts this job and makes its result
1269
- * ineligible to commit.
1270
- */
1271
670
  recomputeCriticalPath(): Promise<{
1272
671
  changes: ChangeSet;
1273
672
  } | null>;
1274
673
  isCriticalPathSettled(): boolean;
1275
674
  whenCriticalPathSettled(): Promise<void>;
675
+ private _withReappliedFilter;
676
+ private _reapplyActiveFilter;
1276
677
  private _recordScheduleMutation;
1277
678
  private _startCriticalPathForCurrentRevision;
1278
679
  private _runCriticalPathAndCapture;
1279
- /**
1280
- * Undo/Redo restores the user's historical mutation while retaining current
1281
- * non-historical truth (for example a refreshed baseline). Re-derive every
1282
- * value that depends on both so the restored model is immediately coherent.
1283
- */
1284
680
  private _recomputeAfterHistoryRestore;
1285
681
  undo(): Promise<ChangeSet | null>;
1286
682
  redo(): Promise<ChangeSet | null>;
1287
683
  canUndo(): boolean;
1288
684
  canRedo(): boolean;
1289
- /**
1290
- * Establishes a new persistence boundary without mutating schedule state.
1291
- * Completed saves call this synchronously so neither prior undo entries nor
1292
- * their redo branch can cross the persisted boundary.
1293
- */
1294
685
  clearHistory(): void;
1295
686
  undoDepth(): number;
1296
687
  private _resyncCustomIdTrackerFromModel;
@@ -1300,13 +691,6 @@ declare class ScheduleCore {
1300
691
  private readonly _ready;
1301
692
  private readonly _saveTracker;
1302
693
  private assertReady;
1303
- /**
1304
- * Revert the current dispatch's mutations from the write-capture journal. If
1305
- * the restore ITSELF throws, the state may be a third, partially-inverted
1306
- * state — worse than either endpoint — so poison the core: refuse all further
1307
- * dispatches and surface the fault so the host reloads from backend. A failed
1308
- * rollback is never swallowed.
1309
- */
1310
694
  private _rollback;
1311
695
  }
1312
696
 
@@ -1323,25 +707,6 @@ interface ParsedInput {
1323
707
  }
1324
708
  declare function parseFromBackend(input: BackendScheduleInput, reporter?: ScheduleCoreReporter): ParsedInput;
1325
709
 
1326
- /**
1327
- * Returns the ACTIVE baseline point of an activity, or null.
1328
- *
1329
- * Ported verbatim from the legacy selector used across the schedule
1330
- * (`react_client/src/views/ganttContainer/gantt/gantt.helper.js`
1331
- * calculatePonderators / getDurationRecursively, and the baseline columns
1332
- * `startBaseCol`…`durationBaseCol`):
1333
- *
1334
- * activity.baseline_points.find(b => b.sectorbaselineversion?.active)
1335
- *
1336
- * Exactly one version per sector is active. Returns null when the activity
1337
- * has no `baseline_points`, an empty array, or no point whose version is
1338
- * active — matching the legacy behavior where such activities get
1339
- * `ponderator = 0` (baseline-only, no fallback to real values; see memory
1340
- * `project_ponderator_baseline_fix`).
1341
- *
1342
- * The `active` flag is treated as truthy (not strictly `=== true`) to match
1343
- * the legacy `if (base.sectorbaselineversion.active)` check.
1344
- */
1345
710
  declare function getActiveBaseline(activity: {
1346
711
  baselinePoints?: readonly CoreBaselinePoint[];
1347
712
  } | null | undefined): CoreBaselinePoint | null;
@@ -1349,135 +714,48 @@ declare function getActiveBaseline(activity: {
1349
714
  interface CalendarLike {
1350
715
  calculateDuration(start: Date, end: Date): number;
1351
716
  }
1352
- /**
1353
- * Expected % a baseline-planned activity should have completed by `now`,
1354
- * measured in working time against its baseline calendar. Faithful port of
1355
- * legacy `calculateExpected` (lookahead-common.js): edge rules first, else the
1356
- * working-time ratio. Returns 0..100. `now` should already be the comparison
1357
- * instant the caller wants (e.g. end-of-day).
1358
- *
1359
- * Shared by the `expected_progress` (live) and `expected_progress_base` columns
1360
- * (column-owns-everything: the formula is the one piece both columns share).
1361
- */
1362
717
  declare function expectedProgressFromBaseline(start: Date, end: Date, now: Date, calendar: CalendarLike): number;
1363
718
 
1364
719
  interface ExpectedProgressAdapter {
1365
720
  getChildrenIds(parentId: string | 0): readonly string[];
1366
721
  getActivity(id: string): Record<string, unknown> | null;
1367
722
  setActivityField(id: string, field: string, value: unknown): void;
1368
- /** Resolve a baseline calendar by its engine id (`${baseCalendarId}-base`). */
1369
723
  getBaseCalendar(engineId: string): CalendarLike | null;
1370
724
  }
1371
- /**
1372
- * Walks the tree from `rootIds`, writing `expected_progress_base` on every
1373
- * activity: leaves from their baseline calendar, parents as
1374
- * `Σ(childBase × child.ponderator) / 100` (faithful port of engine.ts
1375
- * aggregateHierarchy). Returns the ids whose value changed.
1376
- */
1377
725
  declare function computeExpectedProgress(rootIds: readonly string[], now: Date, adapter: ExpectedProgressAdapter, defaultBaseCalendarId?: string | null): string[];
1378
726
 
1379
727
  type StaticActivityDefaults = Omit<CoreActivity, 'id' | 'parentId' | 'name' | 'startDate' | 'endDate' | 'durationHours' | 'calendarId' | 'uniqueCorrelativeId'>;
1380
728
  declare const NEW_ACTIVITY_DEFAULTS: Readonly<StaticActivityDefaults>;
1381
729
 
1382
- /**
1383
- * Schedule-core equivalent of the legacy frontend `checkNoUpdatedLinks`.
1384
- *
1385
- * Returns the CURRENT links that were MODIFIED relative to a `baseline` (the
1386
- * last loaded/saved link state). A link is "modified" iff its `lag` or `type`
1387
- * differs from its baseline counterpart (matched by `id`).
1388
- *
1389
- * Why only lag/type: empirically those are the only mutable fields of a link —
1390
- * `source`/`target` are identity, and the rest are backend/derived. The core
1391
- * `link-update` intent (`dispatch/types.ts`) changes exactly "lag and/or type",
1392
- * so this comparison is complete.
1393
- *
1394
- * New links (absent from baseline) and deleted links (present only in baseline)
1395
- * are NOT "modified" — those belong to the unsaved / deleted buckets. The
1396
- * baseline is supplied by the caller (the save flow's reference snapshot); per
1397
- * interface discipline this is a pure helper, not a method on the core/port.
1398
- */
1399
730
  declare function checkNoUpdatedLinks(baseline: readonly Link[], current: readonly Link[]): Link[];
1400
731
 
1401
- /**
1402
- * Schedule-core equivalent of the legacy frontend `checkNoSavedActivities` /
1403
- * `checkNoSavedLinks`: an entity is "unsaved" when it has no backend id
1404
- * (`proplannerId`) — i.e. it was created in-session and not yet persisted.
1405
- *
1406
- * Pure helper (same `!proplannerId` criterion as legacy, so 0/null/undefined
1407
- * all count as unsaved). Works on `core.getAllActivitiesView()` / `getAllLinks()`
1408
- * or on `gantt.serialize().data` — both carry `proplannerId`. Per interface
1409
- * discipline this is a helper, not a method on the core/port.
1410
- */
1411
732
  interface WithProplannerId {
1412
733
  proplannerId?: number | null;
1413
734
  [key: string]: unknown;
1414
735
  }
1415
736
  declare const getUnsavedActivities: <T extends WithProplannerId>(activities?: readonly T[]) => T[];
1416
737
 
1417
- /**
1418
- * Returns a microtask-priority yield. Resolves on the next event-loop
1419
- * tick — the browser is free to paint, handle input, or process other
1420
- * tasks in between.
1421
- */
1422
738
  declare function yieldToBrowser(): Promise<void>;
1423
739
 
1424
- /**
1425
- * Does this dispatch recompute the critical path? The single authority for the
1426
- * question, shared by the dispatch gate (`resolveDerivedPasses`) and the
1427
- * bridge, which reads it to defer the CP out of the paint. Mirrors legacy: the
1428
- * CP re-derives on every scheduling change. Prop-only/administrative intents
1429
- * and edits that touch only non-scheduling columns leave the current CP
1430
- * revision intact; structural reparents are never exempt.
1431
- */
1432
740
  declare function willRunCriticalPath(action: DispatchAction): boolean;
1433
741
 
1434
- /** Activity types */
1435
742
  declare const ACTIVITY_TYPE: {
1436
743
  readonly TASK: "task";
1437
744
  readonly PROJECT: "project";
1438
745
  readonly MILESTONE: "milestone";
1439
746
  };
1440
747
 
1441
- /**
1442
- * Root-parent sentinel — single source of truth.
1443
- *
1444
- * Root-level activities have meant "root" under several shapes across the
1445
- * codebase and the recorded data: string `'0'` (post-parse domain model),
1446
- * numeric `0` (DHTMLX / engine seam), and nullish. `isRootParent` accepts
1447
- * all of them; `normalizeParentKey` collapses them to the canonical `'0'`.
1448
- *
1449
- * NOTE: `'0'` is overloaded elsewhere in the domain (FS link-type code,
1450
- * correlative ids). These constants are ONLY for the parent axis.
1451
- */
1452
- /** Canonical root-parent id in the post-parse domain model. */
1453
748
  declare const ROOT_PARENT_ID: "0";
1454
- /** True when `parent` denotes the root level, under any historical shape. */
1455
749
  declare function isRootParent(parent: unknown): boolean;
1456
- /** Collapse any root shape to the canonical `'0'`; stringify the rest. */
1457
750
  declare function normalizeParentKey(parent: unknown): string;
1458
751
 
1459
- /**
1460
- * Canonical bidirectional map between DHTMLX link kinds ('0'-'3') and the
1461
- * backend/display two-letter codes (fs/ss/ff/sf), plus the predecessor
1462
- * display-string vocabulary.
1463
- *
1464
- * Single source of truth — replaces the hand-maintained copies that lived in
1465
- * parse-predecessor-string (code→number), apply-link-operation
1466
- * (number→code), boundary/backend parseFromBackend (code→number) and
1467
- * inverses (number→code). The mutual `satisfies Record<…>` constraints make
1468
- * omitting or typo'ing a key on either side a compile error.
1469
- */
1470
-
1471
- /** Backend/display two-letter link codes. */
1472
752
  type LinkTypeCode = 'fs' | 'ss' | 'ff' | 'sf';
1473
- /** DHTMLX numeric kind → backend/display code. */
1474
753
  declare const LINK_TYPE_CODE: {
1475
754
  readonly "0": "fs";
1476
755
  readonly "1": "ss";
1477
756
  readonly "2": "ff";
1478
757
  readonly "3": "sf";
1479
758
  };
1480
- /** Backend/display code → DHTMLX numeric kind (inverse of LINK_TYPE_CODE). */
1481
759
  declare const LINK_CODE_TO_TYPE: {
1482
760
  readonly fs: "0";
1483
761
  readonly ss: "1";
@@ -1485,49 +763,10 @@ declare const LINK_CODE_TO_TYPE: {
1485
763
  readonly sf: "3";
1486
764
  };
1487
765
 
1488
- /**
1489
- * Inverse boundary maps (domain → backend vocabulary).
1490
- *
1491
- * The forward translations live in `constants.ts` (`CONSTRAINT_LABEL_MAP`)
1492
- * and `link-display.ts` (`LINK_CODE_TO_NUMBER`). The *serialization*
1493
- * core → backend is intentionally NOT owned by this package today — it
1494
- * lives in the consumer bridge (`react_client/gantt.helper.js`,
1495
- * `from_number_to_code` / `GanttConstraint` reverse lookup) because the
1496
- * bridge is the source of truth for what the server expects on save.
1497
- *
1498
- * These inverses exist so the mapping is documented and testable next to
1499
- * its forward counterpart, and so a future `core → backend` layer doesn't
1500
- * re-derive them by hand. **Internal only — not exported in `src/index.ts`.**
1501
- * See Obsidian `BOUNDARY_TRANSLATIONS.md` §"Mapeos inversos".
1502
- */
1503
-
1504
- /** Inverse of `CONSTRAINT_LABEL_MAP` — internal code → backend label. */
1505
766
  declare const CONSTRAINT_TYPE_TO_LABEL: Record<ConstraintType, string>;
1506
767
 
1507
- /**
1508
- * Working-day vocabulary — genuine cross-component primitive (creation
1509
- * defaults, internal/state context fallbacks, link lag display). The value
1510
- * is the FALLBACK only; a sector- or calendar-provided hoursPerDay always
1511
- * takes precedence at every consumer.
1512
- */
1513
768
  declare const DEFAULT_HOURS_PER_DAY = 8;
1514
769
 
1515
- /**
1516
- * Canonical catalog of `DispatchAction` kinds.
1517
- *
1518
- * The discriminated union in `types.ts` remains the source of truth; this
1519
- * module exposes it as RUNTIME vocabulary so `.js` consumers (bridge
1520
- * entry-points, guard tests) stop retyping the wire strings. Exhaustiveness
1521
- * is compile-checked HERE (not in the guard test — `tsconfig.json` excludes
1522
- * `*.test.ts` from `tsc --noEmit` and vitest does not typecheck):
1523
- * - `satisfies Record<string, DispatchActionKind>` rejects any entry whose
1524
- * value is not a union member (catalog ⊆ union);
1525
- * - `KIND_CATALOG_COVERS_UNION: Record<DispatchActionKind, true>` requires
1526
- * one entry per union member (union ⊆ catalog) — adding a kind to
1527
- * `DispatchAction` without updating this module breaks the build.
1528
- * The string VALUES are a frozen contract (see `constants.guard.test.ts`).
1529
- */
1530
-
1531
770
  type DispatchActionKind = DispatchAction['kind'];
1532
771
  declare const DISPATCH_ACTION_KIND: {
1533
772
  readonly PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge";
@@ -1551,28 +790,12 @@ declare const DISPATCH_ACTION_KIND: {
1551
790
  readonly SELECTION_TOGGLE: "selection-toggle";
1552
791
  readonly SELECTION_REPLACE: "selection-replace";
1553
792
  readonly VISIBILITY_SET: "visibility-set";
793
+ readonly FILTER_SET: "filter-set";
1554
794
  readonly SIR_SYNC: "sir-sync";
1555
795
  readonly ACTIVITY_LOOKAHEAD_SYNC: "activity-lookahead-sync";
1556
796
  };
1557
797
  declare const DISPATCH_ACTION_KINDS: ReadonlyArray<DispatchActionKind>;
1558
798
 
1559
- /**
1560
- * Canonical vocabulary of activity-creation triggers.
1561
- *
1562
- * The engine's `activity-create` intent does NOT carry this field — these
1563
- * kinds name the USER trigger the consumer's creation hub routes on before
1564
- * dispatching (`requestActivityCreation` / `CreateActivityIntent` in
1565
- * `react_client` core path). The engine owns the vocabulary so the client
1566
- * and the bridge import ONE canonical source instead of retyping the
1567
- * literals (CONSTANTES_DECISIONES paso 1). The string VALUES are a frozen
1568
- * contract — verified verbatim against the client union
1569
- * (`core/features/createActivity/intents.ts`) on 2026-07-02.
1570
- *
1571
- * - `CHILD` — per-row "+" button (runs the lookahead guard).
1572
- * - `CHILD_CONFIRMED` — lookahead-cleanup modal confirm (guard already passed).
1573
- * - `LINE` — green bar "add-task-by-line".
1574
- * - `ROOT` — generic "+" (header / create), no guard.
1575
- */
1576
799
  declare const CREATION_KIND: {
1577
800
  readonly CHILD: "child";
1578
801
  readonly CHILD_CONFIRMED: "child-confirmed";
@@ -1581,16 +804,6 @@ declare const CREATION_KIND: {
1581
804
  };
1582
805
  type CreationKind = (typeof CREATION_KIND)[keyof typeof CREATION_KIND];
1583
806
 
1584
- /**
1585
- * Canonical catalog of dispatch rejection reasons.
1586
- *
1587
- * `DispatchResult.reason` stays `string` on purpose: it is the cross-domain
1588
- * bag that also carries pipeline gate codes (`ParseErrorCode`,
1589
- * `ValidationReason`, `EditDenialReason`) and the link-engine rejected
1590
- * verdicts. This module covers the reasons DISPATCH ITSELF produces, so
1591
- * producers and tests share one compile-checked source. The string VALUES
1592
- * are a frozen contract — the bridge matches on them.
1593
- */
1594
807
  declare const REJECTION_REASON: {
1595
808
  readonly CANNOT_EDIT: "cannot_edit";
1596
809
  readonly PARSE_ERROR: "parse_error";
@@ -1614,11 +827,6 @@ declare const REJECTION_REASON: {
1614
827
  };
1615
828
  type DispatchRejectReason = (typeof REJECTION_REASON)[keyof typeof REJECTION_REASON];
1616
829
 
1617
- /**
1618
- * Amplitude event names emitted by the dispatch layer (structure mutations).
1619
- * Analytics CONTRACT — values must stay byte-identical to production.
1620
- * Column-level entry events live in `columns/shared/trackingEvents.ts`.
1621
- */
1622
830
  declare const DISPATCH_TRACK_EVENT: {
1623
831
  readonly ACTIVITY_CREATION: "schedule_activity_creation";
1624
832
  readonly ACTIVITY_DELETION: "schedule_activity_deletion";
@@ -1628,4 +836,4 @@ declare const DISPATCH_TRACK_EVENT: {
1628
836
  };
1629
837
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
1630
838
 
1631
- export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
839
+ export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };