Saltar al contenido principal

Engineering velocity, in público

Changelog

Cada cambio relevante a la plataforma. Actualizado continuamente. Para el detalle a nivel commit, mirá la lista de PRs en GitHub.

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 /changelog page that renders this file — closes BUG_HUNT_500 #499
  • /api/cron/health endpoint reports per-cron env-readiness; 503 if any required env unset — closes #436
  • tests/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 in withRequestLog
  • /api/webhooks/resend — Svix-signed bounce/complaint receiver, persists to webhook_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)

  • requireAdminUser was accepting any signed-in Supabase user (would have let any tenant customer call any of 16+ commerce admin routes). Now also requires isAdminEmail per ADR 0003
  • /api/leads/bulk-update accepted any signed-in Supabase user → now checkAdmin() only
  • /api/admin/daily-metrics GET+POST had a fake auth check that accepted any literal Bearer foo → now checkAdmin()
  • /api/analytics/track GET had the same fake-Bearer pattern → now checkAdmin()
  • /api/leads/[id]/notes was completely unauthenticated; replaced hardcoded createdBy: 'admin' with the authenticated user's email
  • /api/reminders (GET/POST/PATCH/DELETE) was completely unauthenticated → now checkAdmin() on every method
  • /api/outreach/track was completely unauthenticated; allowed flipping any lead's status to contacted by knowing its UUID → now checkAdmin()
  • /api/leads/[id]/generate-preview was completely unauthenticated; allowed filesystem writes to sites/preview-<id>/ + lead-status mutation → now checkAdmin()

Fixed (perf + correctness)

  • /api/analytics/track Supabase client memoized at module scope (was created fresh per request — cold-start cause); operator-precedence bug in hashIp salt fix — closes #423
  • Image audit + <img> lazy/async — closes #471
  • Removed unused fonts.googleapis.com preconnect — closes #472
  • Cron timezone clarity: every cron route header declares schedule in UTC + Asunción mapping — closes #457
  • Typecheck npx tsc --noEmit exits 0 — closes #381–#385
  • /api/admin/leads filter-options swallowed Supabase errors silently → explicit logger.warn — closes #388
  • Lint cleanup: dead eslint-disable directives + unused generateCacheKey + POOL_CONFIG — closes #393

Changed

  • README API route count 21 → 52, section count 83 → 82; added pointers to all runbooks — closes #483
  • react/jsx-no-target-blank enforced as ERROR in eslint — closes #414
  • LOG_LEVEL and LOG_FORMAT env vars documented in ENV_VARS.md — closes #391
  • RESEND_WEBHOOK_SECRET env var documented in ENV_VARS.md
  • All 5 cron routes wrapped in withRequestLog and 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/resend Svix 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.md at repo root, docs/README.md as hub, and docs/reference/{SECTIONS,BUSINESS_TYPES,API,TENANTS,TOKENS,ENV_VARS}.md as 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 main branch
  • 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 main and Main branches. main (stale lowercase) is deleted; Main is the sole canonical trunk (PR #40 ported the 5 unique files main had)
  • CSP: removed duplicate CSP in middleware.ts that was blocking Google Fonts on tenant sites; canonical CSP lives in next.config.mjs → headers() (PR #32)
  • Docs organisation: adopted Diataxis framework + matklad ARCHITECTURE.md convention. docs/ now structured as reference/, 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 by docs/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; ENHANCED variant 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.

¿Algo no anda?

Escribinos por WhatsApp y lo mirames. Volver al inicio →