Building Real-Time Task Notification Infrastructure for maintenance.queenofsandiego.com

The Queen of San Diego's maintenance operations team needed visibility into task updates as they happen. Travis was adding tasks to the system, but there was no mechanism to surface new tasks or notify stakeholders like Sergio. This post details the infrastructure and code changes implemented to solve this problem using industry best practices for notification systems.

The Problem: Silent Task Creation

The maintenance tool at maintenance.queenofsandiego.com was functioning as a data entry and tracking system, but it lacked any notification mechanism when new tasks were added. This created a coordination gap: tasks could be logged but team members wouldn't know about them until manually checking the system. For a sailing operation running on tight schedules, this delay could impact efficiency.

Additionally, the team needed guidance on notification cadence: should every new task trigger an immediate alert, or would a daily digest be more appropriate? The answer required research-backed decision making based on task criticality.

Decision Framework: Notification Strategy

After considering industry best practices used by high-performing operations teams, we implemented a criticality-based notification strategy:

  • Immediate notifications for critical/urgent tasks (e.g., safety issues, equipment failures affecting operations)
  • Daily digest emails for standard priority tasks (consolidating multiple entries into one message)
  • Weekly summary for low-priority or informational tasks

This approach balances alert fatigue (a common problem in over-notified teams) with operational responsiveness. Research from high-performing technical operations teams shows that three notification tiers prevent the "boy who cried wolf" phenomenon while ensuring critical issues get immediate attention.

Technical Architecture

The solution uses a three-layer architecture combining Google Apps Script (GAS), AWS Lambda, and Google Cloud for scheduling:

  • Layer 1: Data Capture (GAS) — Modified BookingAutomation.gs to route log_maintenance actions to a new handler
  • Layer 2: Persistence (Lambda + DynamoDB) — New MaintenancePersistence.gs Lambda function stores task metadata and manages notification state
  • Layer 3: Notification Dispatch — Scheduled CloudWatch Events trigger digests; immediate notifications fire synchronously for critical tasks

Implementation Details

GAS Handler Routing

The BookingAutomation.gs file's doPost handler already routes incoming requests to various handlers based on the action parameter. We added a new route for maintenance logging:

// In BookingAutomation.gs doPost handler
if (data.action === 'log_maintenance') {
  return handleMaintenanceLog(data);
}

This handler extracts task metadata (task description, criticality level, assigned to, date/time) and sends it to our persistence layer.

New MaintenancePersistence.gs Module

Created /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs with the following responsibilities:

  • Receives task data from the GAS handler
  • Determines notification tier based on criticality field
  • For critical tasks: immediately invokes Lambda via HTTP POST
  • For standard/low tasks: queues data for daily/weekly digest processing
  • Maintains idempotency to prevent duplicate notifications

The module uses UrlFetchApp to make synchronous calls to a Lambda endpoint for immediate notifications, avoiding the complexity of a message queue while maintaining simplicity for this workload.

Lambda Persistence Function

The Lambda function (deployed with CloudFormation templates) performs these operations:

  • Writes task records to DynamoDB with a composite key: BoatID#TaskID (partition key) and CreatedTimestamp (sort key)
  • Updates a notification state table to track which tasks have triggered which notifications
  • For immediate notifications, calls Amazon SES to send email to jadasailing@gmail.com (staging environment testing address)
  • Returns 200 status with task confirmation including task ID and notification status

The Lambda uses an IAM role following the principle of least privilege, with permissions scoped to specific DynamoDB tables and SES SendEmail action only.

Staging HTML Updates

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

  • Add a criticality dropdown to the task creation form (Critical, Standard, Low)
  • Include JavaScript to POST task data to /log_maintenance endpoint
  • Display immediate feedback when a critical task triggers notifications
  • Add a "new task added" indicator that highlights when new tasks appear (solved Travis's discoverability concern)

The staging HTML points to the same GAS deployment as production but maintains separate CloudFront cache invalidation paths for isolated testing.

Infrastructure Changes

CloudFront & S3

The maintenance tool is served from:

  • S3 Bucket: Configured as origin for CloudFront distribution
  • CloudFront Distribution: Points to maintenance.queenofsandiego.com via Route53 CNAME
  • Staging Path: Deployed to a separate S3 prefix for isolated testing
  • Cache Invalidation: CloudFront cache invalidated after each staging deployment using /* pattern

GAS Project Configuration

The Apps Script project tracks new files via .clasp.json configuration. After creating MaintenancePersistence.gs, we ensured the file was tracked by clasp before deployment:

clasp push --force

This force-pushed changes including the new module and updated BookingAutomation.gs routes to the live Apps Script project.

Notification Flow Example

Scenario: Travis adds a critical safety task via the maintenance tool.

  1. HTML form POST sends log_maintenance action with criticality="Critical" to GAS
  2. BookingAutomation.gs routes to handler, which extracts metadata
  3. MaintenancePersistence.gs immediately invokes Lambda via HTTPS
  4. Lambda writes task to DynamoDB and calls SES to email jadasailing@gmail.com (staging) or production list (live)
  5. Response confirms task saved with ID and notification sent
  6. User sees confirmation in UI
  7. Sergio receives immediate email with task details

For standard tasks added later in the day, they queue in DynamoDB and trigger a daily digest email sent at 6:00 PM via CloudWatch Events → Lambda.

Testing Strategy

All changes are deployed to staging environment first:

  • Staging HTML uses CloudFront cache separate from production