Volume 3: Human-System Collaboration

Pattern 20: Scheduled Actions

Part II: Interaction Patterns - Temporal Patterns


Opening Scenario: The Renewal That Almost Expired

Maria managed software licenses for a 500-person company. Their annual Microsoft 365 license was up for renewal on January 15th.

On January 14th at 4:00 PM, she got an email from the vendor:

Subject: URGENT: License expires tomorrow

Your Microsoft 365 license for 500 users expires 
tomorrow, January 15th. If not renewed by midnight, 
all users will lose access.

Renewal cost: $87,500

Maria panicked. The finance department had already left for the day. She needed three approvals: her manager, the CFO, and the CEO. Each approval typically took 24 hours.

She had 8 hours to get 72 hours of approvals.

She called her manager at 5 PM. "Why didn't we handle this earlier?"

Maria checked the system:

License Renewal Form

License: Microsoft 365 (500 users)
Cost: $87,500
Renewal Date: 01/15/2025

[Submit for Approval]

// No reminder system
// No advance warning
// No automated scheduling

The system didn't remind anyone. It didn't schedule the renewal process in advance. It just waited for someone to remember.

Maria worked until midnight getting emergency approvals. The license renewed at 11:47 PM. Crisis averted, but barely.


The IT director, Thomas, investigated. He found the system had no scheduling capabilities:

// Bad: No scheduling or reminders
class LicenseManagement {
  createRenewal(license, renewalDate) {
    database.insert({
      license: license,
      renewalDate: renewalDate
    });

    // That's it. No follow-up.
    // No reminders.
    // No automation.
  }
}

// Result: Everything depends on humans remembering

Thomas found more scheduling failures:

Employee review deadlines missed: - 30% of annual reviews completed late - No reminders to managers - No escalation when overdue

Contract expirations discovered too late: - Vendor contracts auto-renewed at unfavorable rates - No 30-day advance warning - No time to renegotiate

Recurring reports forgotten: - Monthly compliance reports required by board - No automatic scheduling - Ad-hoc panic each month

Time-sensitive approvals delayed: - Purchase orders sitting in queue - Approvers didn't know they were urgent - Projects delayed waiting for approvals


Thomas built a comprehensive scheduling system:

class ScheduledActionsSystem {
  scheduleAction(action, config) {
    const scheduledAction = {
      id: this.generateId(),
      actionType: action.type,
      targetDocument: action.documentId,

      // Scheduling
      executeAt: config.executeAt,
      recurrence: config.recurrence || null,

      // Reminders
      reminders: config.reminders || [],
      escalation: config.escalation || null,

      // Action details
      action: action.action,
      parameters: action.parameters,

      // Status
      status: 'scheduled',
      lastRun: null,
      nextRun: config.executeAt,

      // Metadata
      createdBy: config.userId,
      createdAt: new Date()
    };

    this.scheduler.add(scheduledAction);

    return scheduledAction;
  }
}

Now when Maria created a license renewal:

License Renewal - Microsoft 365

Renewal Date: January 15, 2025

[✓] Schedule Reminders:
  ☑ 60 days before (Nov 16) - Notify procurement
  ☑ 30 days before (Dec 16) - Start approval process
  ☑ 14 days before (Jan 1)  - Escalate if not approved
  ☑ 7 days before (Jan 8)   - Emergency escalation

[✓] Auto-submit approval workflow on Dec 16

[✓] Escalate to CFO if not approved by Jan 1

[✓] Send daily reminders starting Jan 8

[Save Schedule]

On November 16th (60 days before renewal):

📧 Reminder: Microsoft 365 License Renewal

License renews on January 15, 2025 (60 days)
Cost: $87,500

Action needed: Begin budget planning
Next reminder: December 16 (30 days before)

On December 16th (30 days before renewal):

🔔 AUTO-SUBMITTED: License Renewal Approval

The system has automatically initiated the approval 
workflow for Microsoft 365 license renewal.

Current status: Pending Manager Approval
Assigned to: Karen Wilson

Deadline: January 15, 2025 (30 days)

On January 1st (14 days before, not yet approved):

⚠️ ESCALATION: License Renewal Urgent

Microsoft 365 license renewal still pending approval.
Renewal date: January 15 (14 days away)

Escalating to: CFO (Sarah Chen)
Daily reminders will begin on January 8 if not resolved.

Zero last-minute emergencies. Everything scheduled. Everyone reminded. Complete planning.

Context

Scheduled Actions applies when:

