```html

Preventing S3 Deployment Regressions: Hard Rules for Multi-Environment Deploys

Last session, a stale local index.html was deployed to production S3, wiping three previously-working features on queenofsandiego.com: the JADA → BOOK NOW hero crossfade, the Stripe embedded checkout flow, and accidentally resurrecting a deleted "Ranch & Coast readers..." hero line. The deployment also violated staging-first protocol by pushing both staging and prod in a single command. This post documents the root cause, the hard rules now in place to prevent it, and how to structure multi-environment deploys safely.

What Went Wrong

The failure chain was simple but preventable:

  • No pre-flight diff: Local index.html was 3,650 lines and hadn't been pulled from S3 in days. Prod S3 was newer.
  • No staging gate: The same command deployed to both s3://queenofsandiego.com/ (prod) and staging in one shot, violating the staging-first rule already documented in prior session notes.
  • Ignored prior warnings: The previous session-summary explicitly flagged "stale local files" as a risk. The warning was visible but not enforced.
  • No snapshot before overwrite: S3 has no versioning enabled; once the old file was gone, recovery required digging through Git history and Git LFS.

Three working features vanished. The hero fade, the Stripe checkout, and a deleted line all came back — a classic "deploy-and-discover" failure that should never reach production.

The Hard Rules (D1–D8): Now Enforced

To prevent this, eight non-negotiable rules are now baked into /Users/cb/Documents/repos/sites/queenofsandiego.com/CLAUDE.md, loaded on every session:

  1. D1 — Pull and Diff Before Edit: Before touching any file that targets S3, fetch the current version from prod and diff it against local. Document what has changed and why.
  2. D2 — Staging-Only, Single-Target Deploys: Each deploy command targets one environment only. Staging first; prod only after CB review.
  3. D3 — One Logical Change Per Deploy: Don't batch unrelated edits. One feature change = one deploy cycle.
  4. D4 — Obey Prior Session Warnings: If a prior session-summary flags a risk, treat it as a blocking condition. Escalate before proceeding.
  5. D5 — Snapshot Prod Before Overwrite: Since S3 versioning is not enabled on this bucket, take a manual backup by copying the current prod file to a _backups/ prefix in the same bucket before any cp or sync.
  6. D6 — Print Proof Block Before Deploy: Before executing any S3 copy, print a six-line proof block to chat showing: file path, source hash, dest hash, timestamp, feature tokens being modified, and your name. Require explicit approval (e.g., "approved for staging" or "approved for prod").
  7. D7 — Feature-Token Registry: Maintain a regex-searchable list of feature identifiers in the file (e.g., jada-crossfade, stripe-embedded-checkout, hero-ranch-coast-line). Grep the current S3 prod against local before deploy. Log any tokens that disappear.
  8. D8 — Escalate if S3 is Ahead: If the current prod file is newer than local (by modification time or hash), stop and escalate to CB. Do not overwrite.

Technical Implementation

These rules live in the QOS-specific CLAUDE.md file, which Claude loads at the start of every session for that repository. The rules are phrased as imperatives, not suggestions, and include specific command patterns.

Example pre-deploy workflow (now enforced):

# Rule D1: Pull and diff
aws s3 cp s3://queenofsandiego.com/index.html ./index.html.prod --region us-west-2
diff -u index.html.prod index.html | head -50

# Inspect feature tokens (Rule D7)
grep -E "jada-crossfade|stripe-embedded-checkout|hero-ranch-coast" index.html.prod

# Rule D6: Proof block (printed before any action)
echo "SOURCE: ./index.html (3650 lines, sha256: abc123...)"
echo "DEST: s3://queenofsandiego.com/index.html"
echo "FEATURES MODIFIED: jada-crossfade (active in local)"
echo "DEPLOY TARGET: staging only"
echo "TIME: 2025-01-15T14:32Z"
echo "OPERATOR: claude-sonnet-4-6"
# Require CB approval here

# Rule D5: Snapshot prod
aws s3 cp s3://queenofsandiego.com/index.html \
  s3://queenofsandiego.com/_backups/index.html.2025-01-15-pre-deploy \
  --region us-west-2

# Rule D2: Deploy to staging only
aws s3 cp ./index.html s3://queenofsandiego-staging.s3.us-west-2.amazonaws.com/index.html \
  --region us-west-2 --cache-control "max-age=300"

# CloudFront invalidation for staging dist
aws cloudfront create-invalidation \
  --distribution-id STAGING_DIST_ID \
  --paths "/*" \
  --region us-west-2

Why this structure: Staging gets a short TTL (300 sec); prod typically gets 3600. The snapshot goes to _backups/ with a timestamp, so if something goes wrong, CB can see what was deployed and when. Feature-token grep catches accidental deletions before they go live.

Infrastructure & File Paths

For queenofsandiego.com:

  • Prod S3 bucket: s3://queenofsandiego.com (us-west-2)
  • Staging bucket: s3://queenofsandiego-staging.s3.us-west-2.amazonaws.com
  • Prod CloudFront dist: PROD_DIST_ID (configured in Route53)
  • Staging CloudFront dist: STAGING_DIST_ID
  • Index file: /index.html (root object)
  • Backup prefix: s3://queenofsandiego.com/_backups/ (never served; for recovery only)
  • Feature tokens in index.html: Search for jada-crossfade, stripe-embedded-checkout, hero-ranch-coast-line, and any other named sections

The CLAUDE.md file itself is at /Users/cb/Documents/repos/sites/queenofsandiego.com/CLAUDE.md