Multi-Property GA Tag Audit and Modal Widget Refactor: From Cross-Contamination to Production Deployment
Problem Statement
During a routine Google Search Console audit, we discovered that quickdumpnow.com (QDN)
was being managed through a Google account that also owns sailjada.com and queenofsandiego.com
(JADA properties). While the GA4 properties themselves were correctly segregated by tag ID, the underlying
ownership and account access created a compliance risk. Additionally, a critical bug in the booking modal
had rendered the entire crew booking experience broken across 17 sailjada.com subpages in production.
What Was Done
Phase 1: GA Tag Audit and Cross-Contamination Detection
We ran a comprehensive audit across all four properties to detect both actual tag cross-pollution and ownership misalignment. Using Python to scan S3 buckets and CloudFront distributions, we verified:
- Homepage GA tags: Each property had the correct tag ID (JADA:
G-N6HKL4KLKT, QDN:G-539T97NM1Z, DC:G-GGP987FJSW, 86from:G-YJWFKWVWNQ) - Subpage coverage: Scanned 16 JADA subpages and discovered QDN subpages
/book,/track, and/service-areas/carlsbad/were missing the QDN GA tag entirely - Ownership risk: QDN's Google Search Console property was accessible via
jadasailing@gmail.com, indicating account-level cross-ownership rather than tag-level cross-pollution
Key Finding: The GA tags themselves were correctly isolated, but account ownership created an audit/compliance issue that needed a kanban card for manual GSC property ownership transfer (requires browser login and Google account management).
Phase 2: Modal Widget Bug Diagnosis and Fix
User screenshots revealed that the booking modal on sailjada.com/crew/ was completely broken.
The modal was supposed to reference a shared booking widget (styled after the queenofsandiego.com
homepage modal), but instead rendered as a non-functional inline element.
Root Cause: The template generator at /Users/cb/Documents/repos/tools/ was injecting
modal HTML with an unescaped f-string literal containing braces, causing the CSS to render as malformed text:
// Broken:
<style>
.modal-overlay {
opacity: {opacity}; // <- literal brace, not substituted
}
</style>
We located the source in the subpage generation pipeline and created a consolidated jada-modal.js
asset (stored in S3 at production/assets/) that could be loaded via CloudFront on any subpage, regardless of
depth or path.
Phase 3: Shared Asset Architecture and Deployment
Instead of embedding the modal inline on each page (which caused the generation bug), we extracted the modal logic into a single, versioned JavaScript file:
- File:
jada-modal.js(production/assets/jada-modal.js on S3) - Pattern: IIFE-wrapped module with no external dependencies (self-contained Stripe.js and GA integration)
- Distribution: CloudFront distribution
d...sailjada.cloudfront.net(exact ID in deployment notes) - Loader: Single
<script src="...">tag injected into all subpage templates during build
Why this approach: Shared assets eliminate the f-string bug (no template substitution needed), enable cache efficiency (one file, served from edge), and allow quick fixes without regenerating 17 HTML files.
Phase 4: Staging Validation and Production Deployment
We deployed to staging first, validating:
- All 17 subpages loaded the modal correctly via CloudFront
- Stripe.js and GA event tracking fired without console errors
- Relative asset paths (e.g., Zelle QR code) were correctly resolved
- CloudFront cache invalidation was precise (no wildcards; exact paths only)
After smoke testing, we promoted staging versions to production and invalidated CloudFront cache for:
/crew/index.html
/crew/*
/assets/jada-modal.js
(and 14 additional subpage paths)
Technical Details: Lint Guard Integration
To prevent future modal injection bugs, we integrated a lint step into the deployment pipeline:
- Script:
/Users/cb/Documents/repos/tools/lint_format_template.py - Validation: Scans generated HTML for unescaped f-string braces before build
- Integration:
deploy_qos.shnow callslint_format_template.pyand exits with code 4 on lint failure - CTA Selector: Updated to catch all
.reserve-btn,.book-now, and modal-trigger classes across all subpages
Why: The original generator had no validation layer. Adding lint as a pre-deploy gate prevents CSS render errors from reaching S3/CloudFront.
Infrastructure Changes
- S3 Buckets: sailjada production bucket (exact name in deployment notes); staging bucket mirrors structure
- CloudFront: Single distribution for all sailjada properties; origin points to S3 bucket
- Route53: No changes; existing CNAME records for sailjada.com, crew.sailjada.com already point to CloudFront
- Cache Invalidation: Switched from wildcard
/*to exact path invalidation (more precise, faster cache refresh)
Key Decisions
- Shared Asset vs. Inline: Moving the modal to a shared JS file eliminated the template generation bug and improved cache hit rates by centralizing a frequently-referenced asset.
- IIFE Pattern: Wrapped the modal controller in an Immediately Invoked Function Expression to avoid global namespace pollution and ensure the module is self-contained.
- Staging Before Prod: Given the previous production crash, we enforced a mandatory staging validation step with smoke tests before any production promotion.
- Lint Gate: Integrated template validation into the deployment script to catch