Release Notes
What’s new, fixed, and changed in LawnLedger. Versions follow semantic versioning (major.minor.patch).
Releases ship to production automatically after PRs merge to main. The version number bumps with each significant batch of changes.
v3.2.0 — 2026-05-10
A focused day of production-audit followups, render polish, and data-integrity work. No breaking changes.
New
- Customer + property entity decode helpers (
customerName,decodeText) applied across all 18 PDF preset renderers — fixes “null” appearing on entity-customer PDFs and&showing literally in property labels. - One-shot DB backfill script (
backend/scripts/backfill-html-entities.ts) to clean up any existing&literals in plaintext fields. Idempotent, dry-run by default. skipAuthRedirectoption on the API client — for soft auth checks like the token-based portal page that shouldn’t bounce visitors to/loginwhen they aren’t signed in.- Decoupled estimate notes/terms parity queued for next release — see handoff for details.
Fixed
- Dashboard customer count no longer includes soft-deleted records.
- Customer GET/PATCH/DELETE by-id no longer returns soft-deleted records.
- Portal user data no longer mirrored into
localStorage— removes an XSS exposure surface and avoids a stale auth signal after session expiry. - TeamAccessSettings v2 tabs now lazy-load — drops a 604 KB chunk from the initial Settings load.
- Webhook fan-out failures were silently swallowed; now logged with full context.
- Payment recording and refund flows now use
Serializabletransaction isolation — eliminates phantom-read race conditions that could over-pay or over-refund. - Dashboard query errors now capture to Sentry before swallowing — graceful “no data” UI preserved but prod outages are visible.
Schermerhorn & Co.in form pickers no longer renders asSchermerhorn & Co.—decodeEntitiesapplied at every customer/property display site.- Property picker label no longer doubles when the operator typed the address into the property name (e.g.
"1618 Sheridan Rd Condo"doesn’t show" - 1618 Sheridan Rd"appended). sanitizePlainTextnow decodes safe HTML entities after sanitize-html runs, so new writes don’t leave&literals in the DB.
Changed
- Contract form styles seeder no longer creates the 6 default visual presets for CONTRACT-type — contracts inherit the INVOICE default style automatically. New orgs see an empty “Contract form styles” tab; operators who want contract-specific chrome can create one explicitly.
- Login error capture: bare
catch {}replaced withSentry.captureExceptionso network errors, CORS issues, and Better Auth client throws now report. - Geocoding (
/lib/geocoding.ts) returns friendly messages on Nominatim 429 / 5xx instead of crashing JSON.parse on the HTML error page. - Customer substring search raised cap from 500 → 2000 rows with a
truncated: trueflag surfaced when older customers may be missing from results.
Security
- Middleware ordering in
backend/src/index.tsfixed sorateLimit("api")runs beforeautoSanitizeMiddleware. - Encryption key validation deferred to first use (no eager startup crash on malformed keys).
- ESLint rule added: raw
fetchis now banned inwebapp/src/pages/**andwebapp/src/components/**. Only@/lib/apican call the backend, with one legitimateeslint-disablefor an in-memorydata:URL conversion.
v3.1.0 — 2026-05-09
Twilio compliance, invitation flows, and audit hardening.
New
- SMS-AUTO invitation delivery with Twilio compliance prefix (
From LawnLedger:), STOP/HELP/START keyword handling on inbound webhooks, and SmsOptOut model. - Per-org invitation rate limits: 10/hour and 50/day, sliding-window counters.
- Username plugin (Better Auth v1.5.5) added —
User.usernameanddisplayUsernamefields, login-by-username flow.
Fixed
- Customer detail page no longer doubles entity names (“Chicagoland Community Management Chicagoland Community Management” → “Chicagoland Community Management”).
- Sidebar icons no longer clipped on collapsed sidebar — three sidebars (App, Platform, Notion) all patched.
- Bulk import path hardened: chunked transactions, real error messages surfaced, numeric Excel cells coerced to strings before
.trim(). - Property-import emailHash lookup — customers were no longer “not found” when emails contained encrypted PII.
Changed
- First name mandatory, last name optional on customer schema — better fit for entity customers (PMs, businesses, associations).
- Email mandatory, phone optional — matched the brother’s QuickBooks data shape.
v3.0.0 — 2026-03-18
Major release: PDF attachments on emails, portal authentication overhaul, equipment status tracking, crew schedule view, responsive dialogs, circuit-breaker pattern on external API calls, invoice property selector.
Older
See the GitHub Releases page for the full version history.
Last updated on