Future planning required: Actions need to happen at specific future times

Recurring tasks exist: Monthly reports, annual reviews, quarterly audits

Deadlines are critical: Missing deadline has serious consequences

Reminders needed: People forget, systems shouldn't

Multi-step processes: Approvals, reviews, escalations over time

Automation desired: Reduce manual intervention

Coordination needed: Multiple people, multiple timelines

Compliance mandates: Regular required actions (audits, certifications)

Problem Statement

Most systems lack scheduling capabilities, forcing users to remember everything:

No future actions:

// Bad: Can only act on present
function submitForm(data) {
  processImmediately(data);
}

// What if user wants to:
// - Submit next Monday
// - Schedule for end of quarter
// - Auto-submit on deadline
// Not possible!

No reminders:

// Bad: Set deadline, hope someone remembers
database.update(task, {
  deadline: '2025-03-15'
});

// No notification system
// No advance warning
// User must remember to check

No recurrence:

// Bad: Manual creation each time
function createMonthlyReport() {
  // User must:
  // 1. Remember it's time
  // 2. Create report manually
  // 3. Submit manually
  // Every single month
}

// Should be: Schedule once, runs automatically

No escalation:

// Bad: Task sits forever if ignored
{
  assignedTo: 'manager@company.com',
  dueDate: '2024-12-15'
}

// Manager ignores it
// No escalation to their manager
// No alternative assignee
// Task dies

No conditional scheduling:

// Bad: Fixed schedule only
schedule.every('monday').run(task);

// What if:
// - Skip holidays
// - Only on business days
// - Only if previous task completed
// Not supported!

No time zone handling:

// Bad: Assumes one time zone
schedule.at('9:00 AM').run(task);

// 9 AM in which time zone?
// User in Tokyo gets 2 AM notification
// Terrible UX

We need comprehensive scheduling that handles future actions, reminders, recurrence, escalation, and intelligent timing.

Forces

Automation vs Control

  • Automation reduces manual work
  • But users want control
  • Balance convenience with oversight

Precision vs Flexibility

  • Exact scheduling is predictable
  • But needs flexibility for exceptions
  • Balance reliability with adaptability

Reminders vs Noise

  • Reminders prevent forgetting
  • But too many annoy users
  • Balance helpfulness with intrusiveness

Simplicity vs Power

  • Simple scheduling is easy
  • But complex needs require features
  • Balance usability with capability

Local vs Global

  • User's local time is intuitive
  • But system time is consistent
  • Balance user experience with reliability

Solution

Implement comprehensive scheduling system that supports future-dated actions, intelligent reminders with escalation, recurring tasks with flexible patterns, time zone awareness, and conditional execution based on business rules.

The pattern has seven key strategies:

1. Future Action Scheduling

Schedule actions to execute at specific future times:

class FutureActionScheduler {
  constructor() {
    this.scheduledActions = new Map();
    this.executor = new ActionExecutor();
  }

  scheduleAction(action, executeAt, options = {}) {
    const scheduled = {
      id: this.generateId(),
      action: action,
      executeAt: new Date(executeAt),

      // Options
      timezone: options.timezone || 'UTC',
      condition: options.condition || null,
      onSuccess: options.onSuccess || null,
      onFailure: options.onFailure || null,
      maxRetries: options.maxRetries || 3,
      retryDelay: options.retryDelay || 60000, // 1 minute

      // Status
      status: 'scheduled',
      attempts: 0,
      lastAttempt: null,
      error: null
    };

    this.scheduledActions.set(scheduled.id, scheduled);
    this.scheduleExecution(scheduled);

    return scheduled;
  }

  scheduleExecution(scheduled) {
    const now = new Date();
    const delay = scheduled.executeAt - now;

    if (delay <= 0) {
      // Execute immediately
      this.executeAction(scheduled);
    } else {
      // Schedule for future
      setTimeout(() => {
        this.executeAction(scheduled);
      }, delay);
    }
  }

