@critical-path/core 0.15.2 → 0.17.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +25 -0
  3. package/dist/domain/cpm.d.ts +10 -0
  4. package/dist/domain/cpm.d.ts.map +1 -0
  5. package/dist/domain/cpm.js +176 -0
  6. package/dist/domain/cpm.js.map +1 -0
  7. package/dist/domain/cpm.test.d.ts +2 -0
  8. package/dist/domain/cpm.test.d.ts.map +1 -0
  9. package/dist/domain/cpm.test.js +113 -0
  10. package/dist/domain/cpm.test.js.map +1 -0
  11. package/dist/domain/entities.d.ts.map +1 -1
  12. package/dist/domain/entities.js +5 -0
  13. package/dist/domain/entities.js.map +1 -1
  14. package/dist/domain/index.d.ts +3 -0
  15. package/dist/domain/index.d.ts.map +1 -1
  16. package/dist/domain/index.js +3 -0
  17. package/dist/domain/index.js.map +1 -1
  18. package/dist/domain/ladder.d.ts +16 -0
  19. package/dist/domain/ladder.d.ts.map +1 -0
  20. package/dist/domain/ladder.js +352 -0
  21. package/dist/domain/ladder.js.map +1 -0
  22. package/dist/domain/ladder.test.d.ts +2 -0
  23. package/dist/domain/ladder.test.d.ts.map +1 -0
  24. package/dist/domain/ladder.test.js +213 -0
  25. package/dist/domain/ladder.test.js.map +1 -0
  26. package/dist/domain/metrics.d.ts +29 -0
  27. package/dist/domain/metrics.d.ts.map +1 -0
  28. package/dist/domain/metrics.js +403 -0
  29. package/dist/domain/metrics.js.map +1 -0
  30. package/dist/domain/metrics.test.d.ts +2 -0
  31. package/dist/domain/metrics.test.d.ts.map +1 -0
  32. package/dist/domain/metrics.test.js +251 -0
  33. package/dist/domain/metrics.test.js.map +1 -0
  34. package/dist/engine/index.d.ts +8 -1
  35. package/dist/engine/index.d.ts.map +1 -1
  36. package/dist/engine/index.js +124 -1
  37. package/dist/engine/index.js.map +1 -1
  38. package/dist/store/firebase.d.ts +3 -1
  39. package/dist/store/firebase.d.ts.map +1 -1
  40. package/dist/store/firebase.js +1 -1
  41. package/dist/store/firebase.js.map +1 -1
  42. package/dist/store/index.d.ts +6 -2
  43. package/dist/store/index.d.ts.map +1 -1
  44. package/dist/store/index.js +1 -1
  45. package/dist/store/index.js.map +1 -1
  46. package/dist/store/sqlite.d.ts +3 -1
  47. package/dist/store/sqlite.d.ts.map +1 -1
  48. package/dist/store/sqlite.js +1 -1
  49. package/dist/store/sqlite.js.map +1 -1
  50. package/dist/types/index.d.ts +177 -0
  51. package/dist/types/index.d.ts.map +1 -1
  52. package/package.json +1 -1
  53. package/src/domain/cpm.test.ts +128 -0
  54. package/src/domain/cpm.ts +215 -0
  55. package/src/domain/entities.ts +6 -0
  56. package/src/domain/index.ts +4 -0
  57. package/src/domain/ladder.test.ts +251 -0
  58. package/src/domain/ladder.ts +453 -0
  59. package/src/domain/metrics.test.ts +286 -0
  60. package/src/domain/metrics.ts +484 -0
  61. package/src/engine/index.ts +169 -4
  62. package/src/store/firebase.ts +2 -2
  63. package/src/store/index.ts +3 -3
  64. package/src/store/sqlite.ts +2 -2
  65. package/src/types/index.ts +206 -0
  66. package/tsconfig.tsbuildinfo +1 -1
@@ -536,9 +536,9 @@ export class FirebaseStore implements StorageAdapter {
536
536
  return snap.docs.map((doc) => ({ ...doc.data(), id: doc.id }));
537
537
  }
538
538
 
539
- async logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'>): Promise<TimeEntry> {
539
+ async logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'> & { loggedAt?: string }): Promise<TimeEntry> {
540
540
  const docRef = this.db.collection('time_entries').doc();
