```html

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

Last week, a stale local index.html was deployed to production S3, wiping three working features on queenofsandiego.com: the JADA→BOOK NOW hero crossfade, the Stripe embedded checkout flow, and a previously-deleted hero line. The root cause wasn't a code bug—it was process. This post documents the hard rules we've added to prevent that class of regression, and why each rule matters.

What Happened

A prior session had updated s3://queenofsandiego-prod/index.html to include the Stripe checkout integration and the new hero animation. The next session pulled a stale local copy (likely from an earlier git branch), edited it for an unrelated change, and then ran:

aws s3 cp index.html s3://queenofsandiego-prod/ && \
aws s3 cp index.html s3://queenofsandiego-staging/

Both staging and prod were updated in a single command. The stale local file overwrote the newer production version. Three features vanished instantly. CloudFront cache (distribution ID ABCD1234EFGH) served the old version for ~15 minutes until invalidation.

Root Causes

  • No pre-deployment diff: The session didn't fetch the current S3 version and compare it to local before pushing.
  • Staging + prod in one command: Violates the single-responsibility principle—if one target is wrong, both break.
  • Ignored prior warnings: A previous session's summary explicitly noted "stale local files in this repo—always pull S3 first."
  • No snapshot before overwrite: S3 versioning is disabled on the prod bucket (by design, to avoid bloat). Once overwritten, the old version was gone.
  • No proof step: No explicit verification printed to chat before the cp command ran.

The Hard Rules (D1–D8)

We've codified these into /Users/cb/Documents/repos/sites/queenofsandiego.com/CLAUDE.md, which auto-loads on every QOS session:

D1: Pull S3 and Diff Before Any Edit

aws s3 cp s3://queenofsandiego-prod/index.html ./index.html.prod-current
aws s3 cp s3://queenofsandiego-staging/index.html ./index.html.staging-current
diff -u index.html.prod-current index.html

Why: Establishes ground truth. If S3 is ahead of your local copy, you know immediately. If they're identical, the diff is clean and fast. If your local file is newer, the diff shows exactly what you're about to change.

D2: Single-Target Deployments Only

Deploy to staging first, review on staging.queenofsandiego.com, then promote to prod in a separate command:

aws s3 cp index.html s3://queenofsandiego-staging/
# Review at staging.queenofsandiego.com
# Confirm with CB
aws s3 cp index.html s3://queenofsandiego-prod/

Why: One failed file to one target is containable. One command shipping bad code to both staging and prod is a site outage. Separating them enforces a review gate.

D3: One Logical Change Per Deployment

Edit and deploy one feature or fix per cycle. If you're modifying the hero section, the booking form, and the footer in one session, split them into three separate cp commands with staged reviews between each.

Why: Regression becomes surgical to diagnose. If feature A breaks after a deploy, you know which file changed and can revert just that change.

D4: Obey Your Own Prior Session Summaries

Every CLAUDE.md in the repo chain (site-specific and root level) includes session notes and warnings. Re-read them before touching infrastructure.

Why: Institutional memory. The prior session's warning about stale local files was documented but not honored. This rule makes ignoring those warnings a process violation, not a judgment call.

D5: Snapshot Prod Before Overwriting

Even though S3 versioning is off, manually copy the current prod version to a backup key before overwriting:

aws s3 cp s3://queenofsandiego-prod/index.html s3://queenofsandiego-prod/index.html.backup-$(date +%Y%m%d-%H%M%S)
aws s3 cp index.html s3://queenofsandiego-prod/

Why: 60-second recovery path if something goes wrong. Without it, you're asking CB to restore from a backup or roll back via git and redeploy—five-minute outage instead of one.

D6: Print a Six-Line Proof Block Before Every cp

Before running any S3 copy, print this to chat:

DEPLOY PROOF — index.html → staging
Local file: index.html (2.8 KB, SHA256: abc123def456...)
S3 current:  s3://queenofsandiego-staging/index.html (2.1 KB, SHA256: old789...)
Diff: hero section updated, Stripe checkout integrated
Review link: https://staging.queenofsandiego.com/
Approval: [awaiting CB confirmation]

Why: Creates an explicit moment for CB to say "wait, that's the wrong file" before the overwrite. Chat history is also an audit trail.

D7: Maintain a Feature-Token Registry

Keep a file like FEATURES.md that lists every feature and a unique grep-able token in the HTML:

## Features in index.html
- JADA Hero Fade: search for `data-feature="jada-fade"`
- Stripe Checkout: search for `stripe-embedded-checkout-form`
- Book Now Button: search for `id="hero-book-now-btn"`
- Ranch & Coast Link: REMOVED in session 2024-01-15, do not restore

Before deploy, grep the new file against this list:

grep -q 'data-feature="jada-fade"' index.html && echo "✓ JADA fade present" || echo "✗ JADA fade MISSING"
grep -q 'stripe-embedded-checkout-form' index.html && echo "✓ Stripe present" || echo "✗ Stripe MISSING"

Why: Catches accidental removals before deployment. Takes 5 seconds, saves hours of debugging.

D8: Escalate to CB If S3 Is Ahead of Local

If the diff in step D1 shows S3 has code you don't have locally, do not proceed. Message CB:

S3 prod is ahead of local by ~500 bytes. Likely from session [XYZ].
Local file is stale. Requesting git log + S3 audit before