```html

Building an Attended SMS Confirmation Loop for Lead Handoff: Architecture & Quiet-Hours Enforcement

What Was Done

This session established the foundation for a single-confirmation SMS workflow that respects operational constraints: quiet-hours enforcement (17:00–08:00), attended human approval before any outbound message, and zero unattended scheduled sends to real contacts. The focus was narrowly scoped to validate tooling, understand state distribution across three environments (local Mac, EC2 repos, DynamoDB + Lambda), and deliberately choose not to automate what should remain manual.

Technical Details: The Send-SMS Tool & Execution Path

The verification step confirmed that send-sms is present and executable on the development machine:

$ which send-sms
$ file $(which send-sms)  # verify it's a compiled binary or script

The tool wraps the iMessage API (macOS native), meaning SMS delivery routes through the local machine's Messages framework rather than through an external SaaS gateway. This eliminates credential management for a third-party SMS provider but introduces a critical constraint: the machine must be awake and the Messages app must have permission grants. For production crew confirmations, this is acceptable in an attended pattern (human is present to intervene if Messages is unreachable).

The execution path is simple and auditable:

  1. Agent caches charter data (date, role, call time) from DynamoDB or local proposal files
  2. Human returns next morning after 08:00 and says "send Travis's confirmation"
  3. Agent drafts exact SMS text using cached data
  4. Human reviews text on screen before approval
  5. Human one-taps approval; send-sms executes with phone number and message body

No scheduled daemons, no cron jobs, no Lambda functions firing outbound SMS unattended. The latency cost is minimal—human availability during business hours is already required for proposal reviews.

Infrastructure: Distributed State & Why It Matters

A critical discovery during this session: there is no single source of truth for proposal or charter data. The system is deliberately polyglot, and understanding why reshapes the design.

  • Local Mac (development): Proposal files and jada-ops/ snapshots; used for drafting and approval workflows
  • EC2 ~/repos: Code repositories, static sites/, and narrative documentation (jada-heritage-research.md); authoritative for deployed business logic
  • DynamoDB: Charter records, roster, revenue; source of truth for confirmed crew assignments and booking state
  • Lambda (shipcaptaincrew): Stripe integration, subdomain/magic-link pattern for crew portal; runs on schedule or webhook trigger
  • iCloud (jada-ops/ + JADA/): Proposals, Sheraton COIs; human-curated decision artifacts

Why distributed? Each system evolved to solve a specific problem with different consistency guarantees. DynamoDB provides ACID transactions for crew roster (critical for scheduling conflicts). Local proposals remain human-editable during drafting. EC2 repos store only deployed, reviewed code. Mixing them requires explicit choreography, not implicit sharing.

For the confirmation workflow, the agent must read from both local proposal files (what you're reviewing for approval) and DynamoDB (what's actually booked). Discrepancies are a signal to halt and ask for clarification before texting.

Quiet Hours & Timing Constraints

The quiet-hours window (17:00–08:00) is enforced by a simple guard condition in the agent's decision tree:

if current_hour >= 17 or current_hour < 8:
    return "Message queued for 08:00. Not sending in quiet hours."
else:
    # Only proceed if attended (human present)

This prevents accidental SMS sends during sleep hours, which is critical for single-confirmation workflows where a text to the wrong number can't be recalled. The machine's local time (not UTC) is the source of truth for this check, since all crew are in the same timezone.

Key Decisions & Trade-offs

Attended > Unattended for Confirmations
An unattended scheduled send at 08:00 tomorrow would save 30 seconds of human interaction. The cost is irreversibility and operational opacity: if the cached data was wrong, if Travis's number was updated, if the charter got rescheduled, the text leaves the system before anyone glances at it. An attended one-tap send keeps the human in the loop without requiring them to draft the message (agent does that). For a system that sends SMS to real people about real crew assignments, this is the right trade-off.

iMessage API (Local) > SaaS Gateway
No Twilio account, no SendGrid SMS, no credentials to rotate. Eliminates a dependency and an external rate limit. The cost is availability coupling: if Messages crashes, the tool fails. Acceptable for attended scenarios (human can try again); not acceptable for fire-and-forget daemons.

Cache Charter Data Before You Sleep
If the agent caches the relevant fields (crew name, date, role, call time, phone number) from DynamoDB or proposals before the session ends, tomorrow morning's agent doesn't re-fetch and doesn't risk a stale view. This is a micro-optimization but it keeps the confirmation instant and removes a round-trip.

What's Next: The Crew Monitor & Proposal Drafting

Two items are explicitly parked until the next session:

  1. Crew monitor polling loop: A scheduled agent that watches for incoming messages from getmyboat.com and fires an immediate acknowledgment ("Thanks for reaching out—we'll review and follow up"). This requires a persistent polling loop or webhook receiver, which needs careful setup to avoid hammering external APIs. Waiting for your explicit "start the crew monitor" command before implementation.
  2. Proposal drafting: Once a warm lead is acknowledged, the agent should synthesize a proposal using previous proposals sent to Dylan, Noelle, and Molly as templates. This requires pulling those files, extracting structure, and adapting to new lead data. Deferred until the monitor is running and producing leads.

Both require a restart and your confirmation that it's safe to begin: we do not implement polling loops or unattended message drafting without your explicit hand-off.

Validation & Next Steps

Before tomorrow's session:

  • Verify send-sms still works and Messages permissions are intact
  • Confirm which Travis confirmation you want cached (date, role, call time, phone number)
  • Decide: do you want the crew monitor to start tomorrow, or is that a separate session?

When you return after 08:00 tomorrow, say "send Travis's confirmation" and the exact text will be ready for one-tap approval.

```