Changelog
All notable changes to this project are documented here. Format follows Common Changelog. Versions are currently date-tagged (calendar releases) rather than semver — the project is pre-1.0 and pushes to production continuously.
A public-facing version of this changelog is rendered at /changelog.
2026-04-21 — security & observability hardening
Added
- Public
/changelogpage that renders this file — closes BUG_HUNT_500 #499 /api/cron/healthendpoint reports per-cron env-readiness; 503 if any required env unset — closes #436tests/integration/admin-route-auth-coverage.test.ts— two regression-locking meta-tests: every API route must (a) import an auth helper or be in the public allowlist with a written reason, (b) be wrapped inwithRequestLog/api/webhooks/resend— Svix-signed bounce/complaint receiver, persists towebhook_events— closes #443<SkipToContent>link in root layout for keyboard nav — closes #479- Per-route Cache-Control headers (admin/auth
no-store; sitemap/robots/OG long-cached) — closes #482 - 6 ADRs at
docs/decisions/documenting Tailwind-3.4.19 pin, Hostinger crontab, env-allowlist admin, Pagopar primary, defer next/image, Supabase client cache — closes #489 - Doc kit:
GLOSSARY,SALES_PLAYBOOK,DEMO_GIVEAWAY_SCRIPT,CUSTOMER_PLAYBOOK,REQUEST_LOG_AUDIT,IMAGE_OPTIMIZATION_AUDIT— closes #490, #491, #492, #493 - Runbook:
docs/runbooks/ADD_NEW_VERTICAL.md— closes #485
Fixed (security)
requireAdminUserwas accepting any signed-in Supabase user (would have let any tenant customer call any of 16+ commerce admin routes). Now also requiresisAdminEmailper ADR 0003/api/leads/bulk-updateaccepted any signed-in Supabase user → nowcheckAdmin()only/api/admin/daily-metricsGET+POST had a fake auth check that accepted any literalBearer foo→ nowcheckAdmin()/api/analytics/trackGET had the same fake-Bearer pattern → nowcheckAdmin()/api/leads/[id]/noteswas completely unauthenticated; replaced hardcodedcreatedBy: 'admin'with the authenticated user's email/api/reminders(GET/POST/PATCH/DELETE) was completely unauthenticated → nowcheckAdmin()on every method/api/outreach/trackwas completely unauthenticated; allowed flipping any lead'sstatustocontactedby knowing its UUID → nowcheckAdmin()/api/leads/[id]/generate-previewwas completely unauthenticated; allowed filesystem writes tosites/preview-<id>/+ lead-status mutation → nowcheckAdmin()
Fixed (perf + correctness)
/api/analytics/trackSupabase client memoized at module scope (was created fresh per request — cold-start cause); operator-precedence bug inhashIpsalt fix — closes #423- Image audit +
<img>lazy/async — closes #471 - Removed unused
fonts.googleapis.compreconnect — closes #472 - Cron timezone clarity: every cron route header declares schedule in UTC + Asunción mapping — closes #457
- Typecheck
npx tsc --noEmitexits 0 — closes #381–#385 /api/admin/leadsfilter-options swallowed Supabase errors silently → explicitlogger.warn— closes #388- Lint cleanup: dead
eslint-disabledirectives + unusedgenerateCacheKey+POOL_CONFIG— closes #393
Changed
- README API route count
21 → 52, section count83 → 82; added pointers to all runbooks — closes #483 react/jsx-no-target-blankenforced as ERROR in eslint — closes #414LOG_LEVELandLOG_FORMATenv vars documented in ENV_VARS.md — closes #391RESEND_WEBHOOK_SECRETenv var documented in ENV_VARS.md- All 5 cron routes wrapped in
withRequestLogand audit-doc tracked — closes #392
Tests
- 9 cron tests for
leads-digest+sitemap-ping— closes #475 - 6 tests for
/api/analytics/track(incl. "client cached once across N requests") - 6 tests for
/api/webhooks/resendSvix verification - 4 tests for
/api/cron/health - 2 meta-tests for auth + request-log coverage
2026-04-20
Added
- Canonical doc set:
README.md,ARCHITECTURE.md,CONTRIBUTING.mdat repo root,docs/README.mdas hub, anddocs/reference/{SECTIONS,BUSINESS_TYPES,API,TENANTS,TOKENS,ENV_VARS}.mdas lookup catalogs (PRs #41, #45) docs/how-to/deploy.md— canonical deploy guide consolidating 4 legacy docs (PR #43)docs/how-to/generate-images.md— canonical image pipeline consolidating 4 legacy GEMINI/IMAGES docs (PR #46)docs/observability/{README,logging,tracing,metrics}.md— per-signal observability docs (PR #44)- 83-section component library: PR #34 added 22 reusable sections; PR #40 added 4 sushi-menu sections ported from legacy
mainbranch - 21-route API surface: PR #37 added integration webhooks (Calendly, HubSpot, Mailchimp, WhatsApp), customer-portal self-serve (subscriptions pause/skip/preferences), properties catalog, GDPR data-request handler
- Locale-prefixed tenant routing
/s/[locale]/[siteSlug]/+ 4 real tenants (nexaparaguay,dayah-litworks,de-abasto-a-casa,nexa-propiedades) (PR #38) - SEO JSON-LD builders, perf budget, Lighthouse CI, Google Fonts URL utility (PR #35)
- Compliance templates (PY privacy, AML-Nexa, INAN-food, ToS, cookie classification) + legal-review-gate script (PR #36)
- Ops scripts: a11y-audit, audit-duplicates, cli-ops, migrate-demo-to-site, new-tenant, tenant-health (PR #39)
- Business model + AI workflow documentation (7 + 3 files, PR #33)
Changed
- Single trunk: unified
mainandMainbranches.main(stale lowercase) is deleted;Mainis the sole canonical trunk (PR #40 ported the 5 unique filesmainhad) - CSP: removed duplicate CSP in
middleware.tsthat was blocking Google Fonts on tenant sites; canonical CSP lives innext.config.mjs → headers()(PR #32) - Docs organisation: adopted Diataxis framework + matklad ARCHITECTURE.md convention.
docs/now structured asreference/,how-to/,observability/,archive/. Legacy numbered folders preserved until the consolidation plan completes.
Moved / Archived
- 15 status/completion reports →
docs/archive/2026-04/(PR #42) - 4 legacy deploy docs (
QUICK_DEPLOY,CLOUDFLARE_DEPLOY,HOSTINGER_CLOUDFLARE_SETUP,docs/DEPLOYMENT) → archive + replaced bydocs/how-to/deploy.md(PR #43) - 4 legacy image-gen docs → archive + replaced by
docs/how-to/generate-images.md(PR #46) - Monolithic
docs/OBSERVABILITY.md→ archive + split into per-signal files (PR #44) - Questionnaire duplicates (
BUSINESS_MODEL_QUESTIONNAIRE.md,BUSINESS_MODEL_SIMPLE.md) → archive;ENHANCEDvariant renamed as canonical (PR #47) - 4 Laura-egg-farm client docs →
docs/archive/2026-04/clients/laura/(pending tenant creation) (PR #47) - 15
.firecrawl/research scrapes →docs/archive/2026-04/firecrawl-scrapes/(PR #47)
Security
- Discovered 2 Supabase publishable keys hard-coded in archived deploy docs (
okddppczckbjdotrxiev+qyvokpribmbrosafntqa). Publishable keys are client-exposed by design (RLS enforces at DB), so risk is low — but rotating the key is recommended as a hygiene step if either project is still active. See PR #43 description.
Prior history (pre-changelog)
Before 2026-04-20 the project tracked work via status-report markdown files at repo root (IMPLEMENTATION_COMPLETE.md, TRANSFORMATION_SUMMARY.md, WINS_76_100_SUMMARY.md, etc.). Those are archived under docs/archive/2026-04/ for historical audit. From this changelog onward, user-visible changes belong here.
For the full commit-level history, see git log. For merged PR descriptions, see the GitHub PR list.
Maintenance
- Add entries at the top under a new date heading (or version heading once we tag releases).
- Group entries as: Added · Changed · Deprecated · Removed · Fixed · Security · Moved/Archived.
- Link PRs with
(PR #N). - Be specific: "added" beats "implemented"; mention the user-visible outcome.
- Keep entries terse — one sentence each where possible.