```html

Automating Charter Operations: Follow-Up Pipelines, Morning Digests, and Live Document Deployment

What Was Done

This session unified three critical operational subsystems for managing sailing charter logistics: a follow-up email templating system, an automated morning digest aggregator, and a live document deployment pipeline with cache invalidation. The work integrated constraint-validated email queuing, scheduled digest generation via macOS LaunchAgent, and real-time trip-sheet updates deployed to S3 and served through CloudFront.

Follow-Up Email System

The follow-up pipeline is built around template-driven email composition with strict validation rules. The system lives in /Users/cb/.claude/skills/jada-followup/ with three core components:

  • Template definitions: reference/followup_templates.json contains template objects with required fields: name, subject, body, event_type, trigger_days (days after event to send), and exclusions (contact fields to skip). This JSON structure enforces schema consistency across all follow-ups.
  • Build script: scripts/build_followups.py reads the ledger, contacts, and templates, then generates an email queue. The script validates three invariants before queueing: no duplicate recipient+template combinations, no re-mailing suppressed contacts (checked against suppression_property in contacts.csv), and no mailing to event crew or staff roles.
  • Integration: The queue hooks into send_blast.py, the canonical blast delivery engine, which accepts pre-built queues and handles Gmail API delivery with retry logic.

Design rationale: Templating decouples operational content from code. Validation rules prevent accidental duplicate sends and respect opt-out preferences stored in the contacts ledger. The exclusion rules ensure crew and event staff don't receive patron follow-ups.

Morning Digest System

The digest aggregates daily operations snapshots and is deployed as a scheduled background task. Core files:

  • Digest builder: /Users/cb/icloud-jada-ops/digest/build_digest.py scans the ledger, DynamoDB crew dispatch table, and in-flight event records to produce a structured daily summary. It queries for events within a rolling window (default: today and tomorrow) and formats crew assignments, patron counts, and logistics notes into a human-readable digest.
  • LaunchAgent: /Users/cb/Library/LaunchAgents/com.jada.morning-digest.plist schedules the digest builder to run daily at 06:00 (configurable via StartCalendarInterval in the plist). The agent logs output to ~/Library/Logs/jada-morning-digest.log and captures stderr for debugging.
  • Distribution: The digest is saved to /Users/cb/icloud-jada-ops/digest/ and synced to iCloud Drive for access across devices.

Design rationale: A scheduled background task eliminates manual log reviews. DynamoDB integration gives real-time crew visibility; the rolling window surfaces same-day issues and prep for next-event logistics.

Infrastructure: Document Deployment and Caching

Live documents (trip sheets, waivers, guest lists) are deployed to S3 and served through CloudFront. The workflow:

  • S3 bucket: Documents are uploaded to the queenofsandiego.com S3 bucket under the print/ prefix (e.g., print/2026-07-04-dylan-trip-sheet.html). Each document is versioned by event date and type.
  • CloudFront distribution: The queenofsandiego.com CloudFront distribution serves print/* objects with a default TTL of 3600 seconds. On-demand invalidation is triggered via the CloudFront API after each document update.
  • Deployment script: /Users/cb/icloud-jada-ops/jada-deploy (custom tooling) uploads a document to S3, then invalidates CloudFront cache paths matching the document type and date pattern. Invalidation is polled to confirm completion before returning.
  • Canonical sync: After deployment, the updated document is synced back to /Users/cb/icloud-jada-ops/{event_date}/{document_type} for offline reference and audit trails.

Example workflow: When crew assignments are finalized for the July 4 event, the trip sheet is regenerated with crew names and roles, deployed to S3, cached invalidated, and the crew receives an SMS with the live URL. Guests and staff refresh their browser to see the updated sheet without stale cache.

Crew Dispatch via DynamoDB

Crew assignments are stored in DynamoDB table jada-crew-dispatch with event date as the partition key. The table stores role assignments (captain, first mate, second mate, crew) and confirmation status. Updates trigger document regeneration and notification workflows:

  • Query pattern: aws dynamodb scan --table-name jada-crew-dispatch --filter-expression "event_date = :date" (with retries and pagination for large result sets).
  • Update pattern: Role changes are written to the table and immediately surface via the digest builder's next run.
  • Workflow: When a crew member is assigned, their contact info is looked up in contacts.csv (keyed by name), and an email/SMS notification is sent via the blast system.

Design rationale: DynamoDB provides low-latency reads for on-demand crew queries and automatic sync to the morning digest. Partitioning by event date allows efficient bulk queries for a single event.

Key Decisions

  • Template-driven follow-ups: Separating templates from code allows non-engineers to edit follow-up content without touching Python. The JSON schema enforces consistency and validation rules catch common mistakes (duplicate sends, suppressed contacts).
  • LaunchAgent for scheduled digests: Rather than an external cron service or Lambda, macOS LaunchAgent provides local scheduling with built-in log capture and reliability. The digest can run offline and sync to iCloud when the Mac is next online.
  • CloudFront invalidation on deploy: On-demand invalidation (vs. long TTLs) ensures crew and guests always see fresh trip sheets without waiting for cache expiry. Invalidation is synchronous in this workflow to confirm before notifying stakeholders.
  • Ledger + DynamoDB split: The ledger (contacts.csv + metadata) is the source of truth for static crew and patron info; DynamoDB stores event-specific role assignments and confirmation status. This avoids duplicating crew records across systems.
  • No crew spam rule: Follow-up templates exclude crew and staff roles to prevent re-mailing event participants. This is enforced at queue-build time by checking the role field in contacts.csv against exclusion lists.

Technical Patterns

  • Validation at ingest: The follow-up builder validates templates and ledger consistency before queueing any emails, catching schema mismatches early.
  • Idempotent deploys: S3 uploads and CloudFront invalidations are idempotent; re-running a deployment with the same document produces the same output and cache state.
  • Async notification: Email and SMS sends are queued by the blast system and delivered asynchronously, allowing the deployment workflow to complete without waiting for mail delivery.
  • Canonical sync: Local jada-ops directories mirror the S3/CloudFront live state, enabling offline reference and simplifying audit trails.

What's Next

  • Digest distribution: Extend the digest builder to email the morning summary to stakeholders (ops team, charter masters) at a configurable time.
  • Template versioning: Add a version field to templates to track which version was used for each send, enabling targeted re-sends if a template is updated.
  • DynamoDB crew confirmation: Integrate confirmation workflows (email/SMS replies) to update crew status in DynamoDB and trigger escalations if roles remain unconfirmed near event time.
  • Multi-region digest: Extend the digest to aggregate events across multiple regions if the charter operation expands.
```