  async executeAction(scheduled) {
    // Check condition
    if (scheduled.condition) {
      const conditionMet = await this.evaluateCondition(scheduled.condition);
      if (!conditionMet) {
        scheduled.status = 'condition-not-met';
        return;
      }
    }

    // Execute
    scheduled.status = 'executing';
    scheduled.lastAttempt = new Date();
    scheduled.attempts++;

    try {
      const result = await this.executor.execute(scheduled.action);

      scheduled.status = 'completed';
      scheduled.result = result;

      // Success callback
      if (scheduled.onSuccess) {
        await scheduled.onSuccess(result);
      }

    } catch (error) {
      scheduled.error = error.message;

      // Retry?
      if (scheduled.attempts < scheduled.maxRetries) {
        scheduled.status = 'retrying';

        setTimeout(() => {
          this.executeAction(scheduled);
        }, scheduled.retryDelay);

      } else {
        scheduled.status = 'failed';

        // Failure callback
        if (scheduled.onFailure) {
          await scheduled.onFailure(error);
        }
      }
    }
  }

  cancelScheduledAction(actionId) {
    const scheduled = this.scheduledActions.get(actionId);
    if (scheduled && scheduled.status === 'scheduled') {
      scheduled.status = 'cancelled';
      return true;
    }
    return false;
  }

  rescheduleAction(actionId, newExecuteAt) {
    const scheduled = this.scheduledActions.get(actionId);
    if (scheduled) {
      scheduled.executeAt = new Date(newExecuteAt);
      scheduled.status = 'scheduled';
      this.scheduleExecution(scheduled);
      return true;
    }
    return false;
  }
}

2. Intelligent Reminder System

Send timely reminders with escalation:

class ReminderSystem {
  constructor(scheduler) {
    this.scheduler = scheduler;
    this.reminders = new Map();
  }

  createReminderSequence(target, deadline, config) {
    const sequence = {
      id: this.generateId(),
      target: target, // Document, task, etc.
      deadline: new Date(deadline),
      reminders: [],
      escalation: config.escalation || null,

      status: 'active',
      sentReminders: []
    };

    // Schedule reminders at intervals before deadline
    config.intervals.forEach(interval => {
      const reminderTime = new Date(deadline);
      reminderTime.setTime(reminderTime.getTime() - interval);

      const reminder = {
        id: this.generateId(),
        sequenceId: sequence.id,
        time: reminderTime,
        interval: interval,
        recipients: config.recipients,
        template: this.getTemplate(interval, deadline),
        severity: this.getSeverity(interval, deadline),
        sent: false
      };

      sequence.reminders.push(reminder);

      // Schedule the reminder
      this.scheduler.scheduleAction({
        type: 'send-reminder',
        reminder: reminder
      }, reminderTime);
    });

    // Schedule escalation if provided
    if (config.escalation) {
      this.scheduleEscalation(sequence, deadline, config.escalation);
    }

    this.reminders.set(sequence.id, sequence);
    return sequence;
  }

  getSeverity(intervalBeforeDeadline, deadline) {
    const days = intervalBeforeDeadline / (1000 * 60 * 60 * 24);

    if (days <= 1) return 'urgent';
    if (days <= 7) return 'high';
    if (days <= 14) return 'medium';
    return 'low';
  }

  getTemplate(interval, deadline) {
    const days = Math.floor(interval / (1000 * 60 * 60 * 24));

    if (days === 0) {
      return {
        subject: '🚨 URGENT: Deadline is TODAY',
        urgency: 'critical'
      };
    } else if (days === 1) {
      return {
        subject: '⚠️ Reminder: Deadline Tomorrow',
        urgency: 'high'
      };
    } else if (days <= 7) {
      return {
        subject: `📅 Reminder: Deadline in ${days} days`,
        urgency: 'medium'
      };
    } else {
      return {
        subject: `📆 Upcoming: Deadline in ${days} days`,
        urgency: 'low'
      };
    }
  }

  scheduleEscalation(sequence, deadline, escalationConfig) {
    const checkTime = new Date(deadline);
    checkTime.setTime(checkTime.getTime() + escalationConfig.afterDeadline);

    this.scheduler.scheduleAction({
      type: 'check-escalation',
      sequence: sequence.id,
      escalateTo: escalationConfig.escalateTo,
      condition: {
        type: 'not-completed',
        target: sequence.target
      }
    }, checkTime);
  }

  // Example: License renewal reminders
  scheduleLicenseReminders(license, renewalDate) {
    return this.createReminderSequence(
      license,
      renewalDate,
      {
        intervals: [
          60 * 24 * 60 * 60 * 1000,  // 60 days
          30 * 24 * 60 * 60 * 1000,  // 30 days
          14 * 24 * 60 * 60 * 1000,  // 14 days
          7 * 24 * 60 * 60 * 1000,   // 7 days
          1 * 24 * 60 * 60 * 1000    // 1 day
        ],
        recipients: ['procurement@company.com', 'maria@company.com'],
        escalation: {
          afterDeadline: 24 * 60 * 60 * 1000, // 1 day after
          escalateTo: ['cfo@company.com']
        }
      }
    );
  }
}

