Building a Real-Time Task Notification System for maintenance.queenofsandiego.com

When Travis added tasks to the maintenance tool via SMS and they didn't appear in any obvious way, we realized the system needed a proper notification mechanism. This post details how we implemented real-time task change detection, asynchronous notification delivery, and staging/production separation for a Google Apps Script-based maintenance tracking system.

The Problem: Task Visibility and Team Awareness

The maintenance.queenofsandiego.com tool had a critical gap: when new maintenance tasks were added, there was no way for the team (particularly Sergio, our operations lead) to know immediately. The existing system stored tasks but provided no alerting mechanism. We needed to:

  • Surface newly added tasks in real-time
  • Notify team members asynchronously without blocking the UI
  • Determine notification cadence based on task criticality (immediate vs. daily digest)
  • Maintain separate staging and production notification flows
  • Integrate with existing Google Calendar infrastructure for event tracking

Architecture: Lambda + GAS + Email Persistence Pattern

We implemented a three-tier notification architecture:

  • Persistence Layer (Lambda): Serverless function to store task change events and manage notification state
  • GAS Handler: Google Apps Script function to receive task submissions and trigger notifications
  • Email Delivery: Gmail integration for immediate and digest notifications

This pattern isolates notification logic from the real-time UI path, ensuring task creation never waits for email delivery.

Implementation Details

File: /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs

We created a new GAS file to handle persistent storage of maintenance events and notification state. This file manages:

  • Writing task events to a CloudFirestore-backed database (via Lambda)
  • Tracking which tasks have been notified
  • Managing notification timestamps for digest logic
  • Querying recent changes for the staging vs. production email destinations

The Lambda integration allows us to persist data outside of Google's limited GAS storage, giving us reliable audit trails and notification history.

File: /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenanceCalendar.gs

A new companion file for calendar operations. This handles:

  • Creating or fetching the "Jada Maintenance" calendar in jadasailing@gmail.com
  • Parsing task criticality from descriptions
  • Creating calendar events for high-priority maintenance items
  • Updating calendar event descriptions with task completion status

We use Google Calendar as a secondary notification medium for critical tasks, ensuring nothing slips through email filters.

File: /Users/cb/Documents/repos/sites/queenofsandiego.com/BookingAutomation.gs

Updated the existing doPost handler to route maintenance task submissions to a new action handler:

if (data.action === 'log_maintenance') {
  return handleMaintenanceLog(data);
}

The handleMaintenanceLog function:

  • Validates task data (title, description, criticality level)
  • Calls the persistence Lambda with task details
  • Immediately returns success to the UI (async pattern)
  • Triggers notification dispatch asynchronously

File: /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html

The staging maintenance tool HTML was updated with:

  • Task criticality selector (Low / Medium / High / Critical)
  • Form validation for required fields
  • POST handler that routes to the GAS doPost endpoint with action=log_maintenance
  • Client-side environment detection to target staging vs. production endpoints

We added a feature flag in the initialization to send all test notifications to jadasailing@gmail.com while in staging mode, allowing us to test the full notification pipeline without spamming production recipients.

Infrastructure: Lambda Function Deployment

The Lambda function (deployment pattern modeled after existing tips-box Lambda) is configured as:

  • Runtime: Python 3.11
  • Handler: index.lambda_handler
  • IAM Role: Uses existing service role with CloudFirestore and Secrets Manager permissions
  • Timeout: 30 seconds (accounts for potential database latency)
  • Memory: 256 MB

The Lambda function:

  • Receives task payload from GAS via HTTPS POST
  • Stores event in CloudFirestore collection: /maintenance_tasks/{task_id}
  • Updates notification state collection: /notification_state/{date}
  • Returns JSON response with task ID and persistence confirmation
  • Handles failures gracefully with exponential backoff retry logic

Notification Logic: Criticality-Driven Cadence

Industry best practices (informed by analysis of SRE team workflows from Google Cloud documentation and incident management research) suggest:

  • Critical tasks: Immediate email notification + calendar event + SMS (future)
  • High priority: Immediate email notification + calendar event
  • Medium priority: Added to daily digest, sent at 8 AM
  • Low priority: Weekly digest only

This approach prevents alert fatigue while ensuring urgent items get immediate visibility. The daily digest consolidation for medium/low tasks aligns with how high-performing ops teams (per Accelerate: The Science of Lean Software and DevOps) operate — batching non-urgent items to reduce context switching.

Staging vs. Production Separation

Since maintenance.queenofsandiego.com shares infrastructure with the live tool, we implemented separation via:

  • CloudFront staging distribution: Points to staging-index.html on S3 bucket queenofsandiego-maintenance-staging
  • GAS environment variable: Set via PropertiesService.getScriptProperties()['ENVIRONMENT']
  • Lambda conditional: Routes staging requests to test SNS topic (eventually email), production to primary topic
  • Email routing: Test emails go to jadasailing@gmail.com; production notifications route to team distribution list

This separation allows full integration testing without risking production notification spam.

Deployment Steps

The deployment sequence ensures zero downtime:

  1. Create new GAS files: MaintenancePersistence.gs,