What changed recently.
Curated, not exhaustive. Every entry below is a change to the data or product surface that affects users — new metros, new segments, accessibility fixes, classifier upgrades, API contract changes. Internal refactors, lint sweeps, and CI plumbing are filtered out so this stays useful as a signal rather than a raw commit log.
June 2026 — 1 ship
NYC commercial segments restated via PLUTO occupancy classification
DATA QUALITYNew York City commercial permits are now classified with occupancy grounded in PLUTO — the city's tax-lot land-use dataset — so commercial-versus-residential classification reflects the building's recorded occupancy rather than the permit-type string alone. The restatement is overlap-free: permits move into their occupancy-grounded commercial segments at source-metro regen, so cross-metro totals stay reconcilable. The counts on the NYC commercial segment pages reflect the restated classification; see /cohorts for how classification works.
May 2026 — 26 ships
Nav 1280px wrap fix + RFC 9457 problem-details rendering + Modal focus-trap
SITEThe top nav no longer wraps awkwardly between 1024px and 1366px. Backend error responses that conform to RFC 9457 (the problem+json standard) now render their `title` and `detail` fields verbatim instead of a generic 'Something went wrong' message. The contact/account modals now trap focus and close on Escape.
Recency placeholder copy refresh
SITEThe 520 recency landing pages had carry-over copy from the pre-OpenAPI /v1/permits framing. Rewrote the placeholder body to match the current target_segment routing and to stop overstating what's shipped on those pages today.
/sandbox CLS regression fix
SITEThe sandbox page was reporting CLS = 0.119551 since the homepage live-activity feed merge. The skeleton placeholder now matches the populated TableView dimensions exactly, so the layout no longer shifts when data arrives.
Site-build handoff docs added
DOCUMENTATIONThree handoff documents shipped to the docs site to make new-chat continuity less fragile: current state, open backlogs, and decision-points. Internal-facing, but linked from the public docs index.
/account/api-keys wired to backend /v1/keys CRUD
SITESelf-serve API-key management is now live: create, list, and revoke keys from /account/api-keys. Keys are scoped per organization and the secret is shown exactly once at creation.
Stripe webhook drives backend tier-sync
INFRASTRUCTURESuccessful Stripe checkouts now propagate to the backend tier table within the webhook handler — no manual re-sync step. Failed webhooks retry with idempotency keys so duplicate events don't double-grant entitlements.
Homepage live activity feed
SITEThe homepage now shows recent permits across all mapped metros, stratified one-per-metro for cross-metro diversity (default 12 cards). The feed reads from prebuild JSON artifacts at static-generation time; no per-request HTTP and no parallel build-time fetches.
Recency pages now use /v1/permits/target with target_segment routing
DATA QUALITYPorted the recency surfaces to the OpenAPI contract. The 10 segment recency families now query /v1/permits/target with the cohort's target_segment slug, matching the curated commercial/multifamily/ADU slice rather than the broader /v1/permits firehose.
Sandbox /v1/permits client aligned to actual API contract
DATA QUALITYThe sandbox was calling /v1/permits with the URL slug; the backend expects the API slug. Added a translation layer plus a fallback for stale audit selectors so the sandbox renders the same rows the API returns for the same query.
Navbar search bar
SITEAdded a lower-prominence search input to the top nav that routes to the matching metro or segment hub on submit. The input is collapsed on viewports under 1024px to keep the primary nav unchanged on mobile.
Performance hardening — lazy-load CRMRequestForm + stable Lighthouse signal
SITEThe contact form on /coverage is now lazy-loaded, dropping ~14kB from initial JS on every page that ships the form. Also stabilized the Lighthouse CI pre-start path so the perf signal is reproducible across runs.
NYC bespoke H2 + standard metro <title> format across 26 metros
SITENYC's bespoke H2 was visually inconsistent with the other 25 metro templates; standardized it. Also normalized the <title> tag pattern across every metro so search-result snippets read consistently.
Sandbox accessibility fixes — color contrast + target size + label
SITEThree WCAG AA violations on /sandbox cleared: a low-contrast disabled-button state, a sub-44px tap target on mobile, and a missing aria-label on the segment filter. Verified with axe-core under CI.
Unrouted-commercial coverage state added
COVERAGEAdded a fourth coverage state — 'unrouted_commercial' — for metros where the upstream portal publishes residential permits cleanly but commercial permits route through a separate system we haven't ingested yet (San Diego pattern). The /coverage table surfaces this distinctly from DEPLOY-PENDING.
Replaced vague 'soon' framing across the site
SITEEvery instance of 'coming soon' / 'shipping soon' on user-facing surfaces was rewritten to either name the specific dependency that gates the work or to mark it 'filed as backlog'. Calibrated honesty over implied roadmaps.
Restored SUSI estimate column + countProvenance attribute
DATA QUALITYThe Single-Universe Size Indicator estimate column was dropped during the segment-distribution port; restored it with a countProvenance attribute that surfaces whether a count is observed, modeled, or pending. Rendered next to every metro total so the reader knows what they're looking at.
State-specific eyebrows across hero / OG / recency surfaces
SITEMetro pages now carry the state abbreviation in the hero eyebrow, the OG image, and the recency cards. Disambiguates the four 'Portland' / two 'Columbus' / multiple 'Springfield' cases for search-result snippets and shared links.
OG total swap + segment-card ratio framing
DATA QUALITYOG image total now reflects the published silver-row count, not the bronze raw count. Segment cards on metro pages render ratios (e.g. '3.2k of 41k permits') instead of percentages, which were misleading when the denominator drifted week-over-week.
Sandbox empty-data path + graceful query errors
SITEThe sandbox now renders a documented empty-state when a filter combination matches zero rows, instead of a perpetual loading spinner. Backend query errors surface the underlying message rather than a generic toast.
Production Turnstile keys + abuse-scoped rate limiting
INFRASTRUCTURESwapped Turnstile test keys for production keys and blocked the test-key fallback in production builds. Also narrowed the middleware rate-limit from every endpoint to just the abuse-prone auth endpoints, so legitimate sandbox queries are no longer throttled.
Server-side session gate on /account and /api/keys
SITEThe /account routes now enforce the session gate server-side instead of trusting the client redirect. Closed a wrap bug where a custom Auth.js callback bypassed the framework's default redirect.
Recency pages scaffolding — 520 routes across 3 URL families
COVERAGEAdded scaffolding for recency landing pages: 520 routes covering metro × segment × time-window combinations. Currently rendering a documented placeholder with valid JSON-LD (Dataset + BreadcrumbList); the real recency-data render lands in B1.9.4.
Metro slug decoupled from API slug
SITEURL slugs and backend API slugs are now independent — the site can use shorter URL slugs ('sandiego') while the backend keeps verbose API slugs ('san-diego-ca'). Added a translation layer with explicit mapping per metro.
Preview-audit automation CLI
INFRASTRUCTUREAdded a CLI that runs the visual + a11y + Lighthouse audit suite against any Vercel preview URL on demand. Caps OG-route upstream wait at 3 seconds to avoid FUNCTION_INVOCATION_TIMEOUT on Neon cold starts.
Launch-critical polish — self-hosted Geist, dynamic OG, observability
SITESelf-hosted the Geist font family (no more Google Fonts dependency at render). Added dynamic OG image generation per metro and per segment. Wired PostHog for first-party page-view + interaction telemetry.
Tier 1 template scaled to all 25 metros + Tier 7 permit detail SSG
COVERAGEThe NYC anchor template now ships on every mapped metro (25 routes). Tier 7 individual-permit detail pages are statically generated from the silver corpus where the permit ID is stable; unstable IDs fall back to ISR.
What we don't surface here.
This changelog is the user-facing slice, not the engineering log. Operational bugfixes that no user ever noticed, internal refactors with no UX impact, docstring drift, lint sweeps, CI plumbing, and pre-launch scaffolding don't earn an entry. Backend pipeline changes that affect query results materially do earn one — under Data quality — even when no site code changed.
For the full engineering record, the canonical reference is the GitHub repo's commit history and merged-PR list. Visit permitcore-site merged PRs for site changes, or the permit-pipeline repo for upstream-data and classifier work.
Machine-readable updates.
An RSS / Atom feed at /changelog/feed.xml is filed as backlog — not yet shipped. Until then, the GitHub repo is the canonical machine-readable signal: subscribe to releases or watch the repo for merged-PR notifications. We'll mirror those into a proper feed once the cadence justifies it.
If you're building an integration that depends on changelog awareness — webhook on new entry, polling endpoint, etc. — the contact path runs through /coverage. Naming a specific use case bumps it on the backlog.