3. Recurring Task Scheduler

Handle recurring events with complex patterns:

class RecurringScheduler {
  constructor() {
    this.recurrences = new Map();
  }

  scheduleRecurring(action, pattern, options = {}) {
    const recurrence = {
      id: this.generateId(),
      action: action,
      pattern: pattern,

      // Options
      startDate: options.startDate || new Date(),
      endDate: options.endDate || null,
      timezone: options.timezone || 'UTC',
      skipConditions: options.skipConditions || [],

      // Status
      status: 'active',
      executions: [],
      nextExecution: null,
      lastExecution: null
    };

    // Calculate next execution
    recurrence.nextExecution = this.calculateNextExecution(recurrence);

    this.recurrences.set(recurrence.id, recurrence);
    this.scheduleNext(recurrence);

    return recurrence;
  }

  calculateNextExecution(recurrence) {
    const pattern = recurrence.pattern;
    let next = new Date(recurrence.lastExecution || recurrence.startDate);

    switch(pattern.type) {
      case 'daily':
        next.setDate(next.getDate() + pattern.interval);
        break;

      case 'weekly':
        next.setDate(next.getDate() + (7 * pattern.interval));
        break;

      case 'monthly':
        next.setMonth(next.getMonth() + pattern.interval);
        break;

      case 'yearly':
        next.setFullYear(next.getFullYear() + pattern.interval);
        break;

      case 'custom':
        next = pattern.calculateNext(next);
        break;
    }

    // Apply skip conditions
    while (this.shouldSkip(next, recurrence.skipConditions)) {
      next = this.calculateNextExecution({
        ...recurrence,
        lastExecution: next
      });
    }

    // Check end date
    if (recurrence.endDate && next > recurrence.endDate) {
      return null;
    }

    return next;
  }

  shouldSkip(date, skipConditions) {
    return skipConditions.some(condition => {
      switch(condition.type) {
        case 'weekend':
          const day = date.getDay();
          return day === 0 || day === 6;

        case 'holiday':
          return this.isHoliday(date);

        case 'specific-dates':
          return condition.dates.some(d => 
            this.isSameDay(date, new Date(d))
          );

        case 'custom':
          return condition.check(date);

        default:
          return false;
      }
    });
  }

  scheduleNext(recurrence) {
    if (recurrence.nextExecution) {
      this.scheduler.scheduleAction({
        type: 'execute-recurring',
        recurrenceId: recurrence.id,
        action: recurrence.action
      }, recurrence.nextExecution, {
        onSuccess: () => {
          this.recordExecution(recurrence, true);
          this.scheduleNext(recurrence);
        },
        onFailure: (error) => {
          this.recordExecution(recurrence, false, error);
        }
      });
    } else {
      recurrence.status = 'completed';
    }
  }

  recordExecution(recurrence, success, error = null) {
    recurrence.executions.push({
      timestamp: new Date(),
      success: success,
      error: error
    });

    recurrence.lastExecution = new Date();
    recurrence.nextExecution = this.calculateNextExecution(recurrence);
  }

  // Example patterns

  // Every weekday at 9 AM
  everyWeekday(action, time = '09:00') {
    return this.scheduleRecurring(action, {
      type: 'daily',
      interval: 1
    }, {
      skipConditions: [
        { type: 'weekend' },
        { type: 'holiday' }
      ]
    });
  }

  // First Monday of each month
  firstMondayOfMonth(action) {
    return this.scheduleRecurring(action, {
      type: 'custom',
      calculateNext: (current) => {
        const next = new Date(current);
        next.setMonth(next.getMonth() + 1);
        next.setDate(1);

        // Find first Monday
        while (next.getDay() !== 1) {
          next.setDate(next.getDate() + 1);
        }

        return next;
      }
    });
  }

  // Last business day of quarter
  endOfQuarter(action) {
    return this.scheduleRecurring(action, {
      type: 'custom',
      calculateNext: (current) => {
        const next = new Date(current);
        const month = next.getMonth();

        // Find end of quarter
        const quarterEndMonth = Math.floor(month / 3) * 3 + 2;
        next.setMonth(quarterEndMonth + 3);
        next.setDate(0); // Last day of previous month

        // Back up to business day
        while (!this.isBusinessDay(next)) {
          next.setDate(next.getDate() - 1);
        }

        return next;
      }
    });
  }
}

