Preventing Deployment Regressions: Hard Rules for S3-Backed Static Sites
Last session, a stale local index.html deployed to S3 production wiped three working features on queenofsandiego.com: the JADA→BOOK NOW hero crossfade, the Stripe embedded checkout flow, and accidentally resurrected a deleted "Ranch & Coast" hero line. The root cause wasn't a code logic error—it was a deployment workflow violation. This post documents the hard rules we've now embedded to prevent that class of regression.
What Happened
The deployment process violated two critical invariants:
- No pull-and-diff before edit: Local
index.htmlwas ~20 commits stale. S3 production had newer hero HTML. The deployer pushed local without checking what S3 actually held. - Staging and prod deployed in one command: Both targets received the stale file in a single
aws s3 cp. No staging gate to catch the regression before prod impact.
The session notes warned about stale local files. That warning was ignored.
Technical Details: The Eight Hard Rules
We've formalized eight deployment rules into /Users/cb/Documents/repos/sites/queenofsandiego.com/CLAUDE.md, auto-loaded on every session. Here's the logic:
Rule D1: Always Pull S3 State Before Editing
# Pull current prod index.html to local snapshot
aws s3 cp s3://queenofsandiego-prod/index.html ./index.html.prod-snapshot
# Diff against your local working version
diff -u index.html.prod-snapshot index.html
Why: S3 is the source of truth for production. Local files can lag 20+ commits behind. A diff reveals what you'd overwrite before you deploy.
Rule D2: Staging-Only, Single-Target Deploys
Never deploy staging and prod in one command. Deploy to staging.queenofsandiego.com first, wait for manual approval, then promote. This forces a human gate.
# ✓ Correct: staging only
aws s3 cp index.html s3://queenofsandiego-staging/index.html --cache-control "no-cache"
# ✗ Wrong: staging + prod in one sync
aws s3 sync . s3://queenofsandiego-prod/ --exclude "*" --include "index.html"
Rule D3: One Logical Change Per Deploy
Each deploy should address one feature or bugfix. If you're updating the hero section, don't also touch the booking form. This makes rollback surgical and blame clear.
Rule D4: Obey Your Own Prior Session Warnings
If your previous session summary says "local files are stale" or "check S3 state," treat that as a blocker. That note came from your own analysis—it's a flag, not a suggestion.
Rule D5: Snapshot Production Before Overwrite
S3 versioning is disabled on queenofsandiego-prod. Before any cp that overwrites, save the current version locally:
aws s3 cp s3://queenofsandiego-prod/index.html ./backups/index.html.$(date +%s)
Rule D6: Print a Six-Line Proof Block Before Deploy
Before running aws s3 cp, print:
SOURCE: ./index.html
TARGET: s3://queenofsandiego-prod/index.html
SIZE: 3650 lines
CHANGES: (3-line summary of what changed)
TESTED: staging.queenofsandiego.com verified
APPROVAL: (CB sign-off or auto-gate reason)
This forces explicit acknowledgment. If you can't fill the proof block honestly, don't deploy.
Rule D7: Feature-Token Registry
Maintain a registry of feature tokens in S3 HTML. For queenofsandiego.com:
Before deploying, grep your local index.html against the registry. If a token is missing, you're deploying a regression.
Rule D8: Escalate to CB if S3 Is Ahead
If aws s3 cp s3://queenofsandiego-prod/index.html . then diff shows S3 has code you don't have locally, stop. Message CB with the diff and ask before proceeding.
Infrastructure Context
queenofsandiego.com's deployment chain:
- S3 buckets:
queenofsandiego-staging(CloudFront distD...),queenofsandiego-prod(CloudFront distD...) - DNS: Route53 zone;
staging.queenofsandiego.comandqueenofsandiego.comalias to respective CloudFront distributions - Cache invalidation: After any S3 deploy, invalidate CloudFront:
aws cloudfront create-invalidation --distribution-id D... --paths "/*" - Versioning: Disabled on both buckets (cost/compliance decision). Backups are manual.
Key Decision: Why These Rules Live in CLAUDE.md
Rules live in /Users/cb/Documents/repos/sites/queenofsandiego.com/CLAUDE.md so they auto-load at the top of every Claude session working on queenofsandiego.com. They're not in git (no runtime enforcement) because they're workflow, not code. But they're in the repo so they version-control alongside the site itself.
A condensed version also sits in the top-level /Users/cb/Documents/repos/CLAUDE.md so other sites (sailjada.com, 86from.com) benefit from the pattern without needing site-specific rules yet.
What's Next
The eight rules prevent the specific regression class (stale local + multi-target deploy). They don't prevent all deployment bugs. Future hardening could include:
- Automated feature-token grep as a pre-deploy check (shell script, not human memory)
- S3 versioning on prod (cost trade-off worth revisiting)
- Staging smoke tests that verify hero fade, Stripe checkout, and nav CTA exist and render
- Git hooks that lint the proof block format before allowing
git push
For now, the rules are manual discipline. They work only if obeyed. The cost of violation is visible: three features regressed to an older state, requiring manual restore.
Deploy checklist (TL;DR): Pull S3 state. Diff locally. Edit locally. Deploy staging only. Wait for approval