541
- const now = new Date().toISOString();
541
+ const now = entry.loggedAt || new Date().toISOString();
542
542
  const newEntry: TimeEntry = { ...entry, id: docRef.id, loggedAt: now };
543
543
  await docRef.set(sanitizeFirestoreData(newEntry));
544
544
  return newEntry;
@@ -91,7 +91,7 @@ export interface ActivityRepository {
91
91
 
92
92
  export interface TimeEntryRepository {
93
93
  getTimeEntries(taskId: string): Promise<TimeEntry[]>;
94
- logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'>): Promise<TimeEntry>;
94
+ logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'> & { loggedAt?: string }): Promise<TimeEntry>;
95
95
  }
96
96
 
97
97
  export interface DependencyRepository {
@@ -512,9 +512,9 @@ export class InMemoryStore implements StorageAdapter {
512
512
  return Array.from(this.timeEntries.values()).filter((e) => e.taskId === taskId);
513
513
  }
514
514
 
515
- async logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'>): Promise<TimeEntry> {
515
+ async logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'> & { loggedAt?: string }): Promise<TimeEntry> {
516
516
  const id = `time_${Math.random().toString(36).substring(2, 9)}`;
517
- const newEntry: TimeEntry = { ...entry, id, loggedAt: new Date().toISOString() };
517
+ const newEntry: TimeEntry = { ...entry, id, loggedAt: entry.loggedAt || new Date().toISOString() };
518
518
  this.timeEntries.set(id, newEntry);
519
519
  return newEntry;
520
520
  }
@@ -1084,9 +1084,9 @@ export class SQLiteStore implements StorageAdapter {
1084
1084
  return rows.map((r) => ({ ...r, isBillable: r.isBillable !== null ? Boolean(r.isBillable) : undefined }));
1085
1085
  }
1086
1086
 
1087
- async logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'>): Promise<TimeEntry> {
1087
+ async logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'> & { loggedAt?: string }): Promise<TimeEntry> {
1088
1088
  const id = `time_${Math.random().toString(36).substring(2, 9)}`;
1089
- const now = new Date().toISOString();
1089
+ const now = entry.loggedAt || new Date().toISOString();
1090
1090
  const newEntry: TimeEntry = { ...entry, id, loggedAt: now };
1091
1091
 
1092
1092
  const stmt = this.db.prepare(`
@@ -424,3 +424,209 @@ export interface CriticalPathConfig {
424
424
  deliverables?: Deliverable[];
425
425
  };
426
426
  }
427
+
428
+ // ==========================================
429
+ // Ladder of Abstraction Types
430
+ // ==========================================
431
+
432
+ export type AbstractionLevel = 'macro' | 'standard' | 'concrete' | 'all';
433
+
434
+ export type MacroPhaseHealth = 'on_track' | 'at_risk' | 'blocked' | 'overdue' | 'completed';
435
+
436
+ export interface MacroPhaseRollup {
437
+ id: string; // containerId, iterationId, deliverableId, or 'unassigned'
438
+ type: 'container' | 'iteration' | 'deliverable' | 'phase';
439
+ name: string;
440
+ description?: string;
441
+ startDate?: string;
442
+ endDate?: string;
443
+ durationHours: number;
444
+ totalTasks: number;
445
+ completedTasks: number;
446
+ inProgressTasks: number;
447
+ blockedTasks: number;
448
+ progressPercentage: number;
449
+ totalEstimatedHours: number;
450
+ totalLoggedHours: number;
451
+ isCritical: boolean;
452
+ criticalTaskCount: number;
453
+ health: MacroPhaseHealth;
454
+ taskIds: string[];
455
+ }
456
+
457
+ export interface MacroTimelineSummary {
458
+ projectId: string;
459
+ projectName: string;
460
+ overallStartDate?: string;
461
+ overallEndDate?: string;
462
+ projectedFinishDate?: string;
463
+ totalDurationHours: number;
464
+ criticalPathDurationHours: number;
465
+ overallProgressPercentage: number;
466
+ health: MacroPhaseHealth;
467
+ totalTasks: number;
468
+ completedTasks: number;
469
+ inProgressTasks: number;
470
+ blockedTasks: number;
471
+ totalEstimatedHours: number;
472
+ totalLoggedHours: number;
473
+ phases: MacroPhaseRollup[];
474
+ }
475
+
476
+ export interface TaskCriticalPathSchedule {
477
+ taskId: string;
478
+ earlyStart: number;
479
+ earlyFinish: number;
480
+ lateStart: number;
481
+ lateFinish: number;
482
+ totalSlack: number;
483
+ isCritical: boolean;
484
+ }
485
+
486
+ export interface CriticalPathAnalysis {
487
+ projectId: string;
488
+ calculatedAt: string;
489
+ totalDurationHours: number;
490
+ criticalTaskIds: string[];
491
+ tasks: TaskCriticalPathSchedule[];
492
+ }
493
+
494
+ export interface ConcreteEvidenceSummary {
495
+ attachmentCount: number;
496
+ deliverableCount: number;
497
+ todoCount: number;
498
+ completedTodoCount: number;
499
+ timeEntryCount: number;
500
+ totalLoggedHours: number;
501
+ hasVisualAsset: boolean;
502
+ lastActivityAt?: string;
503
+ }
504
+
505
+ export interface StandardTaskTimelineItem extends Task {
506
+ cpm?: TaskCriticalPathSchedule;
507
+ blockingTaskIds: string[];
508
+ dependentTaskIds: string[];
509
+ childTaskIds: string[];
510
+ concreteEvidenceSummary: ConcreteEvidenceSummary;
511
+ }
512
+
513
+ export interface RealityDelta {
514
+ plannedStartDate?: string;
515
+ actualStartDate?: string;
516
+ dueDate?: string;
517
+ actualEndDate?: string;
518
+ estimatedHours: number;
519
+ loggedHours: number;
520
+ varianceHours: number; // loggedHours - estimatedHours
521
+ effortVarianceHours?: number; // loggedHours - estimatedHours (effort variance)
522
+ durationVarianceHours?: number; // actual duration hours - planned duration hours
523
+ scheduleVarianceDays?: number; // days difference between planned/due vs actual
524
+ accuracyRatio?: number; // loggedHours / estimatedHours (estimation accuracy ratio)
525
+ isOverdue: boolean;
526
+ isOverEstimate: boolean;
527
+ }
528
+
529
+ export type ProgressInferenceSource = 'explicit' | 'todos' | 'time_effort' | 'schedule_elapsed' | 'blended';
530
+
531
+ export interface TaskProgressInference {
532
+ progressPercentage: number;
533
+ source: ProgressInferenceSource;
534
+ isExplicit: boolean;
535
+ breakdown: {
536
+ explicitProgress?: number;
537
+ todoProgress?: number;
538
+ effortProgress?: number;
539
+ scheduleProgress?: number;
540
+ };
541
+ }
542
+
543
+ export type ProgressCurveProfile =
544
+ | 'linear'
545
+ | 's_curve'
546
+ | 'early_surge'
547
+ | 'late_rush'
548
+ | 'stalled'
549
+ | 'insufficient_data';
550
+
551
+ export interface TaskProgressHistoryPoint {
552
+ timestamp: string;
553
+ progress: number;
554
+ inferredProgress?: number;
555
+ status: TaskStatus;
556
+ semanticStatus?: SemanticStatus;
557
+ action: string;
558
+ }
559
+
560
+ export interface TaskProgressHistory {
561
+ taskId: string;
562
+ points: TaskProgressHistoryPoint[];
563
+ curveProfile: ProgressCurveProfile;
564
+ }
565
+
566
+ export interface TaskEVM {
567
+ plannedValue: number; // PV (expected hours)
568
+ earnedValue: number; // EV (earned hours)
569
+ actualCost: number; // AC (logged hours)
570
+ costVariance: number; // CV = EV - AC
571
+ scheduleVariance: number; // SV = EV - PV
572
+ costPerformanceIndex: number; // CPI = EV / AC
573
+ schedulePerformanceIndex: number; // SPI = EV / PV
574
+ }
575
+
576
+ export interface TaskInferredActuals {
577
+ actualStartDate?: string;
578
+ actualEndDate?: string;
579
+ isStartDateInferred: boolean;
580
+ isEndDateInferred: boolean;
581
+ activeWorkingHours?: number;
582
+ }
583
+
584
+ export interface TaskMetrics {
585
+ taskId: string;
586
+ inferredActuals: TaskInferredActuals;
587
+ realityDelta: RealityDelta;
588
+ progress: TaskProgressInference;
589
+ evm: TaskEVM;
590
+ progressHistory: TaskProgressHistory;
591
+ }
592
+
593
+ export interface ConcreteTaskEvidence {
594
+ taskId: string;
595
+ attachments: Attachment[];
596
+ deliverables: Deliverable[];
597
+ todos: TaskTodoItem[];
598
+ timeEntries: TimeEntry[];
599
+ dailyEffortDistribution: Array<{ date: string; hours: number }>;
600
+ activities: Activity[];
601
+ realityDelta: RealityDelta;
602
+ evidenceSummary: ConcreteEvidenceSummary;
603
+ }
604
+
605
+ export interface TimelineLadderOptions {
606
+ level?: AbstractionLevel;
607
+ containerId?: string;
608
+ iterationId?: string;
609
+ }
610
+
611
+ export interface TimelineLadder {
612
+ projectId: string;
613
+ generatedAt: string;
614
+ level: AbstractionLevel;
615
+ macro?: MacroTimelineSummary;
616
+ standard?: {
617
+ tasks: StandardTaskTimelineItem[];
618
+ criticalPathTaskIds: string[];
619
+ dependencies: TaskDependency[];
620
+ totalDurationHours: number;
621
+ };
622
+ concrete?: Record<string, ConcreteTaskEvidence>;
623
+ }
624
+
625
+ export interface TaskLadderView {
626
+ taskId: string;
627
+ macroPhase?: MacroPhaseRollup;
628
+ standard: StandardTaskTimelineItem;
629
+ concrete: ConcreteTaskEvidence;
630
+ metrics?: TaskMetrics;
631
+ }
632
+
@@ -1 +1 @@
1
- {"root":["./src/deliverables.test.ts","./src/index.test.ts","./src/index.ts","./src/status.test.ts","./src/task-state.test.ts","./src/use-case-validation.test.ts","./src/domain/custom-fields.ts","./src/domain/ddd.test.ts","./src/domain/entities.ts","./src/domain/events.ts","./src/domain/graph.ts","./src/domain/index.ts","./src/engine/index.ts","./src/plugins/index.ts","./src/storage/file-storage.ts","./src/storage/firebase-storage.ts","./src/storage/index.ts","./src/storage/s3.ts","./src/storage/storage.test.ts","./src/store/firebase.test.ts","./src/store/firebase.ts","./src/store/index.ts","./src/store/sqlite.test.ts","./src/store/sqlite.ts","./src/types/index.ts","./src/utils/index.ts","./src/utils/key.test.ts","./src/utils/key.ts","./src/utils/mentions.test.ts","./src/utils/mentions.ts","./src/utils/status.ts","./src/utils/workflow.ts"],"version":"5.9.3"}
1
+ {"root":["./src/deliverables.test.ts","./src/index.test.ts","./src/index.ts","./src/status.test.ts","./src/task-state.test.ts","./src/use-case-validation.test.ts","./src/domain/cpm.test.ts","./src/domain/cpm.ts","./src/domain/custom-fields.ts","./src/domain/ddd.test.ts","./src/domain/entities.ts","./src/domain/events.ts","./src/domain/graph.ts","./src/domain/index.ts","./src/domain/ladder.test.ts","./src/domain/ladder.ts","./src/domain/metrics.test.ts","./src/domain/metrics.ts","./src/engine/index.ts","./src/plugins/index.ts","./src/storage/file-storage.ts","./src/storage/firebase-storage.ts","./src/storage/index.ts","./src/storage/s3.ts","./src/storage/storage.test.ts","./src/store/firebase.test.ts","./src/store/firebase.ts","./src/store/index.ts","./src/store/sqlite.test.ts","./src/store/sqlite.ts","./src/types/index.ts","./src/utils/index.ts","./src/utils/key.test.ts","./src/utils/key.ts","./src/utils/mentions.test.ts","./src/utils/mentions.ts","./src/utils/status.ts","./src/utils/workflow.ts"],"version":"5.9.3"}