4. Conditional Execution

Execute based on conditions, not just time:

class ConditionalScheduler {
  constructor() {
    this.conditions = new Map();
  }

  scheduleWhen(action, condition, options = {}) {
    const conditional = {
      id: this.generateId(),
      action: action,
      condition: condition,

      // Options
      timeout: options.timeout || null,
      maxChecks: options.maxChecks || 1000,
      checkInterval: options.checkInterval || 60000, // 1 minute

      // Status
      status: 'waiting',
      checks: 0,
      lastCheck: null
    };

    this.conditions.set(conditional.id, conditional);
    this.startChecking(conditional);

    return conditional;
  }

  async startChecking(conditional) {
    const checkCondition = async () => {
      conditional.lastCheck = new Date();
      conditional.checks++;

      try {
        const conditionMet = await this.evaluateCondition(conditional.condition);

        if (conditionMet) {
          // Execute action
          conditional.status = 'executing';
          await this.executeAction(conditional.action);
          conditional.status = 'completed';
          return;
        }

        // Check limits
        if (conditional.maxChecks && conditional.checks >= conditional.maxChecks) {
          conditional.status = 'max-checks-exceeded';
          return;
        }

        if (conditional.timeout) {
          const elapsed = Date.now() - conditional.createdAt;
          if (elapsed >= conditional.timeout) {
            conditional.status = 'timeout';
            return;
          }
        }

        // Schedule next check
        setTimeout(checkCondition, conditional.checkInterval);

      } catch (error) {
        conditional.status = 'error';
        conditional.error = error.message;
      }
    };

    checkCondition();
  }

  async evaluateCondition(condition) {
    switch(condition.type) {
      case 'document-state':
        return await this.checkDocumentState(
          condition.documentId,
          condition.state
        );

      case 'field-value':
        return await this.checkFieldValue(
          condition.documentId,
          condition.field,
          condition.value
        );

      case 'approval-complete':
        return await this.checkApprovalComplete(condition.documentId);

      case 'user-action':
        return await this.checkUserAction(
          condition.userId,
          condition.action
        );

      case 'time-elapsed':
        return this.checkTimeElapsed(
          condition.startTime,
          condition.duration
        );

      case 'custom':
        return await condition.evaluate();

      default:
        return false;
    }
  }

  // Example: Submit form when all approvals complete
  submitWhenApproved(formId) {
    return this.scheduleWhen(
      {
        type: 'submit-form',
        formId: formId
      },
      {
        type: 'approval-complete',
        documentId: formId
      },
      {
        timeout: 30 * 24 * 60 * 60 * 1000, // 30 days
        checkInterval: 60 * 60 * 1000 // Check every hour
      }
    );
  }

  // Example: Escalate if not completed within 24 hours
  escalateIfDelayed(taskId, assignee, escalateTo) {
    return this.scheduleWhen(
      {
        type: 'escalate-task',
        taskId: taskId,
        from: assignee,
        to: escalateTo
      },
      {
        type: 'time-elapsed',
        startTime: new Date(),
        duration: 24 * 60 * 60 * 1000 // 24 hours
      }
    );
  }
}

5. Time Zone Management

Handle scheduling across time zones:

class TimeZoneScheduler {
  constructor() {
    this.timezones = new Map();
  }

  scheduleInUserTimezone(action, localTime, userTimezone) {
    // Convert user's local time to UTC
    const utcTime = this.convertToUTC(localTime, userTimezone);

    return this.scheduler.scheduleAction(action, utcTime, {
      timezone: userTimezone,
      displayTime: localTime
    });
  }

  scheduleForMultipleTimezones(action, localTime, users) {
    // Schedule same action for each user in their local time
    const scheduled = [];

    users.forEach(user => {
      const userScheduled = this.scheduleInUserTimezone(
        {
          ...action,
          userId: user.id
        },
        localTime,
        user.timezone
      );

      scheduled.push(userScheduled);
    });

    return scheduled;
  }

  convertToUTC(localTime, timezone) {
    // Use proper timezone library (e.g., moment-timezone)
    const moment = require('moment-timezone');
    return moment.tz(localTime, timezone).utc().toDate();
  }

  // Example: Daily standup at 9 AM in each team member's timezone
  scheduleDailyStandup(team) {
    return this.scheduleForMultipleTimezones(
      {
        type: 'send-standup-reminder',
        message: 'Time for daily standup!'
      },
      '09:00',
      team.members
    );
  }

