Building an Automated Operations Stack for Sailing Charter Fulfillment
Over the past development session, I built out a comprehensive automation layer for JADA's sailing charter operations—spanning email follow-ups, operational digests, trip sheet deployment, and real-time crew dispatch coordination. The system integrates disparate data sources (DynamoDB crew tables, CSV contact registries, S3-hosted guest pages) into a single operational workflow, reducing manual touchpoints and ensuring Coast Guard compliance.
What Was Built
The system consists of three core components:
- Follow-up Email Builder — Auto-generates templated follow-ups to charter clients (guest list requests, waiver reminders, boarding details)
- Morning Digest Service — Aggregates daily operations summary: crew assignments, pending deposits, guest manifests, weather, launch checklist
- Trip Sheet Deployment Pipeline — Generates guest-facing HTML trip sheets with unique photo codes, uploads to S3, invalidates CloudFront cache, and notifies crews
All three are coordinated through a scheduled macOS LaunchAgent and triggered from a central orchestration file.
Technical Architecture
Follow-Up Email System
Located at /Users/cb/.claude/skills/jada-followup/, the system uses a template-based approach:
- Templates:
reference/followup_templates.json— JSON-driven templates for guest list requests, deposit reminders, and cancellation notices - Builder:
scripts/build_followups.py— Reads fromledger.jsonandcontacts.csv, matches charter bookings to template rules, and generates email payloads - Filtering: Excludes crew-only records and prospective bookings; validates email addresses; applies suppression rules
The builder operates in a dry-run mode by default, masking PII before output—critical for safe local testing. Template invariants (required subject line, sender identity, required fields) are asserted before queue generation.
Morning Digest Pipeline
The digest system is defined in /Users/cb/icloud-jada-ops/digest/build_digest.py and automatically scheduled via LaunchAgent.
Automation: Registered with macOS at /Users/cb/Library/LaunchAgents/com.jada.morning-digest.plist, set to run daily at a configured hour. The LaunchAgent wrapper provides automatic restart on failure and integrates with system logs (queryable via log stream --predicate 'process == "build_digest.py"').
Data Sources:
- DynamoDB table
jada-crew-dispatch— real-time crew assignments, roles, and availability ledger.json— charter bookings, deposit status, payment termscontacts.csv— guest contact info, special requests, dietary restrictions- Weather APIs and external status feeds
Output: Digest is written to a timestamped file and emailed to the operations team, aggregating all pending actions into a single morning briefing.
Trip Sheet Deployment with Photo Codes
The trip sheet generation and deployment workflow:
- Generation: HTML trip sheet is generated dynamically, embedded with a unique 6-character photo code (e.g.,
BYHMJG) for guest photo sharing - S3 Upload: Trip sheet and waiver PDFs are uploaded to the live S3 bucket (specific bucket name varies by environment; refer to
/Users/cb/icloud-repos/shared/references/environments.mdfor current) - Cache Invalidation: CloudFront distribution is invalidated for the trip sheet path, ensuring guests see the latest version immediately
- Guest Notification: Guests receive SMS and email with direct links to the trip sheet and waiver
Deployment Tool: Uses jada-deploy CLI, which abstracts S3 bucket management and CloudFront distribution IDs. Command pattern:
jada-deploy upload-print \
--charter-id=2026-07-04-dylan \
--file=trip-sheet.html \
--invalidate-cdn=true
DynamoDB Integration for Crew Dispatch
Real-time crew assignments are stored in DynamoDB table jada-crew-dispatch, queried via key-value lookups on event date and crew member ID. The schema supports:
- Role assignments (captain, deckhand, bartender, etc.)
- Availability status and last-minute substitutions
- Contact info for notifications
Queries are paginated to handle large datasets (July 4th events, for example, span multiple crew assignments). Failed queries are retried with exponential backoff to tolerate transient DynamoDB throttling.
Contact Management and Validation
The system maintains two canonical contact sources:
contacts.csv— authoritative registry of guest and crew info (email, phone, role, special flags)- DynamoDB
jada-crew-dispatch— real-time crew assignment state
Cross-referencing ensures that email sends target the correct contact and that phone numbers match between sources. Edge cases (missing email, crew-only rows, prospective bookings) are filtered during template matching.
Key Architectural Decisions
Why LaunchAgent for Scheduling?
Rather than relying on external cron services or Lambda, the morning digest uses macOS LaunchAgent. Rationale:
- No external dependency for a single-user, daily task
- Native integration with system logging and restart policies
- Operates even if the cloud infrastructure is temporarily down
- Centralized control in the user's home directory
Why S3 + CloudFront for Guest Docs?
Trip sheets and waivers are served via S3 + CloudFront (not generated on-demand):
- Performance: CloudFront edge caching ensures guests get near-instant downloads, critical on event day
- Audit trail: S3 versioning tracks all trip sheet revisions
- Cost: Static hosting is cheaper than running a web server
- Availability: CDN remains available even if the primary application is down
Why Template-Based Emails Over Custom Logic?
Email generation uses JSON templates rather than inline business logic:
- Non-engineers can edit templates without code changes
- Templates are versioned and auditable
- New email types can be added without redeploying scripts
- A/B testing variants is trivial
Infrastructure and Secrets Management
All infrastructure references (S3 bucket names, CloudFront distribution IDs, DynamoDB table names, Gmail API credentials) are kept in /Users/cb/icloud-repos/shared/references/environments.md and loaded at runtime, never hardcoded in scripts. This allows the same codebase to work across development and production environments without modification.
What's Next
The foundation is in place for further automation:
- Guest list validation—automatically parse responses from clients and cross-check against Coast Guard manifest requirements
- Payment status monitoring—automatically remind guests of outstanding balances as the charter date approaches
- Crew coordination—trigger follow-ups to crew only when their role is confirmed in DynamoDB
- Weather-driven rescheduling—if marine forecast deteriorates, auto-notify guests of potential reschedule windows
All of these can be built as additional rules in the template system and digest pipeline without architectural changes.
```