The build was gated on `git diff BEFORE AFTER`, where BEFORE was HEAD
before checkout. Re-deploying the same ref (or re-running after an
aborted deploy) made BEFORE == AFTER, so the diff was empty and the SPA
build silently skipped — shipping stale assets while migrations still ran.
- Always rebuild the SPA; only gate the heavy dep installs.
- npm ci / composer install also run when node_modules / vendor are missing.
- Track the last successfully deployed commit in .deploy-last-commit and
diff against that, so an aborted run never advances the baseline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add .DS_Store to .gitignore and untrack the two committed copies
(.DS_Store at the root and .claude/.DS_Store). macOS noise that
shouldn't have been versioned in the first place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the agreed design for Stripe webhook handling, 5-day grace
period with branded day-3/day-5 reminders, and Stripe Customer Portal
as the single subscription-management surface. Updates payments rules
to match and ignores .worktrees/ for isolated implementation work.