  // Example: Global webinar at specific time
  scheduleGlobalEvent(event, eventTime, timezone) {
    // Event time is in organizer's timezone
    // Show local time to each participant

    const utcTime = this.convertToUTC(eventTime, timezone);

    event.participants.forEach(participant => {
      const localTime = this.convertFromUTC(utcTime, participant.timezone);

      this.sendReminder(participant, {
        event: event,
        utcTime: utcTime,
        localTime: localTime,
        timezone: participant.timezone
      });
    });
  }
}

6. Deadline Management

Track and enforce deadlines:

class DeadlineManager {
  constructor(reminderSystem, escalationSystem) {
    this.reminderSystem = reminderSystem;
    this.escalationSystem = escalationSystem;
    this.deadlines = new Map();
  }

  setDeadline(target, deadline, config) {
    const deadlineEntry = {
      id: this.generateId(),
      target: target,
      deadline: new Date(deadline),

      // Configuration
      reminders: config.reminders || [],
      escalation: config.escalation || null,
      autoActions: config.autoActions || [],

      // Status
      status: 'active',
      completed: false,
      completedAt: null,
      missedDeadline: false
    };

    // Schedule reminders
    if (config.reminders) {
      this.reminderSystem.createReminderSequence(
        target,
        deadline,
        config.reminders
      );
    }

    // Schedule deadline check
    this.scheduler.scheduleAction({
      type: 'check-deadline',
      deadlineId: deadlineEntry.id
    }, deadline);

    // Schedule auto-actions
    config.autoActions.forEach(autoAction => {
      const actionTime = new Date(deadline);
      actionTime.setTime(actionTime.getTime() + autoAction.offset);

      this.scheduler.scheduleAction(
        autoAction.action,
        actionTime,
        {
          condition: {
            type: 'deadline-not-met',
            deadlineId: deadlineEntry.id
          }
        }
      );
    });

    this.deadlines.set(deadlineEntry.id, deadlineEntry);
    return deadlineEntry;
  }

  markCompleted(deadlineId) {
    const deadline = this.deadlines.get(deadlineId);
    if (deadline) {
      deadline.completed = true;
      deadline.completedAt = new Date();
      deadline.status = 'completed';

      // Check if missed
      if (deadline.completedAt > deadline.deadline) {
        deadline.missedDeadline = true;
        deadline.daysLate = this.calculateDaysLate(
          deadline.deadline,
          deadline.completedAt
        );
      }

      // Cancel pending reminders
      this.reminderSystem.cancelReminders(deadlineId);
    }
  }

  // Example: Project milestone with escalation
  setProjectMilestone(project, milestone, dueDate) {
    return this.setDeadline(
      { type: 'milestone', project: project, milestone: milestone },
      dueDate,
      {
        reminders: {
          intervals: [
            14 * 24 * 60 * 60 * 1000,  // 14 days
            7 * 24 * 60 * 60 * 1000,   // 7 days
            3 * 24 * 60 * 60 * 1000,   // 3 days
            1 * 24 * 60 * 60 * 1000    // 1 day
          ],
          recipients: [project.manager.email]
        },
        escalation: {
          afterDeadline: 24 * 60 * 60 * 1000,
          escalateTo: [project.director.email]
        },
        autoActions: [
          {
            offset: 0, // On deadline
            action: {
              type: 'update-status',
              status: 'overdue'
            }
          },
          {
            offset: 7 * 24 * 60 * 60 * 1000, // 7 days after
            action: {
              type: 'escalate-to-executive',
              executive: project.vp
            }
          }
        ]
      }
    );
  }
}

7. Calendar Integration

Visualize and manage scheduled actions:

class ScheduleCalendar {
  constructor(scheduler) {
    this.scheduler = scheduler;
  }

  getUpcomingActions(userId, timeRange) {
    const upcoming = [];

    // Get scheduled actions
    const scheduled = this.scheduler.getScheduledForUser(userId);

    scheduled.forEach(action => {
      if (this.isInRange(action.executeAt, timeRange)) {
        upcoming.push({
          type: 'scheduled-action',
          time: action.executeAt,
          action: action.action,
          status: action.status
        });
      }
    });

    // Get reminders
    const reminders = this.reminderSystem.getUpcomingReminders(userId);

    reminders.forEach(reminder => {
      if (this.isInRange(reminder.time, timeRange)) {
        upcoming.push({
          type: 'reminder',
          time: reminder.time,
          severity: reminder.severity,
          message: reminder.template.subject
        });
      }
    });

    // Get deadlines
    const deadlines = this.deadlineManager.getActiveDeadlines(userId);

    deadlines.forEach(deadline => {
      if (this.isInRange(deadline.deadline, timeRange)) {
        upcoming.push({
          type: 'deadline',
          time: deadline.deadline,
          target: deadline.target,
          status: deadline.status
        });
      }
    });

    // Sort by time
    return upcoming.sort((a, b) => a.time - b.time);
  }

