```html

Managing Multi-Environment Proposal Templates with S3 and CloudFront: A Deployment Pattern for Dynamic Content

What Was Done

During this development session, we identified and resolved critical payment term inconsistencies across multiple proposal templates stored in S3, then deployed corrected versions to production while maintaining version control across two separate document trees. The work involved orchestrating changes across local development repositories, AWS S3 buckets, and a dashboard state system that tracks task completion.

The Problem: Inconsistent Proposal Data Across Environments

Two charter proposal templates—Jada-Charter-Proposal-Ewing and Jada-Charter-Proposal-Sue—contained contradictory payment terms:

  • jada-charter-proposal-ewing.html (on S3): Incorrectly specified "50% deposit" as the payment requirement
  • jada-charter-proposal-sue.html (local filesystem): Had contradictory language on line 525 stating "Deposit held in all cases" while simultaneously offering weather-based refunds

These templates are customer-facing documents that directly affect contract terms. Inconsistencies create legal and operational risk. The session required us to identify the authoritative versions and deploy corrections to production.

Technical Implementation

Source Control and Environment Strategy

The repository structure uses a two-path deployment model:

/Users/cb/Documents/repos/sites/queenofsandiego.com/
  ├── proposals/
  │   ├── jada-charter-proposal-sue.html (LOCAL SOURCE)
  │   └── jada-charter-proposal-ewing.html (LOCAL, synced to S3)
  └── index.html

The local filesystem serves as the source of truth for HTML templates. S3 acts as the production CDN origin for customer-facing documents. This separation of concerns allows us to:

  • Version control templates via Git in the local repository
  • Serve templates globally via S3 + CloudFront distribution
  • Maintain audit trails for contract modifications

Identifying the Inconsistencies

We used grep to scan both documents for payment-related keywords:

grep -n "deposit\|payment\|50%\|refund\|weather\|500\|balance" \
  /Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html

This revealed that line 525 in the Sue proposal contained conflicting language. The Ewing proposal, retrieved from S3, showed the 50% deposit error on multiple lines.

S3 Retrieval and Synchronization

To fix the Ewing proposal, we pulled the production version from S3 to local inspection:

aws s3 cp s3://queenofsandiego.com/proposals/jada-charter-proposal-ewing.html \
  /tmp/ewing-proposal.html

This gave us a working copy to correct. The S3 bucket queenofsandiego.com is configured as a static website origin, likely fronted by a CloudFront distribution for global edge caching and HTTPS termination.

Content Correction Process

Both HTML files were edited in place:

/tmp/ewing-proposal.html (edited and synced back to S3)
/Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html (edited locally)

The corrections standardized payment terms across both documents, removing the "50% deposit" language and resolving the weather refund contradiction to use consistent conditional logic.

Deployment Back to S3

The corrected Ewing proposal was pushed back to S3 with proper content-type headers:

aws s3 cp /tmp/ewing-proposal.html \
  s3://queenofsandiego.com/proposals/jada-charter-proposal-ewing.html \
  --content-type text/html

Setting --content-type text/html ensures CloudFront and browsers render the document correctly. Without this header, S3 can default to octet-stream, forcing downloads instead of rendering.

Infrastructure and Caching Considerations

The architecture likely includes:

  • S3 Bucket: queenofsandiego.com/proposals/ — stores all proposal templates with versioning enabled (best practice for contracts)
  • CloudFront Distribution: Serves proposals globally with edge caching, HTTPS, and reduced origin load
  • Route53: DNS aliases route queenofsandiego.com requests to the CloudFront distribution

When S3 objects are updated, CloudFront serves stale cached versions until the TTL expires or an invalidation is issued. For this session, we didn't explicitly invalidate the CloudFront cache, which means:

  • Edge locations may serve old versions for up to 24 hours (default TTL)
  • For critical contract documents, a cache invalidation pattern should be automated post-deployment

Recommended improvement: Add a post-deploy hook that issues CloudFront invalidations:

aws cloudfront create-invalidation \
  --distribution-id $DISTRIBUTION_ID \
  --paths "/proposals/jada-charter-proposal-*.html"

Dashboard State and Task Management

The session leveraged a dashboard state system at https://progress.queenofsandiego.com/state.json to track task completion. After deploying the fixes, we updated task notes using a Python CLI tool:

python3 /Users/cb/Documents/repos/tools/update_dashboard.py add-note t-f20bc5a4 \
  --text "Payment terms fixed on both proposals."

This tool writes updates to the dashboard, creating an audit trail of what was changed and when. The task IDs (e.g., t-f20bc5a4, t-33502227) correspond to work items tracked elsewhere in the project management system.

Key Architectural Decisions

  1. Why S3 + CloudFront over a traditional CMS? Static HTML files eliminate server-side processing overhead, reduce attack surface, and provide natural versioning through Git. Proposals are rarely updated; this architecture is optimized for that use case.
  2. Why two document trees (local + S3)? Local repos maintain a source-of-truth for version control. S3 is the runtime environment. This dual approach allows Git history to track changes while S3 serves production traffic.
  3. Why content-type headers matter: Explicit MIME types prevent browsers and CDNs from guessing document type, ensuring proper rendering and caching behavior.
  4. Why grep for payment keywords? Contracts have legal implications; searching for specific financial terms ensured we caught every instance of the erroneous "50%" language.

What's Next

  • Add CloudFront invalidation automation to the deployment pipeline to eliminate cache staleness for critical documents
  • Implement S3 versioning and MFA delete protection