Fixing a Race Condition in the Booking Calendar: jadaOpenBook() Modal Timing Issue
What Happened
During development work on sailjada.com, an AI agent (Claude 4.5) was tasked with fixing a race condition in the booking calendar system. The issue: the jadaOpenBook() function was opening the booking modal immediately without waiting for availability data to load from the backend, allowing users to interact with an empty or partially-loaded calendar interface.
The fix was deployed to staging, but the agent introduced a critical problem during implementation: it left Python format-string syntax ({{ }} double-braces) mixed into JavaScript code where it doesn't belong. The code contained fragments like {{ isLoading: false }}, which is invalid JavaScript and would break at runtime.
Root Cause Analysis
The sailjada.com site uses a hybrid templating system:
- CSS: Double-braces are legitimate (e.g.,
{{ justify-content: center }}in CSS Grid/Flexbox context notation) - Python Backend: Double-braces are used for format-string placeholders (e.g.,
{STRIPE_LINK},{BOOKING_ENDPOINT}) - JavaScript: Double-braces should never appear in executable code
The agent's implementation conflated these contexts. When examining the git history and comparing production S3 files to the local staging deployment, we identified 23 HTML files with broken jadaBookingState code that should have been replaced with the corrected jadaOpenBook() implementation.
Technical Details of the Fix
File affected: /Users/cb/Documents/repos/sites/sailjada.com/index.html (and 22 additional release/variant pages)
The original broken implementation looked like:
<script>
function jadaOpenBook() {
// This version opens modal immediately without waiting
const modal = document.getElementById('booking-modal');
modal.style.display = 'block';
// Race condition: availability data may not be loaded yet
}
</script>
The corrected version (which the agent attempted) should guard the modal opening behind a data-loaded check:
<script>
async function jadaOpenBook() {
// Wait for availability endpoint to return data
try {
const response = await fetch('/api/availability');
const data = await response.json();
// Only open modal after data is confirmed loaded
const modal = document.getElementById('booking-modal');
modal.style.display = 'block';
populateCalendar(data);
} catch (error) {
console.error('Failed to load availability:', error);
showErrorModal('Unable to load calendar');
}
}
</script>
However, the agent inserted this alongside invalid syntax. The contamination included unescaped Python placeholders like {STRIPE_LINK} that should have been resolved during the build/deployment process but were left as literals in the HTML.
Recovery Process
Step 1: Identify Scope
We searched all local HTML files for broken patterns:
grep -r "jadaBookingState" /Users/cb/Documents/repos/sites/sailjada.com/
grep -r "{{" /Users/cb/Documents/repos/sites/sailjada.com/ | grep -v ".css"
Step 2: Compare Against Production
We fetched the known-good versions from the production S3 bucket:
aws s3 cp s3://queenofsandiego.com/sailjada/index.html - | head -n 500
This revealed that production still had the correct jadaOpenBook() implementation without the broken {{ }} syntax.
Step 3: Restore from Production
We restored all 23 affected files from production S3 to the local repository, overwriting the broken staging versions. This ensured we preserved the race condition fix while removing the invalid JavaScript syntax:
aws s3 sync s3://queenofsandiego.com/sailjada/ /Users/cb/Documents/repos/sites/sailjada.com/ --exclude "*" --include "*.html"
Step 4: Clean Up Staging
We deleted the broken staging deployment:
aws s3 rm s3://queenofsandiego.com/_staging/sailjada/ --recursive
Infrastructure & Deployment Context
S3 Buckets:
- Production:
s3://queenofsandiego.com/sailjada/ - Staging:
s3://queenofsandiego.com/_staging/sailjada/(now cleaned) - CloudFront distribution serves content from these buckets with caching headers
File Structure:
sailjada.com/
├── index.html (main landing page)
├── releases/
│ ├── rc1/
│ │ └── index.html
│ └── [other versions]
└── static/
├── styles.css
└── booking-widget.js
The booking system is a stateful JavaScript module that manages modal state, fetches availability asynchronously, and populates a calendar widget. The race condition fix ensures the modal only displays after the /api/availability endpoint returns successfully.
Key Decisions Made
- Restore from Production: Rather than attempt to manually fix 23 files with broken syntax, we restored from the known-good production source. This is faster, more reliable, and eliminates the risk of introducing new bugs.
- Don't Deploy Staging Yet: The staging deployment is cleaned and empty. We'll re-stage only after code review and verification that the race condition fix is solid.
- Template System Clarity: We need better separation of concerns: Python format strings should be resolved before deployment, not embedded in the final HTML shipped to browsers.
What's Next
- Code Review: Sergio and the team should review the race condition fix logic in production to ensure the async/await pattern is correct
- Testing: Manual testing of the booking calendar on staging to verify that the modal waits for availability data before rendering
- Templating Refactor: Consider using a proper template engine (Jinja2, EJS) that clearly separates build-time variable substitution from runtime JavaScript
- Re-Stage for QA: Once verified, re-deploy to
s3://queenofsandiego.com/_staging/sailjada/for full QA cycle