  renderCalendar(userId, month, year) {
    const upcoming = this.getUpcomingActions(userId, {
      start: new Date(year, month, 1),
      end: new Date(year, month + 1, 0)
    });

    return `
      <div class="schedule-calendar">
        <div class="calendar-header">
          <h3>${this.getMonthName(month)} ${year}</h3>
        </div>

        <div class="calendar-grid">
          ${this.renderMonthGrid(upcoming, month, year)}
        </div>

        <div class="calendar-legend">
          <span class="legend-item scheduled">Scheduled Actions</span>
          <span class="legend-item reminder">Reminders</span>
          <span class="legend-item deadline">Deadlines</span>
        </div>
      </div>
    `;
  }

  renderDayCell(date, actions) {
    const hasActions = actions.length > 0;

    return `
      <div class="calendar-day ${hasActions ? 'has-actions' : ''}">
        <div class="day-number">${date.getDate()}</div>
        <div class="day-actions">
          ${actions.map(a => this.renderDayAction(a)).join('')}
        </div>
      </div>
    `;
  }

  renderDayAction(action) {
    return `
      <div class="day-action ${action.type}">
        <span class="action-icon">${this.getIcon(action.type)}</span>
        <span class="action-time">${this.formatTime(action.time)}</span>
      </div>
    `;
  }
}

Implementation Details

Complete Scheduled Actions System

class ComprehensiveSchedulingSystem {
  constructor() {
    this.futureScheduler = new FutureActionScheduler();
    this.reminderSystem = new ReminderSystem(this.futureScheduler);
    this.recurringScheduler = new RecurringScheduler();
    this.conditionalScheduler = new ConditionalScheduler();
    this.timezoneScheduler = new TimeZoneScheduler();
    this.deadlineManager = new DeadlineManager(
      this.reminderSystem,
      this.escalationSystem
    );
    this.calendar = new ScheduleCalendar(this);
  }

  // Schedule one-time future action
  schedule(action, executeAt, options) {
    return this.futureScheduler.scheduleAction(action, executeAt, options);
  }

  // Schedule recurring action
  scheduleRecurring(action, pattern, options) {
    return this.recurringScheduler.scheduleRecurring(action, pattern, options);
  }

  // Set up reminders
  remind(target, deadline, reminderConfig) {
    return this.reminderSystem.createReminderSequence(
      target,
      deadline,
      reminderConfig
    );
  }

  // Schedule with condition
  scheduleWhen(action, condition, options) {
    return this.conditionalScheduler.scheduleWhen(action, condition, options);
  }

  // Set deadline with reminders and escalation
  deadline(target, dueDate, config) {
    return this.deadlineManager.setDeadline(target, dueDate, config);
  }

  // Get user's schedule
  getSchedule(userId, timeRange) {
    return this.calendar.getUpcomingActions(userId, timeRange);
  }
}

// Usage examples

const scheduling = new ComprehensiveSchedulingSystem();

// 1. Schedule license renewal with reminders
scheduling.deadline(
  { type: 'license', id: 'microsoft-365' },
  '2025-01-15',
  {
    reminders: {
      intervals: [
        60 * 24 * 60 * 60 * 1000,  // 60 days
        30 * 24 * 60 * 60 * 1000,  // 30 days
        14 * 24 * 60 * 60 * 1000,  // 14 days
        7 * 24 * 60 * 60 * 1000    // 7 days
      ],
      recipients: ['procurement@company.com']
    },
    autoActions: [
      {
        offset: -30 * 24 * 60 * 60 * 1000, // 30 days before
        action: {
          type: 'submit-approval-workflow'
        }
      }
    ]
  }
);

// 2. Schedule recurring monthly report
scheduling.scheduleRecurring(
  {
    type: 'generate-report',
    reportType: 'monthly-compliance'
  },
  {
    type: 'custom',
    calculateNext: (current) => {
      // Last business day of month
      const next = new Date(current);
      next.setMonth(next.getMonth() + 1);
      next.setDate(0); // Last day of month

      while (!isBusinessDay(next)) {
        next.setDate(next.getDate() - 1);
      }

      return next;
    }
  }
);

// 3. Auto-submit form when approvals complete
scheduling.scheduleWhen(
  {
    type: 'submit-form',
    formId: 'purchase-order-3847'
  },
  {
    type: 'approval-complete',
    documentId: 'purchase-order-3847'
  }
);

Consequences

Benefits

Never Miss Deadlines: - Automatic reminders - Escalation mechanisms - Clear visibility

Reduced Manual Work: - Recurring tasks automated - No remembering required - System handles timing

Better Planning: - Future actions scheduled - Dependencies tracked - Timeline visualized

Improved Compliance: - Required tasks automated - Audit trail of scheduling - Proof of diligence

Enhanced Coordination: - Multi-person workflows - Time zone handling - Synchronized actions

Liabilities

Complexity: - Scheduling logic intricate - Many configuration options - Learning curve exists

Over-Reliance: - Users depend on system - Notifications ignored - "Alert fatigue"

Maintenance: - Schedules need updating - Edge cases arise - System must be monitored

Time Zone Confusion: - DST changes problematic - Multiple zones complex - Errors possible

False Security: - System can fail - Notifications can be missed - Backup plans still needed

Domain Examples

IT: License Management

scheduling.deadline(
  { type: 'license', name: 'Microsoft 365' },
  renewalDate,
  {
    reminders: { intervals: [60, 30, 14, 7, 1] },
    autoActions: [
      { offset: -30, action: 'start-approval' },
      { offset: -7, action: 'emergency-escalation' }
    ]
  }
);

HR: Performance Reviews

scheduling.scheduleRecurring(
  {
    type: 'performance-review',
    employees: allEmployees
  },
  { type: 'yearly', month: 3 }, // March
  {
    reminders: {
      intervals: [30, 14, 7, 1],
      recipients: ['managers']
    }
  }
);

Finance: Quarterly Reports

scheduling.scheduleRecurring(
  {
    type: 'quarterly-report',
    reportType: 'financial-summary'
  },
  {
    type: 'custom',
    calculateNext: endOfQuarter
  }
);
contracts.forEach(contract => {
  scheduling.deadline(
    contract,
    contract.expirationDate,
    {
      reminders: {
        intervals: [90, 60, 30],
        recipients: [contract.owner]
      },
      escalation: {
        afterDeadline: 0,
        escalateTo: ['legal-team@company.com']
      }
    }
  );
});

Prerequisites: - Volume 3, Pattern 16: Temporal Validation (valid dates for scheduling) - Volume 3, Pattern 17: State-Aware Behavior (scheduled state transitions)

Synergies: - Volume 3, Pattern 18: Audit Trail (log all scheduled actions) - Volume 3, Pattern 19: Version Control (scheduled versioning) - All patterns (scheduling enhances all workflows)

Conflicts: - Real-time only systems - Ad-hoc workflows - Unpredictable processes

Alternatives: - Manual calendar reminders - Cron jobs (simple scheduling) - External scheduling services

Known Uses

Calendar Apps: Google Calendar, Outlook scheduled events

Task Management: Asana, Trello due dates and reminders

Email: Scheduled send, follow-up reminders

Backup Systems: Scheduled backups with retention

CI/CD: Scheduled builds and deployments

Monitoring: Scheduled health checks and reports

Marketing: Campaign scheduling and drip emails

Maintenance: Scheduled system maintenance windows


Further Reading

Academic Foundations

  • Job Scheduling: Pinedo, M.L. (2016). Scheduling: Theory, Algorithms, and Systems (5th ed.). Springer. ISBN: 978-3319265803
  • Cron Expression Theory: Vixie, P. (1994). "Cron Man Page." https://man7.org/linux/man-pages/man5/crontab.5.html
  • Distributed Scheduling: Chapin, S.J., et al. (1999). "Distributed and parallel job scheduling." IEEE Distributed Systems Online.

Practical Implementation

Standards & Specifications

  • Pattern 21: Temporal Validation - Schedule timing validation
  • Pattern 22: State-Aware Behavior - Scheduled state transitions
  • Pattern 23: Audit Trail - Log scheduled action execution
  • Volume 2, Pattern 14: Predictive Time Windows - Predict scheduled outcomes
  • Volume 1, Chapter 6: Business Domain Patterns - Scheduling architecture

Tools & Services

Implementation Examples