Product Changelog

A running history of Clean Estimate Pro product updates, fixes, launches, and release work.

BeginnerownermanageradminUpdated 2026-04-28

Product Changelog

This page is the running release history for Clean Estimate Pro. It is built from the cleanestimatepro git history and captures shipped product work across features, fixes, hardening, and rollout changes.

This log currently covers releases from 2026-03-06 through 2026-04-28. Merge-only commits, docs-only mirror commits, and housekeeping-only commits are intentionally left out so the list stays focused on product changes.

Going forward, every production-facing change should add an entry here before the work is considered done.

2026-04-28

  • Hardened the public health check after the page-speed stress run showed Supabase-backed probes degrading while public pages stayed fast. The health endpoint now aborts its Supabase reachability query at the timeout boundary so repeated uptime checks do not leave extra database requests running during a Supabase incident.
  • Followed up on the page-speed audit with a smaller Clients loading path and lighter telemetry ingest. The Clients workspace now opens its page shell without waiting on the full account tree API during server navigation, then hydrates rows through the existing client-side loader, and route telemetry rate limiting no longer spends an extra database RPC before writing sampled timings.
  • Started the admin usage-reduction pass for the highest-traffic workspaces. Dashboard metrics keep exact visible totals while caching the heavy chart RPC, Estimates opens without server-side self-fetches and keeps exact workbench counts with cached summary metadata, Schedule caches its reference data briefly, and admin route telemetry samples more heavily by default so real logged-in slow pages show up faster.
  • Extended the exact-number guarantee across admin dashboards and reports. Revenue, pipeline, schedule, clients, commissions, crew pay, franchise, retention, operations, lead-source, holiday-lights, sales-cycle, geography, and margin reporting now avoid hidden row caps, display money to the cent, and fail visibly when a report source cannot be loaded instead of showing guessed or partial totals.
  • Tightened Revenue Analytics so money reports stay exact instead of fast-but-approximate. Revenue rollups now page through every matching accepted record, include fleet/building/holiday lights in the time series, sum in cents, fail closed on query errors, and display penny-level totals on the revenue report.

2026-04-27

  • Reduced shared admin-page load pressure after Schedule and Clients were still taking several seconds to open. Admin org resolution now skips unnecessary franchise descendant lookups, sidebar gamification no longer performs setup/write work during normal navigation, admin API middleware rate limiting no longer spends a database RPC on protected office reads, and the Clients list avoids unused count and all-org health work where it can.
  • Fixed an admin alert feedback loop that could make the logged-in app feel slow. The alert bell now reads without regenerating managed alerts, dashboard alert generation is throttled, unchanged alert rows are no longer rewritten, realtime only refreshes on new alerts, and slow alert fetches stop instead of stacking in the browser.
  • Hardened the East-backed login and worker recovery path. Password sign-in now surfaces a clear auth-service timeout instead of spinning forever, minute-by-minute background workers have incident kill switches and lower default concurrency, super-admin pages stay dynamic during deployment, and the public ingress rate limiter now fails closed quickly when a hot key is locked instead of holding database workers until gateway timeout.
  • Expanded the incident scheduler pause so DISABLE_CRON_JOBS=true now stops follow-ups, drip campaigns, proposal expiration, automation waiting runs, scheduled automation triggers, franchise summary refreshes, background jobs, and webhook delivery before those routes open Supabase connections. This gives the team a safer recovery path when Supabase warns that the project is near Disk IO Budget exhaustion.
  • Reorganized the admin sidebar around daily office work. The top group now reads Daily work and pins Command Center, Inbox, Estimates, Schedule, and Jobs, while Pipeline and AI Hub move into the growth/admin area so the left rail matches the actual day-to-day workflow more closely.
  • Updated the owner dashboard into a more action-first Command Center. The page now opens with live office signal pills and a full-width Daily admin queue before reports, charts, and secondary shortcuts, keeping follow-ups, replies, and handoffs above analytics.
  • Refocused the admin Estimates page into an Estimate workbench with priority cards for drafts, viewed follow-ups, unassigned estimates, and accepted job handoffs, plus row-level Next action controls for the most common send, follow-up, job, payment, schedule, review, and detail workflows.

2026-04-26

  • Added the Phase 0 production telemetry baseline for scaling work. CE Pro now samples production route and API timing, exposes service-role-only exports for top route latency, pg_stat_statements, and Supabase connection snapshots, and documents the stop condition, business-load target prerequisite, kill switches, and MRR-based cost gates before any speculative scale phases continue.
  • Moved commercial fleet and building proposal delivery onto the background job queue. The send dialog now acknowledges a queued delivery immediately while the worker renders the proposal PDF, sends email/SMS, retries transient failures, and records final delivery state in the background; COMMERCIAL_PROPOSAL_SEND_QUEUE_DISABLED=true rolls the path back to synchronous delivery.

2026-04-25

  • Added docs-site discovery surfaces for docs.cleanestimate.pro. The help center now publishes a generated /sitemap.xml, /site-map, /llms.txt, /llms-full.txt, and /robots.txt, all sourced from the MDX docs catalog so search engines and AI ingestion tools can find the current documentation.
  • Connected the mobile CRM and residential estimate handoff to live Clean Estimate data. Signed-in mobile users now load, create, and update customers through authenticated live client APIs, client/lead New estimate actions carry CRM context into the builder, and mobile residential address entry now uses the same property lookup service as the web estimator to prefill square footage, stories, lot size, and year built.

2026-04-24

  • Repaired live customer-portal Stripe invoice payments after QA found Checkout sessions could open but paid portal invoices stayed pending. The production app URL environment value was corrected, and the central Stripe webhook now recognizes portal invoice Checkout metadata so successful test-mode payments mark the portal invoice paid while declined cards remain unpaid.
  • Added Red Line pricing to the mobile residential sales flow. Eligible sales reps now see per-service floors, status, and visible commission in the mobile pricing/review steps, below-floor mobile edits are clamped or blocked before save/send, mobile pricing settings show the active Red Line model, and queued mobile residential proposals now carry the Red Line snapshot for audit.
  • Hardened the Android mobile sales QA blockers found in the live app pass. Mobile API calls now default to app.cleanestimate.pro, mobile-created customers show back up in CRM search and detail, proposal-client records no longer dead-end the client page, commercial building proposals cannot advance to final review with missing client/property/service/pricing/frequency data, modal headers respect safe areas, and Android Back now asks before discarding unsaved lead or customer details.
  • Restored self-serve workspace signup so new owners can create an account, create their organization, receive starter setup data, start a 44-day trial, sign in, and land in onboarding from /signup. The public CTAs now point at the trial signup path, stale waitlist signup blocks were removed from the public marketing surface, and the new regression test covers owner creation, org creation, membership creation, bootstrap seeding, duplicate-workspace blocking, and rollback on partial signup failure.
  • Hardened the first-run onboarding finish path. Service toggles now save in one parallel pass instead of holding the setup flow through a long sequence of individual saves, and the final dashboard handoff now shows an Open Dashboard fallback link if the workspace completion save succeeds but the admin dashboard is slow to render.
  • Added a read-only browser QA script for the super-admin portal and used it against the live site. The pass now checks the signed-out redirect guard, super-admin magic-link sign-in, Dashboard, Organizations, organization detail, Users, Waitlist, Activity, and empty-result filters, while the underlying fixes stabilize super-admin table hydration and allow the deployed Google Analytics collect endpoint through CSP so real portal failures are easier to spot.
  • Added a live browser QA script for self-serve signup and onboarding. The pass creates a fresh AgentMail inbox, signs up a new owner/org, completes onboarding, verifies the database state, confirms password login, and opens a real delivered access-link email; the dashboard Lead Sources shortcut now points at the live Lead Source ROI report instead of a missing analytics route.

2026-04-23

  • Added a safe dual telecom provider path for Phone / Voice. Workspaces can now choose Esendex or Twilio, provider-specific settings and token-protected webhook URLs are shown in Settings, outbound SMS/manual messaging/ETA texts/automation calls use the active provider, and legacy Twilio workspaces keep Twilio validation and callback behavior. Provider switches also fail safe by turning voice off until the selected provider has a ready number and forwarding setup.
  • Tightened the live QA accessibility blockers found in the admin and customer estimator flows. Icon-only alert, table action, route, and schedule controls now have screen-reader names, admin and estimator filters expose clear select labels, customer/property inputs are associated with labels, XP progress bars are named, dark-sidebar contrast is stronger, login/signup paragraph links no longer rely on color alone, the customer portal footer has stronger contrast, and the admin theme toggle no longer risks a hydration mismatch during page load.
  • Fixed the customer magic-link login page on app.cleanestimate.pro/customer/login. Signed-out customers can now open the login form directly instead of getting caught in a redirect loop from the protected customer portal shell.

2026-04-22

  • Cleaned up the main admin dashboard top section so the report shortcuts now live inline beneath the office snapshot instead of as a separate skinny right-side rail. The awkward Open the next read panel copy is gone, the new section simply reads Top reports, and the dashboard header area is easier to scan at laptop widths.
  • Tightened the live portal referral gate and the public discovery surfaces after the latest production QA pass. Unknown emails on the portal referral tab are blocked again instead of falling into the referral-code enrollment step, known customers without a saved code can still create one, and the asphalt-maintenance module is once again listed across /site-map, /sitemap.xml, and /llms-full.txt.

2026-04-21

  • Repaired another live Rehash popup failure after reproducing the exact broken estimate against the real workspace schema. The detail drawer and inline Rehash quote-save path now tolerate older workspaces that are still missing legacy estimate pricing columns like minimum_adjustment and taxable_amount instead of crashing the popup with a generic request failure.
  • Expanded the Rehash detail drawer into a true quote-editing popout. Managers can now see each residential service's list price, current quoted price, and saved Red Line floor side by side, adjust quoted service prices inline without leaving Rehash on standard residential service-line quotes, and save those line-item changes directly from the drawer while the app keeps the floor guardrails intact. Package and maintenance-plan quotes now stay read-only in that drawer and point managers back to the full quote editor instead of allowing a risky partial inline save.
  • Hardened the Rehash popup after live failure reports. The quote drawer now fails open when older records have malformed parent-estimate links, invalid legacy rep ids, or bad Rehash template rows, so the manager can still open the quote summary instead of getting a generic request failure.
  • Added Red Line room summaries to the Rehash queue so managers can spot discountable residential quotes faster. Queue rows and the Rehash detail drawer now show quoted service totals versus the saved Red Line floor, plus a clear room / at-floor / below-floor badge for faster win-back review.
  • Hardened the Rehash setup diagnostics again after more live rollout feedback. The queue and analytics loaders now tolerate optional missing Rehash columns where they can, the setup warning now includes the exact missing estimates.rehash_* or disposition fields when the production database is still missing the core Rehash foundation migration, and unrelated missing-column errors no longer get mislabeled as a Rehash rollout problem.
  • Tightened the Rehash setup fallback after another live production report. The queue no longer treats missing legacy message_templates storage as a fatal Rehash-foundation failure, so /admin/rehash can still load with the built-in staged copy while only the actual Rehash estimate/disposition schema remains a hard requirement.
  • Repaired the live Rehash route guard after another audit pass. /admin/rehash is now registered in the admin permission map, so direct visits to the queue no longer bounce back to the dashboard before the Rehash access checks can run.
  • Audited the new Rehash Phase 4 rollout and repaired three gaps before the next production pass: Rehash analytics now fails soft with the same setup warning as the queue when a workspace is missing migrations, the Commissions page no longer hard-crashes in that partial-rollout state, and payroll exports now keep legacy accepted lead-source payouts instead of skipping them.
  • Began Phase 4 of the residential Red Line rollout. The Rehash queue now works much better on mobile, Pricing Manager can toggle Rehash template A/B testing, Rehash analytics now tracks template performance and queue-pressure aging buckets, and the Commissions dashboard can export payroll-ready CSVs including recovered Rehash split payouts.
  • Hardened the Rehash rollout path after the first live feedback. If a workspace is still missing part of the Rehash database setup, the Rehash page now loads with a clear setup message instead of failing outright while the queue API crashes.
  • Began Phase 3 of the residential Red Line rollout with the new Rehash bolt-on. Residential quotes can now require a saved Rehash disposition, unsold quotes can auto-enroll into the staged win-back sequence, managers get a dedicated /admin/rehash queue with text, call, email, touch, dead, and re-quote tools, and the new Rehash analytics page tracks recovered revenue plus saved recovered-close attribution.
  • Hardened the residential Red Line rollout after another audit pass. Residential estimates now fail closed to standard pricing when an assigned rep record can no longer be resolved instead of borrowing Red Line access from whoever happened to open the quote, explicitly enabled non-sales teammates now appear in the residential rep-assignment list, and the Commissions dashboard now keys its Red Line badge off the saved Red Line snapshot instead of assuming every stored commission amount came from Red Line.
  • Began Phase 2 of the residential Red Line rollout. Residential reps can now save below-floor drafts when manager override tracking is enabled, request a Red Line override directly from the estimator, and owners or managers can approve or reject that request from the review step or the estimate detail page before the quote is allowed to send.

2026-04-18

  • Added background autosave to the residential estimate wizard for internal office users. New and edited residential drafts now save as you work instead of waiting for Step 3, pending changes are flushed if the office clicks away right after editing, the review/send tools reuse that same saved draft, and service or add-on price overrides can be typed directly into the estimator instead of relying on browser spinner arrows.
  • Tightened CRM navigation and lead handoff workflows. New leads now open directly on their detail page after creation, estimate/job/invoice workspaces now include clearer View Client shortcuts, and lead-launched residential estimates keep the linked customer context without forcing duplicate data entry.
  • Expanded the mobile sales flow into a close-ready proposal detail experience. Reps can now open a secure customer-facing proposal from mobile, hand the device to the customer for on-the-spot acceptance and signature, and launch either a 25% deposit checkout link or a full-balance payment link without leaving the proposal screen.
  • Added sales_rep access to the mobile payment-link creation endpoints so sales users can launch hosted Stripe Checkout links from proposal detail instead of waiting on a manager or crew-lead role.
  • Broadened the mobile proposal shell to recognize all live estimate wizard families. Dashboard cards, proposal lists, and client proposal history now normalize commercial building, generic quote, asphalt maintenance, residential, fleet, and holiday-lights estimates correctly instead of hiding unsupported module types behind fallback labels.
  • Added guided mobile web handoff cards for Generic Quote and Asphalt Maintenance, so sales reps can still launch those live quote builders from the app and then come back to mobile proposal detail for presentation, signature, and payment follow-up.
  • Fixed the mobile Present and Close handoff to prefer the live customer estimate view before the legacy proposal path. That keeps holiday lights, fleet, commercial building, asphalt, and other quote-core-backed estimates from opening a dead-end customer URL when reps launch the close flow from mobile.
  • Fixed the residential mobile builder entry path to reset correctly when starting a brand-new proposal, preventing old in-memory quote state from bleeding into the next residential estimate.
  • Fixed a CRM handoff gap when launching residential estimates from lead and client context. New Estimate actions now carry the linked client, lead source, service address, and saved property square footage into the wizard automatically so the office no longer has to re-enter customer details after starting from a lead or client record.
  • Made the Red Line rollout easier to understand during setup. The residential estimator now shows an inline notice when Red Line is hidden because the workspace setting is off or the assigned rep is on standard pricing, and the Team / estimate-assignment lookups now fail soft in older workspaces that have not finished the Red Line membership migration yet.

2026-04-17

  • Added the first Red Line pricing foundation to the residential estimator. Floor pricing can now be derived from the live suggested service price instead of a separate flat table, so square footage, service size, and dirtiness all flow into the floor automatically before reps adjust pricing.
  • Added real-time Red Line estimator feedback for eligible users. Residential service rows can now show the floor, status, and commission context while pricing, review screens carry that same information forward, and the API now rejects attempts to save Red Line estimates below the floor.
  • Added the first admin controls for the Red Line rollout. Pricing Manager now includes a Red Line section for workspace-wide toggles, commission percentages, and per-service floor percentages, while Team Management now includes a per-user Red Line access toggle for mixed-comp teams.
  • Started persisting Red Line payout data with residential estimates and canonical line items so the Commissions dashboard can use explicit commission amounts and flag those rows as Red Line instead of relying only on the older 5% / 10% lead-source model.
  • Followed up on the Red Line rollout after the local audit. Residential estimate saves now keep redline_snapshot populated even for standard-pricing deals, the estimator and API now resolve Red Line access from the assigned sales rep instead of whoever happens to be editing the estimate, and team role changes now reset the Red Line access toggle to that role's default instead of leaving stale settings behind.

2026-04-14

  • Hardened the first Lead Connectors rollout after the local audit pass. Public website-form connector endpoints now enforce an IP bucket before the contact-aware rate limit, review-queue items can only be approved or rejected once, connector import-run logging now has database-level replay guards on external ids and idempotency keys, connector-backed API imports preserve explicit existing_client_id handoff into the shared lead-intake pipeline, and CE Pro now enforces one active connector per provider/transport pair instead of silently choosing the oldest active record.
  • Tightened email-forward lead parsing so phase 1 now requires an active email-forward connector before inbound emails can auto-create leads. Low-confidence email candidates continue to pause in the review queue when a connector threshold is configured, but the old no-connector parser fallback no longer creates leads on its own.
  • Added the first phase of the new Lead Connectors platform in Settings > Integrations. Workspaces can now configure connector-driven lead intake for trusted API imports, browser-safe website forms, and forwarded email parsing from one shared admin surface instead of treating every source like a separate custom integration.
  • Added connector-level field mapping, defaults, test imports, recent import history, and a low-confidence email review queue. Teams can now normalize outside field names into CE Pro lead fields, apply source or assignee defaults, test the connector before launch, and approve or reject uncertain parsed emails before they become live leads.
  • Hardened duplicate suppression for connector-based lead intake by carrying external ids and idempotent request keys through the shared lead pipeline, while the docs now include a dedicated Lead Connectors guide and updated integrations onboarding steps.

2026-04-10

  • Canonicalized authenticated web entry points to the dedicated app host. Marketing-site Log In and direct admin links now send users to app.cleanestimate.pro, root-domain requests for /admin, /login, password reset, and auth callback routes now hand off to the app host automatically, and invite plus recovery emails now generate their setup links on the same app subdomain instead of mixing root-site and app-site entry paths.

2026-04-08

  • Hardened the Esendex phone rollout right after the first audit pass. Voice transfer scripts now preserve the provider command syntax Esendex expects, voice status callbacks now reuse the tokenized webhook URL contract shown in Settings instead of drifting from protected environments, and the Phone / Voice setup now clearly stays on text-to-speech greetings for Phase 1 instead of advertising custom audio playback that is not live yet.
  • Replaced the old Twilio-shaped phone setup with Esendex across the app and docs. Workspaces now configure a shared Esendex number, SMS license key, and Voice license key from the Phone / Voice settings card, while the docs now point to the new Esendex setup guide instead of the retired Twilio instructions.
  • Shipped the Esendex phone stack for daily office use. Shared SMS sends and replies now run through Esendex, inbound customer calls transfer to the saved office line, missed calls log into Messages, and the phone timeline now uses provider-neutral message and call ids instead of depending on Twilio-only SIDs.
  • Updated automation call behavior for the Esendex rollout. Make Call and Voicemail Drop now use the shared Esendex number, phone triggers now cover Call Received, Call Answered, Missed Call, and Call Completed, and the docs now clearly mark AI Voice Agent and inbound voicemail recording playback as out of scope for this Phase 1 release.

2026-04-05

  • Smoothed another big chunk of the mobile page-by-page QA pass. Proposal builders now keep the residential step state in sync without mutating state during render, the standalone proposal detail screen now has working call, email, and maps actions plus a proper back header, the estimate-type picker now gives reps clearer field-quoting guidance with step counts, the dashboard sync indicator now behaves like a real queued-sync shortcut instead of a dead notification bell, and client notes save more reliably when a rep leaves the notes field.
  • Fixed the mobile client detail and proposal shells so they line up with the live estimate data model instead of a stale proposal-table contract. Client detail now pulls proposal history from current estimate records, proposal lists stop breaking when you scroll or refresh, and remote proposal cards no longer show fake 0% margin badges when margin data is unavailable.
  • Reworked the mobile pipeline around what field users can actually act on. Pipeline cards now normalize service labels correctly, the summary bar shows Needs Follow-Up instead of a misleading always-zero average margin metric, empty pipelines explain what to expect, and the deal detail sheet now gives real proposal, call, and email actions when contact data exists.
  • Cleaned up the mobile account/settings shell on phones. The Settings hub now opens on a safer padded account card with organization context, and the Profile, Organization, Notifications, Offline, and About screens now respect the safe area so content no longer crowds the top system chrome on smaller devices.
  • Fixed the first mobile startup blockers that were making the current Expo shell feel dead on arrival during testing. The app no longer crashes during startup because of WatermelonDB model field syntax, the web-safe auth storage path now avoids the expo-secure-store failure on browser-based QA runs, and the tab shell no longer registers the Jobs tab against the wrong nested route.
  • Refreshed the mobile auth flow so the first-run experience looks intentional instead of bare. The sign-in and forgot-password screens now use branded intro and recovery cards, stronger spacing, proper input chrome, and consistent action buttons instead of the older stacked text-and-button layout.
  • Cleaned up the mobile dashboard shell for small screens by removing the duplicate tab header and replacing the cramped quick-action strip with larger proposal-type cards that hold up better on phone-width layouts.
  • Fixed the mobile auth shell so route protection now lives in the nested auth and app layouts instead of firing redirects from the root navigator. Internal beta testers should no longer see the startup/login warning banner that previously made the Android dev client feel broken before they even signed in, and the local debug build now suppresses the two known development-only native-library warnings that were still pinning a generic warning bar to the bottom of the login screen.

2026-04-04

  • Hardened the Clean Estimate Pro mobile internal beta for the current Expo app instead of splitting into separate iOS and Android codebases. Mobile preview builds now use the internal beta lane, the app reads the required public Expo/Supabase support config consistently, profile editing now writes back to the live user-profile schema, and field proposal drafts reopen through one shared deep-link contract instead of occasionally landing on the wrong step.
  • Added the first crew-lite mobile ops slice behind scheduling. Eligible roles now get a dedicated Jobs tab, job-assigned notifications open the same typed job route as the rest of the app, job detail now reads the live customer/job fields correctly, and crews can review schedule, address, invoice balance, crew alerts, and payment collection from one mobile screen.
  • Replaced the remaining mobile residential placeholder pricing and tax assumptions with shared live config reads. The residential builder now applies the active web pricing config and shared state tax overrides, while the mobile Pricing Defaults and Tax Rates settings screens now act as read-only references to the same data sources the web app uses instead of writing to stale legacy fields.
  • Finished the crew-alert rollout on the current generic-quote and job stack. Generic quote line items now expose a dedicated internal crew-alert field again, estimate edit/save flows preserve that value, jobs created from those quotes keep the alert on each line item, and both the web job detail screen plus the mobile crew job screen now show a clear Crew Alerts callout without exposing those notes to customers.
  • Added internal crew-alert support to commercial building proposals, generic quotes, and downstream job detail views. Office staff can now save field-only instructions alongside customer-facing line-item descriptions, commercial building catalog adds prefer the shared price-book description when available, and both quote flows preserve crew alerts when drafts are reopened without exposing those notes on customer-facing proposals, quotes, or PDFs.
  • Made commercial building line-item descriptions actually visible and editable in the wizard as a dedicated Customer Description field, then backfilled clearer default scope copy across the stock commercial building services, seeded fleet vehicle types, and built-in residential add-ons so default catalog rows stop starting blank when they are reused in shared quote-core line items. Older commercial draft rows that were saved blank now also repopulate that customer description when the proposal is reopened.
  • Unified customer-facing item descriptions across the remaining quote builders. Residential services and add-ons now expose editable Customer Description fields in both the office wizard and the public standalone estimator, generic quote detail notes now render on the customer quote page and PDF, and fleet vehicle rows plus add-ons now carry editable customer descriptions through builder, review, customer proposal, and PDF flows.

2026-04-02

  • Fixed the lead-creation modal launched from linked client records on shorter laptop viewports. The New Lead dialog now becomes vertically scrollable instead of trapping the save button below the fold when the selected-client flow expands with address and appointment fields.

2026-04-01

  • Fixed a residential estimate save failure on quotes that included mirrored offer children such as proposal packages and maintenance plans. CE Pro now allows the residential offer-mirror workflow to persist multiple child estimate rows for the same parent quote instead of tripping the one-to-one source-proposal uniqueness rule that only applies to linked commercial, fleet, and holiday-lights estimates.
  • Followed that residential mirror hotfix through the rest of the office and platform surfaces. Revenue, service-mix, geography, retention, franchise, admin-alert, sales-rep dashboard, residential overview, commissions, estimate-list, marketing audience, sales follow-up schedule, and super-admin estimate rollups now treat mirrored residential offer children as internal support rows instead of counting them as extra quotes, so maintenance or package siblings no longer inflate analytics, stale-deal alerts, rep follow-ups, estimate lists, or back-office totals.

2026-03-31

  • Tightened customer-engagement tracking across messages, quotes, and invoices. Public estimate, proposal, maintenance, and shared-quote opens now wait for the alert write instead of leaving those inserts as best-effort background work, so live admin popups are more reliable when a customer opens the document.
  • Added invoice-viewed engagement alerts to the hosted customer invoice page. Token-based invoice opens now log an Invoice viewed system event in the customer thread, update the invoice's first-viewed timestamp, and raise the same live popup plus alert-bell notification pattern used for proposal and quote views.
  • Expanded outbound email history in the Messages thread. Resend email webhooks now keep first/latest open timestamps, first/latest click timestamps, open counts, click counts, and the latest clicked URL on the outbound message metadata, while the thread UI shows those details directly under the email bubble instead of only a single Opened or Clicked badge.
  • Hardened Resend status syncing so later open events no longer downgrade a previously clicked email back to Opened. The email row now keeps the higher click state while still counting each additional open that Resend reports.
  • Made those customer-engagement events much more visible inside Messages. Quote, proposal, estimate, maintenance proposal, and invoice views now render as highlighted activity cards in the thread, and outbound emails now show a dedicated engagement panel for email opens and tracked link clicks instead of leaving that history buried in tiny status text.

2026-03-30

  • Hardened the Twilio phone-call rollout after the first deep audit. Inbound conference callbacks no longer misclassify successful conversations as missed calls, voice recordings now post to the dedicated recording webhook instead of disappearing into the generic status callback, completed-call automations no longer fire on no-answer outcomes, voicemail drops now fail closed when a live human answers, and the legacy auto-dial migration now carries forward usable phone visibility before the old table is retired.
  • Added Phase 1 Twilio phone calling across CleanEstimate Pro. The shared Twilio number can now receive inbound customer calls, forward them to the office line, record voicemail, log call and voicemail events into the unified Messages timeline, and raise missed-call or voicemail alerts for the team.
  • Added Twilio voice support to the automation engine. Workflows can now trigger from Call Received, Call Answered, Missed Call, Voicemail Received, and Call Completed, and they can execute live Make Call and Leave Voicemail steps instead of treating those call nodes as unsupported placeholders.
  • Expanded the Phone / Voice settings and docs so operators can configure forwarding numbers, voicemail greetings, voice readiness, recording, and missed-call alerts from the same Twilio integration area used for SMS.
  • Fixed customer-engagement alerting around sent quotes and invoices. Proposal, estimate, and invoice emails now log under the live Resend provider id used by outbound delivery, older mismatched rows can self-heal when Resend open webhooks arrive, and the admin alert bell plus live popup feed now raise a new alert every time a customer opens the email or opens the proposal page itself across residential, maintenance, fleet, commercial building, and shared quote flows. Public quote and proposal pages now also ignore common crawler and link-preview traffic so bot fetches do not create false viewed alerts or premature viewed-state changes.
  • Fixed the new asphalt module entry path so it always opens the live editor. The canonical /admin/asphalt-maintenance route now forwards into the working asphalt quote builder, the admin route-permission guard recognizes that module path instead of redirecting it back to the dashboard, Quick Start uses that stable module path, and global search keeps a separate New Asphalt Quote action for the explicit create flow instead of leaving some clicks or bookmarks on a missing landing page.
  • Fixed the next asphalt send regression for older shared-manual drafts. When a generic or asphalt quote is missing its persisted public_quote_token, the send stack now seeds and saves that shared quote token before email/SMS delivery, so older drafts can still send with a working shared quote link. If secure signing is not configured, the customer still gets a view-only link instead of the office seeing a false "Secure estimate links are temporarily unavailable" error.
  • Fixed another customer-link delivery failure behind estimate sends. Quote, proposal, maintenance-plan, and shared quote link generation now fall back to the configured public site URL when the older dedicated app URL env is blank, so sends keep working instead of throwing a generic secure-link error during production config drift.
  • Followed that send hotfix with the remaining link and state-hardening pass. Holiday-lights sends now use the same public-site URL fallback as the other customer-link flows, and shared quote token repair now merges against the latest stored delivery state before it writes so newer payment or send-tracking metadata is not wiped while older drafts self-heal.
  • Fixed the next estimate-send troubleshooting blind spot. Office send errors no longer collapse unrelated delivery failures into the generic secure-link warning, so missing proposal data, missing maintenance plans, and email-provider setup issues now surface their own actionable messages while true customer-link setup problems keep the secure-link error.
  • Fixed the actual generic and asphalt send blocker behind that misleading warning too. Quote delivery no longer selects rollout-only pricing columns like minimum_adjustment and taxable_amount from the live estimates table before building the email, so workspaces on older production schemas can send quote emails again instead of failing the send before Resend is ever called.
  • Removed the last false secure-link warning on shared quote sends. If a quote already has a persisted public quote token, CE Pro now treats that shared /quote/[token] route as fully configured even when CUSTOMER_TOKEN_SECRET is missing, so successful generic, asphalt, residential, fleet, commercial building, and holiday-lights sends no longer tell the office the delivered link is only "view-only."
  • Hardened the new automation visual builder after the first live review pass. The canvas Wizard button and mobile fallback now route back into the real workflow edit screen instead of a dead /wizard URL, new drag-and-drop nodes save with UUID-safe ids, graph saves no longer briefly wipe all edges before rewriting them, and the save badge now shows Save failed correctly when a graph write errors instead of getting stuck on a stale Saved state.
  • Rebuilt the automation visual builder into a real graph editor instead of the old loose card pile. Workflows now return step positions and edge labels from the API, save the full graph through one dedicated graph endpoint, auto-arrange into a readable layered layout when positions are missing, preserve manual drag edits after save, and expose clearer canvas save states plus Auto Arrange, Fit Workflow, and Reset View controls. CE Pro also now surfaces the visual builder much earlier through Open Visual Builder on the new automation flow and Build Visually on template cards instead of hiding the canvas until after a workflow is already saved.
  • Built and activated a shared Estimate Accepted automation flow through the production automation builder so accepted estimates now trigger a customer thank-you email, a customer thank-you text, and an internal follow-up task for scheduling and next steps from one shared workflow instead of requiring per-module handoff rules.
  • Fixed the automation workflow detail page for newly created workflows. The single-workflow API now returns the same run counters and last-run metadata the overview cards expect, and the detail view now falls back safely if those metrics are still empty on a brand-new workflow.
  • Fixed the last live signature-save failure on commercial proposal acceptance. Fleet and commercial building proposal signatures now recreate the missing proposal-signature storage bucket automatically before retrying the upload, and CE Pro now has regression coverage across all three signature pipelines: shared estimate/quote signatures, commercial proposal signatures, and holiday-lights contract signatures.
  • Fixed the last shared quote acceptance-auth gap across estimate types. Shared /quote/[token] pages for residential, proposal packages, maintenance plans, fleet, commercial building, holiday-lights linked estimates, generic quotes, and asphalt quotes now treat the saved public quote token as a valid interaction token for signatures, package selection, maintenance acceptance, public PDFs, and deposit actions, so customers and office previews no longer see a false "online approval unavailable" state just because the server could not mint a separate customer-token URL.
  • Fixed the last public-acceptance preview mismatch across estimate types. Office-side View Public Page actions for fleet, commercial building, holiday-lights linked estimates, and the older raw estimate/proposal/maintenance routes now resolve the same valid customer-link contract used in sent emails and texts, so internal previews no longer fall into a read-only "online approval unavailable" state just because they opened from an older module URL.
  • Closed the last two module rollout seams found during a live cross-module smoke pass. Residential estimates now stay compatible with older addon_structures schemas that do not expose created_at yet, and holiday-lights draft saves now keep the shared estimate-list projection writing delivery_state as JSON so seasonal quotes no longer fail on partially upgraded production databases.
  • Fixed the fleet proposal create path after the quote-core rollout for older production schemas. Fleet and commercial proposal list-projection sync now write delivery_state as JSON again, so first draft saves from Admin > Fleet > New Proposal no longer fail on workspaces that still had the older estimate-list trigger definitions in place.
  • Tightened that fleet launch flow again so proposals opened from a lead now preload the lead source into step 1 and carry it through the first draft save automatically instead of dropping the source unless staff reselected it by hand.
  • Launched the Asphalt Maintenance module across the product and website. Teams can now create asphalt quotes from the office navigation, Quick Start, and global search, reopen them in a dedicated asphalt editor, publish the live asphalt marketing page, and keep asphalt quotes on the shared quote-core send, PDF, payment, and customer-link path instead of hiding that workflow as a proof-only slice.
  • Fixed the last shared-manual quote hardening gaps that surfaced during the asphalt rollout. Asphalt edits now fail closed instead of silently flattening back into generic quote data, untaxed shared manual quotes stay untaxed when reopened, maintenance acceptance copy uses the workspace brand instead of a hardcoded provider name, and the shared quote delivery-state alignment migration now handles already-JSONB projection rows safely during deploy.
  • Refined the admin workspace chrome again so XP now lives in the left sidebar between the workspace section and Quick Start on every admin page, while the page-level header keeps Search everywhere and only shows the duplicate Help and Alerts actions inside the content area on desktop. Mobile pages now rely on the fixed top bar for Help and Alerts instead of repeating those bubbles again inside the page body.

2026-03-29

  • Started the asphalt maintenance proof slice for the unified quote core. CleanEstimate Pro now has a registered asphalt_maintenance wizard plugin plus shared global baseline price-book items for single-coat sealcoat, double-coat sealcoat, crack fill, line striping refresh, and patch repair, which proves a net-new estimate type can plug into the shared quote core without adding a new send stack, payment stack, public page stack, or quote tables.
  • Extended that asphalt proof slice into the shared manual-quote persistence path too. The asphalt quote builder can now prepare a real shared quote-core payload with wizard_key = asphalt_maintenance, shared payment/send/public compatibility, wizard-specific measurement snapshots, and asphalt document/review metadata without introducing a new estimate route or another bespoke quote save service.
  • Finished the next asphalt proof read path too. Office estimate lists, type counts, type filters, and estimate-detail edit actions can now surface wizard-backed asphalt maintenance quotes as their own estimate type instead of collapsing them into the generic quote bucket, while still routing those rows back into the shared quote editor correctly.
  • Tightened the asphalt proof edit/save round-trip. Reopened asphalt quotes now reload their description and internal notes from the asphalt_maintenance module namespace correctly, and asphalt canonical line items keep their own source_wizard plus shared price_book_item_id linkage instead of being flattened back through the generic quote normalizer during save.
  • Tightened the asphalt proof shared-quote lane too. Asphalt maintenance quotes now stay on the same shared manual quote path for customer links, public quote rendering, pricing normalization, send-time generic detection, and PDF selection instead of depending on generic-only checks that could miss wizard-backed manual quotes.
  • Finished the last shared-manual asphalt office blind spots. Asphalt-backed quotes can now reopen through the shared office quote editor without failing the old estimate_type = generic filter, the shared estimate create/update routes now keep asphalt on the shared manual lane instead of falling back to residential-only assumptions, and office-side send copy now uses asphalt-specific quote wording when staff send those quotes from the shared editor.
  • Tightened that last shared-manual send layer again so asphalt quote sends now record asphalt_maintenance_quote in shared delivery history and quote events instead of being mislabeled as generic, and shared manual-quote delivery now falls back to a neutral sender label if the workspace name cannot be loaded instead of defaulting to another provider's brand name.
  • Fixed another final quote-core data-contract batch: shared public quote token lookups now use the indexed quote-token expression instead of a slower JSON containment scan, the estimate-list projection now stores shared delivery state as JSON so payment fallback badges keep reading the live quote payment metadata, asphalt zero-footage drafts no longer create a phantom $0 sealcoat row, and canonical total math now preserves non-taxable line-item value when a taxable subtotal is supplied separately.
  • Fixed the shared price-book settings rollout for existing workspaces. Older orgs now backfill their active residential pricing under the same nested residential_pricing_config key the quote-core readers expect, so the move into shared price-book settings preserves current pricing instead of silently snapping those workspaces back to the platform defaults.
  • Extended quote-core payment tracking on estimates. Stripe pay-link creation, successful estimate payments, and office-recorded estimate payments now write shared quote payment metadata and payment-state history onto the canonical estimate record, which keeps shared quote links, office payment follow-up, and quote event history aligned with the actual balance state.
  • Hardened that estimate payment sync again so it now merges against the latest shared quote delivery state before it writes. A payment update no longer risks overwriting newer shared quote-link or send-history metadata that landed a moment earlier on the same estimate.
  • Finished the next estimate-payment unification step by teaching the public estimate page, shared quote page, and office estimate list to fall back to the shared quote-core payment state when the older flat estimate payment columns are stale. That keeps payment badges and pay-link actions aligned with the latest quote delivery/payment history during the rollout.
  • Accepted residential maintenance agreements now keep their linked recurring templates aligned after later office edits too. If the team changes an already accepted maintenance estimate, CE Pro resyncs the recurring job-template series from the updated maintenance plan instead of leaving future recurring visits on the older scope.
  • Removed a redundant second residential package-offer hydration during customer package selection. Signed package accepts now validate from one shared offer-core pass instead of doing the same database rehydrate twice inside the public proposal-selection flow.
  • Pricing Manager saves, restores, onboarding pricing setup, and org bootstrap now all keep the active residential pricing config mirrored into the workspace default price book. That means the shared quote-core price-book layer stays current immediately after an office pricing change instead of waiting for a later catalog repair to catch up.
  • Fixed another holiday-lights quote-core reporting gap. The first customer open on a holiday-lights quote now advances the seasonal source quote itself from Sent to Viewed instead of only stamping the viewed timestamp, so holiday-lights list filters, viewed-status dashboards, and seasonal reporting stay aligned with the real first open.
  • Tightened that holiday-lights lifecycle again so Viewed quotes still stay resendable from the office, shared /quote/[token] opens record as shared-quote opens instead of portal-only opens in the seasonal activity trail, and traced zone-based draft edits no longer fail just because the manual linear-feet field was left at zero.
  • Continued the shared offer-layer migration for residential quotes. Customer dashboard links, customer estimate-list links, portal proposal lookup links, and office resend/SMS offer routing now all classify residential package proposals versus maintenance offers through one shared offer rule instead of each surface re-checking the old parent estimate JSON on its own.
  • Started the next shared offer-layer ownership cut for residential package proposals. Customer package selection now rehydrates proposal options from the mirrored estimate_offer_groups records before it validates the request, so signed customers can still accept the right package even when the rollout has already moved those proposal options off the parent estimate JSON.
  • Pushed that residential offer-layer ownership further into acceptance flows. Customer maintenance-agreement approvals, maintenance proposal PDF preparation, and customer package acceptance now keep the linked child offer quotes in sync with the parent estimate state so later reads, sends, and recurring-template automation stay aligned to the same accepted offer.
  • Moved another holiday-lights ownership seam behind the shared quote-core services. Holiday-lights create/edit routes now reuse one shared draft-save path for pricing recalculation, linked-estimate sync, design updates, and bundled power-wash linkage, office or customer Declined / Expired actions now write the matching shared quote event onto the linked estimate instead of stopping at the seasonal source record, and holiday-lights opens through the shared /quote/[token] page now feed their viewed timestamp and seasonal pipeline move back into the source seasonal quote too.
  • Reworked the shared price-book foundation so CE Pro can carry one platform-wide default price book and keep each organization's custom default book private. Catalog bridges still write org-local custom books during the migration, but shared fallback reads can now land on the global default baseline when an org has not customized its own book yet.
  • Tightened that shared pricing model again so org-specific price-book reads now behave like overrides on top of the shared default book instead of an all-or-nothing switch. If one workspace customizes a shared catalog item, CE Pro keeps that override private to that workspace while untouched shared items still inherit from the global baseline.
  • Hardened that overlay model again so shared baseline price-book items still participate in residential lookup and repair flows even after an org-specific sync runs, non-legacy org overrides can replace shared defaults cleanly by name, and hourly fleet vehicle pricing now keeps its minimum quoted price when it round-trips through the shared price book.
  • Moved another commercial ownership seam into the shared quote-core layer. Fleet and commercial building draft edits now let the quote services own status-field bookkeeping and linked-estimate sales-transition logging, while commercial proposal send routes now reuse the shared quote-link preparation helper instead of rebuilding customer URLs in each route.
  • Followed that pricing-foundation change with a tighter residential catalog write path. Residential service, add-on, and custom-template admin saves now update only the touched shared price-book rows instead of rebuilding the whole residential catalog bridge after each edit, while still rolling the legacy save back if the shared price-book write fails.
  • Tightened recurring maintenance-template sync so CE Pro no longer re-ends already-ended recurring templates every time a maintenance agreement is reaccepted or resynced. Historical ended templates now keep their original ended_at audit trail unless they are actively being changed again.
  • Tightened the fleet and commercial linked-estimate bridge again so draft saves now treat canonical quote-core line-item sync as part of the save itself. If the linked estimate cannot keep its canonical line items aligned, the draft save rolls back instead of leaving the proposal tables ahead of the shared quote workspace, and older linked-estimate fallback writes now reuse the existing estimate number instead of creating duplicate orphaned estimates on retry.
  • Followed that with another ownership cut for fleet and commercial building drafts. Those draft-save services now own the linked shared estimate write directly and only use the old sync-to-estimates path as a compatibility loader wrapper, which keeps the proposal save and the shared estimate workspace on one tighter success-or-rollback path.
  • The Create Recurring Template shortcut on accepted fleet and commercial building estimates now prefers canonical quote-core line items from the linked estimate workspace and carries the source estimate id into the saved recurring template. That keeps recurring-template prefill aligned with the shared quote-core breakdown, gives later recurring automation a direct quote-to-template link to follow, and surfaces the source quote link directly on the recurring-template list and detail screens.
  • Accepted residential maintenance agreements now create linked recurring job templates automatically. CE Pro seeds one recurring template per repeating seasonal visit pattern from the shared maintenance offer quote, rolls the public maintenance-accept action back if that recurring-template setup fails, and records the quote-core conversion so recurring residential work starts from the same canonical quote data instead of a manual rebuild.
  • Tightened the commercial catalog bridge so fleet vehicle-type saves plus commercial building service/surface-type creates now fail closed if the shared price-book write cannot be completed. Instead of quietly updating only the old settings tables and leaving price_book_items stale, CE Pro now rolls those admin changes back and surfaces a real save error.
  • Applied that same fail-closed catalog rule to the residential and holiday-lights admin settings paths too. Residential service, add-on, and custom-template writes, plus holiday-lights inventory creates and edits, now roll back if the shared price-book bridge cannot be updated instead of silently leaving the legacy catalog tables ahead of the unified quote-core catalog.
  • Narrowed those shared catalog writes again so fleet vehicle types, commercial building service/surface types, and holiday-lights inventory now update only the touched shared price_book_items rows instead of rebuilding each module's entire shared catalog bridge after every admin mutation.
  • Tightened the shared commercial quote-link handoff so fleet and commercial building resends now reuse the saved /quote/[token] link from the linked estimate workspace before falling back to an older signed estimate URL. If the linked commercial source record can no longer be reloaded, the shared quote route now fails closed instead of showing the wrong residential/generic page.
  • Fixed commercial customer totals so fleet and commercial building proposal views only apply tax when the saved proposal actually includes a tax rate. Blank tax proposals no longer assume a default 6% tax on the public page, and recurring building portfolio summaries now use the saved maintenance visit pricing in the per-visit totals instead of quietly showing the one-time initial-clean price there.
  • Moved residential estimate updates from PATCH /api/v1/estimates/{id} onto the shared residential quote-core save path, so supported API-side updates now preserve canonical quote snapshots, canonical line-item sync, delivery-state metadata, and shared quote events instead of bypassing that newer persistence layer.
  • Hardened the shared estimate send and status routes before more wizard families move onto them. Holiday-lights sends from linked estimate workspaces now stay on the seasonal delivery helper, office-side estimate status changes now require estimate permissions inside the active workspace, and commercial linked estimates now refuse the wrong shared send path instead of silently using the residential sender.
  • Shared estimate and quote public pages now show the workspace organization name or a neutral product fallback instead of a hardcoded Rolling Suds label, so customer-facing public links stay correctly branded across workspaces during the quote-core rollout.
  • Expanded the shared quote page into the first commercial linked-estimate slice. Fleet and commercial building linked estimates with persisted public quote tokens can now open through /quote/[token], and the shared route renders the matching commercial proposal layout instead of forcing those linked-estimate previews back through legacy signed estimate URLs.
  • Wired holiday-lights linked estimates more directly into that same shared public-token model by carrying the source seasonal estimate id on the linked estimate row, so shared quote-core public links can rebuild the holiday-lights summary when office or customer flows open the linked estimate outside the dedicated portal send path.
  • Followed that holiday-lights public-link rollout into delivery too. When a linked holiday-lights estimate already has a persisted public quote token, CE Pro now delivers the shared /quote/[token] URL in seasonal email and SMS sends instead of defaulting back to the older portal link, while still keeping the portal as the fallback path if the shared quote link is not ready yet.
  • Tightened shared delivery again so quote SMS sends no longer fall back to a broken unsigned estimate URL when secure customer-link generation is unavailable, and holiday-lights email/SMS delivery events now stay linked to the originating estimate record for cleaner communication history.
  • Fixed a fleet quote-core regression that could show optional $0.00 add-ons as real estimate line items. Non-included fleet add-ons now stay out of the shared estimate breakdown unless they actually carry a billed price, while intentionally included add-ons still render as included items.
  • Fixed commercial building custom-item pricing in the shared quote core so quantity-based custom service rows now derive a real per-unit rate from the saved total instead of copying the whole line total into unit_price and breaking downstream math.
  • Finished another residential offer-layer hardening pass across the summary surfaces. Customer dashboards, customer estimate lists, portal proposal lookups, and the office estimate list now rebuild residential package-proposal and maintenance-plan classification from the mirrored shared offer records instead of relying only on the legacy parent estimate JSON, so those links and badges stay correct during the quote-core migration.
  • Fixed one more legacy residential handoff into the shared quote page. Older /p/[id] public links now preserve whether the customer opened a base estimate, package proposal, or maintenance proposal when they redirect into /quote/[token], which keeps quote-view analytics aligned with the original public context.
  • Tightened the shared residential quote page so it now behaves correctly in read-only mode. If secure customer-token signing is temporarily unavailable, customers can still open the shared quote link to review pricing and scope, but package selection, maintenance-plan approval, and customization actions stay hidden instead of failing after the click.
  • Preserved residential proposal and maintenance-plan analytics when those older public links hand off to the shared quote page. Proposal-option links and maintenance-plan links now keep their own public-view context, so reporting and follow-up history no longer collapse those opens into a generic quote-view event.
  • Moved another residential offer-layer read seam onto the shared quote core. Residential customer/public reads can now rebuild proposal-option and maintenance-plan data from mirrored estimate_offer_groups child quotes, so those pages stay aligned to the shared offer model even while the older parent-estimate JSON remains in place for compatibility.
  • Extended that residential offer-layer hydration into customer actions and document reads too. Residential package selection, residential send classification, and maintenance-plan PDF reads now rebuild proposal-option or maintenance-plan state from the mirrored shared offer records before they decide which package flow, send path, or maintenance document to use, which keeps those actions aligned with the shared offer model during the migration.
  • Hardened the shared commercial proposal lifecycle helper so accepted fleet and commercial-building proposal writes stay org-scoped just like the shared send path, which reduces the chance of future cross-workspace drift as more commercial flows move onto the quote core.
  • Hardened the next Sprint 8 ownership seam too. Fleet proposal PATCH requests now strip unexpected fields before they hit the shared draft-save service, fleet linked estimates keep lost_notes aligned with the proposal workspace, residential shared price-book lookups now honor org overrides correctly in their item-id resolution path, and commercial resend prep now preserves the same shared quote token plus linked-estimate send history even if the status flip hits a transient failure.

2026-03-28

  • Started the remaining commercial catalog bridge into the shared price-book foundation. Fleet vehicle types plus commercial building service/surface catalogs now read through the shared price_book_items layer when available, and the matching admin create/update/delete routes refresh that shared catalog copy after legacy commercial settings writes so the next wizard migrations can stop depending on standalone catalog tables.
  • Tightened that commercial quote-core bridge so fleet and commercial building linked estimates now preserve the real shared price_book_item_id on their canonical line items for bridged vehicle and service catalog rows instead of only copying the catalog metadata into the linked estimate snapshot.
  • Filled in another shared quote-event gap for commercial quote-core migration. Fleet and commercial building draft create/edit saves now write quote-core created and saved events on their linked estimate records as soon as the linked estimate sync succeeds, so commercial draft activity shows up in the same event stream the generic, residential, and holiday-lights quote flows already use.
  • Moved fleet and commercial building draft-save ownership into shared quote-core services instead of leaving the full persistence choreography inline in the proposal routes. Those draft saves now complete linked-estimate sync before the API returns, and they roll back more cleanly if the shared estimate workspace cannot be updated, which keeps commercial proposal drafts and their linked estimate records from drifting apart during save failures.
  • Moved the public fleet and commercial building signature routes onto one shared commercial acceptance helper. Both proposal types now use the same signature upload, accepted-alert, pipeline, linked-estimate sync, and accepted-estimate follow-up path, which keeps customer signatures resilient even when later follow-up bookkeeping has a temporary issue.
  • Moved fleet and commercial building proposal send follow-up onto one shared quote-core helper too. Those proposal sends now refresh the linked estimate workspace before the API returns, and the shared estimate record now writes its sent-event and send-history state from the same commercial send path instead of relying on a best-effort background sync after delivery.
  • Moved commercial accepted follow-up onto the shared quote-core path for both customer signatures and office-side won-stage actions. Fleet and commercial building proposals now sync the linked estimate workspace and reuse the same accepted follow-up automation path whether the proposal was signed publicly or marked won from the internal commercial pipeline.
  • Moved the first public fleet and commercial building proposal view onto one shared quote-core helper too. Customer opens now record the source proposal view, pipeline movement, linked-estimate viewed status, and shared quote event from one path instead of depending on a best-effort linked-estimate sync after the public page already rendered.
  • Started the holiday-lights quote-core bridge too. Linked holiday-lights estimates now normalize through a dedicated wizard plugin and write canonical shared quote-core line items plus quote snapshots, so the shared estimate workspace can carry a structured holiday-lights breakdown instead of only the older compatibility JSON line-item snapshot.
  • Followed that holiday-lights bridge with a V2 pricing alignment pass. Zone-based holiday-lights quotes now sync their linked estimate totals from the saved 1-year or 3-year contract term instead of the old midpoint install range, the office editor reopens with the saved contract term and proposal media intact, and holiday-lights design records now keep the proposal media payload alongside the design snapshot.
  • Extended that holiday-lights selected-term pricing alignment into the rest of the office surfaces too. Holiday-lights send copy, AI review totals, sales-cycle math, client-detail totals, and revenue / lead-source reporting now use the saved 1-year or 3-year contract price before falling back to the older low/high range fields, so the number the office sees stays aligned with the customer’s actual contract term.
  • Moved holiday-lights accepted follow-up work onto the same shared quote-core acceptance helper used by the newer estimate flows. Customer accepts and office contract-sign actions now re-sync the linked estimate workspace first, then reuse the shared accepted-event, pipeline, webhook, gamification, and accepted-job follow-up path instead of keeping a separate seasonal acceptance branch.
  • Tightened that holiday-lights acceptance bridge so customer status changes and office contract signing now update the source seasonal quote through the same shared helper too. The holiday-lights sales pipeline records the acceptance from one path, and already-accepted quotes no longer rewrite accepted_at when a contract record is patched later.
  • Started the holiday-lights inventory catalog bridge into the shared price-book foundation too. The office inventory list and holiday-lights dashboard low-stock count now read shared bridged SKU rows when they exist, and holiday-lights inventory create/update actions refresh the shared price_book_items copy after legacy SKU writes so that catalog can migrate onto the same shared pricing infrastructure as the other estimate wizards.
  • Moved holiday-lights quote sending onto the same shared review-and-send path used by the other estimate wizards. The admin holiday-lights quote page now opens the shared double email/text modal before anything is sent, seasonal send completion now writes linked-estimate send history and sent quote events from the quote-core path, and holiday-lights sends keep their portal link instead of falling back to the standard residential customer page.
  • Moved holiday-lights portal quote viewing onto the shared quote-core path too. When a customer opens the holiday-lights portal from a delivered quote link, the latest open seasonal quote now records its viewed timestamp, public pipeline move, and linked-estimate viewed event from one shared helper instead of leaving those portal opens outside the unified quote send/view history.
  • Started the unified quote-core foundation under the existing estimates system and migrated generic quotes onto the first shared slice. Generic quote saves now write canonical quote metadata plus canonical line items, preview/send/open actions all reuse one persisted customer quote link, and the shared /quote/[token] page is now the source of truth for generic quote delivery and acceptance.
  • Hardened that first generic quote-core slice so generic quote edits no longer silently reset sent quotes back to draft, quote send/view/accept activity now records as Generic Quote instead of leaking into residential pipeline history, and the shared /quote/[token] page now stays viewable in read-only mode when customer-link signing is not configured instead of crashing on open.
  • Tightened the local quote-core foundation before Phase 2 so the new quote tables now carry org-scoped access policies, the three-axis quote fields are written by both residential and synced module estimate writers, generic quote send history only records channels that actually delivered, and the shared generic quote page now shows the workspace organization name instead of a hardcoded brand label.
  • Closed another generic quote-core cleanup pass before Phase 2: generic quote creation no longer burns two estimate numbers per save, generic edit saves now preserve numeric estimated-hours values from string-based form posts, tenant scoping was added to canonical line-item rollback reads, and the generic send flow now refuses to deliver a quote when no working customer link can be generated.
  • Started the residential catalog bridge into the shared price-book core. Residential estimator entrypoints and the services/add-ons/custom-template admin reads now hydrate through the shared price-book-backed catalog layer, keeping the office and public residential wizard aligned to one normalized catalog surface while the legacy pricing screens remain in place.
  • Added the first residential wizard plugin under the shared estimate-core registry and started feeding residential estimate saves into the canonical quote line-item table. Residential create/update still preserve the legacy estimate fields and line-item snapshot for compatibility, but the shared quote core now receives the residential service/add-on/custom-item breakdown too.
  • Hardened that residential bridge so estimator page loads now read the shared residential catalog without trying to resync the price book on every render, public residential estimators now merge partial pricing configs with the same defaults used by the office flow, and canonical residential line items now carry real shared price-book item links instead of saving as unlinked shadow rows.
  • Finished another residential quote-core hardening pass so estimate edits now persist roof, gutter, and saved measurement-source fields correctly, add-on rebuilds match stored selections by label instead of fragile array position, and PDF/email regeneration waits for the canonical residential line-item sync before it rebuilds customer-facing documents.
  • Followed that residential hardening pass with safer rollback behavior when quote-core sync fails during save, and kept template-based custom items linked to the shared residential price-book rows even though editable custom items use runtime-generated row ids in the estimator UI.
  • Moved residential estimate create/update onto the shared quote-core persistence layer instead of leaving save ownership inline in the residential API routes. Residential writes now save quote snapshots and quote events alongside the canonical line-item sync, residential send plus public view flows now write the same shared delivery-state and quote-event telemetry used by generic quotes, and API-created residential estimates now land on that same shared quote-core path too.
  • Continued the quote-core migration by moving accepted-side effects onto one shared helper for generic and residential estimate flows. Customer signatures and accepted-status updates now share the same event logging, webhook dispatch, pipeline history, gamification credit, and accepted-estimate automation path instead of duplicating those follow-up actions in separate route branches.
  • Extended that shared residential acceptance path into customer package selection too. When a signed customer accepts one of the residential proposal options, CE Pro now runs the same accepted-event, pipeline, webhook, gamification, and accepted-estimate automation flow instead of only overwriting the selected package totals on the estimate row.
  • Extended the shared quote-core public link model into residential base estimates too. New residential saves now keep a persisted public quote token in delivery_state, office/customer open actions can reuse the shared /quote/[token] page for the base estimate, and the shared public quote page can now render residential estimates alongside generic quotes while proposal-option and maintenance-plan pages remain on their specialized screens.
  • Started the residential offer-layer bridge under that same quote-core rollout. Residential proposal options and maintenance plans now mirror into hidden child estimates plus shared estimate_offer_groups / estimate_offer_members records, and customer proposal-package picks or maintenance-plan decisions now keep those shared offer pointers in sync even though the current residential public pages still render from the legacy JSON fields for compatibility.
  • Moved the next residential customer-link slice onto the shared quote page. When a residential estimate already has a persisted public quote token, the legacy /estimate, /proposal, /maintenance-proposal, and unified /p/[id] customer links now redirect into the shared /quote/[token] experience so the customer sees one quote page with the estimate, package-option, and maintenance tabs instead of separate residential public readers.
  • Pulled another commercial quote-core seam into shared ownership. Fleet and commercial building proposal sends plus public acceptance now reuse the same shared lifecycle writer for source-proposal status updates, sales transition logging, pipeline advancement, and linked-estimate follow-up instead of each path maintaining its own copy of that lifecycle bookkeeping.
  • Started the first fleet quote-core migration slice under the existing commercial proposal flow. Fleet linked estimates now normalize through a real fleet wizard plugin and write canonical shared quote-core line items, so the estimate workspace tied to a fleet proposal keeps a structured fleet service/add-on breakdown instead of only the older compatibility JSON snapshot.
  • Extended that same quote-core bridge pattern into commercial building proposals. Linked commercial building estimates now normalize through a dedicated wizard plugin and write canonical per-property service line items, so the shared estimate workspace holds a structured building-service breakdown instead of relying only on the older compatibility snapshot.
  • Restored the owner-facing Admin > Bug Reports inbox so it no longer disappears when ownership records are reordered, and widened that inbox back to the owner's accessible org scope so bug reports from any managed workspace keep rolling into the same review queue instead of narrowing to only the currently switched child workspace.
  • Added direct Residential and Holiday Lights shortcuts to the main Settings hub so those workspace pages are easier to find without adding more permanent items to the left sidebar.

2026-03-27

  • Consolidated residential estimates, generic quotes, holiday lights, fleet proposals, and commercial building proposals onto one shared review-and-send modal. The office now edits the email and SMS copy in the same module everywhere, generic quotes no longer fire immediately without review, and the residential plus holiday-lights send routes now honor the edited email/SMS copy instead of silently falling back to their old defaults.
  • Fixed the fleet proposal Review & Send margin mismatch so the review summary and AI review now use the saved quote's real fleet pricing, wash-time assumptions, visit schedule, and fleet cost settings. The old hardcoded preview calculator that could show a healthy gross margin while the quote was actually below target is gone, and seasonal proposals now use the full quote mix instead of only one season's visit math.
  • Hardened estimate and invoice attachments so uploads now accept a defined set of images, PDFs, TXT or CSV files, and common Word/Excel/PowerPoint documents, then verify the uploaded bytes before saving. Renamed or mismatched files are rejected with a plain-language message instead of being stored anyway.
  • Tightened another production error-handling pass across sign-in, review-request creation, estimate-list fallback loading, photo deletes, and attachment deletes. Those flows now keep raw auth, storage, and database details in server logs while the operator sees the shorter action-oriented message.
  • Moved Jobs and Invoices tag filters fully into the database join path instead of loading every matching client id into app memory first, which makes tagged list views hold up more cleanly in larger workspaces.
  • Fixed the Google Places address dropdown inside the office lead and client dialogs so staff can click an autocomplete suggestion directly again instead of having to use the keyboard to apply it.
  • Fixed fleet proposal draft creation when the client is prefilled from a lead or native CRM record. The fleet save flow now creates or reuses the correct proposal-side contact automatically instead of failing until the office switches to a custom manual client, and it no longer rewrites another saved proposal contact just because two records share the same office email or phone.
  • Fixed the Fleet Inventory step so active org vehicle types load with the full pricing/category data the wizard expects, while the fleet read path now stays limited to fleet/commercial readers instead of exposing fleet pricing and margin settings to broader estimate-only roles.
  • Reworked the admin header search into a more compact trigger so the command palette still opens from the top rail or Cmd/Ctrl + K, but the workspace chrome no longer leads with the oversized search field.
  • Fixed seasonal fleet proposal totals across review, customer view, and PDF export. Saved fleet drafts now persist their month-range schedule split, seasonal biweekly service now counts as two visits per configured month instead of averaging to an extra visit, and regenerated proposals keep the same annual math after reopen or resend.
  • Reworked fleet proposal PDFs so seasonal pricing now prints as separate standard and alternate schedule sections, while the Scheduling & Service Plan page lists every service month in the split cadence instead of collapsing the whole year into one generic frequency line.
  • Fixed commercial building and fleet proposal PDF cover dates so saved date-only proposal records print on the correct calendar day instead of shifting backward by one day in U.S. time zones.
  • Added editable commercial proposal default terms in Admin > Commercial > Settings for both fleet wash and commercial building proposals, then wired those defaults into the PDF generators whenever a proposal does not override its own terms. The main Settings workspace and left navigation now expose the commercial dashboard/settings routes more directly, while Admin > Fleet > Settings keeps the fleet-specific setup tabs and links into the shared proposal-defaults editor.
  • Hardened the public commercial and fleet signature flow so customers can no longer accept those proposals without a valid saved signature image. Signed-proposal bell alerts now refresh live in the admin header, accepted-alert writes retry before giving up, and downstream activity/pipeline/automation hiccups no longer turn a completed customer signature into a false failed-sign result after the proposal is already accepted.
  • Fixed the shared commercial-client detail API for fleet/commercial proposal workspaces so linked proposal-side contacts load again on older production schemas that never had a proposal_clients.updated_at column. The linked tags/contact card no longer falls through to Failed to load commercial client for those records.

2026-03-26

  • Extended every current trialing workspace by another 30 days on the live system, using the later of the existing trial end or "today" as the base so already-expired workspaces were restored instead of staying blocked.
  • Raised the default trial length for newly provisioned waitlist workspaces from 14 days to 44 days, and updated the app's public trial copy to match.
  • Fixed Google Maps preview availability so the internal Property Visual, MapMeasure, and holiday-lights Street View routes can fall back to NEXT_PUBLIC_GOOGLE_MAPS_API_KEY when GOOGLE_MAPS_SERVER_API_KEY is missing, while still surfacing the plain-language unavailable message if that public key is browser-only or denied for server-side geocoding / Street View requests.
  • Added the shared Property Visual workspace to the standalone Generic Quote Builder, so office users can open Street View, satellite imagery, CompanyCam links, and quote-linked site photos from the /admin/estimates/quote flow. New standalone quotes can now save a draft in place from the panel to unlock persistent photo uploads before the quote is sent.

2026-03-25

  • Fixed the lead-creation dialog so teams with client-management access can create a brand-new customer inline instead of leaving the workflow to build the client first. The same dialog now auto-selects the only saved property when a linked customer has exactly one address on file.
  • Fixed the Google Places suggestion handling in lead and client address flows so clicking text inside an autocomplete suggestion no longer closes the dialog before the address is applied.
  • Fixed commercial building and fleet proposal launch links that start from a lead record. New proposal pages now honor lead_id as well as client_id, so the customer preloads correctly on step 1.
  • Stabilized the admin Estimates list first render so date and stale-follow-up badges hydrate deterministically instead of tripping the production React mismatch error on page load, while keeping the displayed estimate date on the office user's local calendar.

2026-03-22

  • Added a dedicated Supabase region-migration guide plus repo-side cutover, audit, and validation runbooks so the Oregon-to-Virginia project move has an explicit checklist for auth settings, mobile config, storage buckets, edge functions, and the post-cutover stress rerun.
  • Fixed two stress-test regressions on the admin office data layer: lead-source admin reads no longer request a non-existent updated_at column, and Jobs/Invoices pagination now returns an empty page instead of a 500 when the requested offset is beyond the currently available rows.
  • Followed that pagination fix with a production-shape compatibility patch so the empty-page guard also catches the real PostgREST 416 Range Not Satisfiable response format returned by Supabase.
  • Added a repeatable developer stress-test harness and runbook for the core office hot paths, including the server-rendered admin dashboard/list pages, paginated Jobs and Invoices APIs, cached reference-data reads, and the health/queue checks we now rely on for scale validation.
  • Followed up on the Phase 2 async-delivery pass so queued recovery, setup-link, account-reset, and manual admin-email jobs now retry on transient provider failures instead of dead-lettering after a single blip once the user already received a success response.
  • Tightened the new direct-upload attachment surface so invoice attachment reads and writes now follow billing permissions instead of estimate permissions, which closes the cross-surface access mismatch between estimate-only and billing-only roles.
  • Hardened the Phase 4 paginated Jobs and Invoices list helpers by sanitizing free-text search terms before they are interpolated into PostgREST OR filters, preventing punctuation-heavy searches from breaking or reshaping the filter expression.
  • Scrubbed the remaining enqueue-failure email logs so forgot-password, setup-link, account-reset, and admin-email queue failures stop writing plaintext recipient addresses into structured logs.
  • Followed up on the Phase 4 scale pass by hardening the root request proxy against injected active-org context headers, so request-scoped org hints are now stripped from inbound traffic and only re-applied inside the trusted admin proxy flow.
  • Tightened the baseline /api/admin/* middleware throttling again so those buckets now key off the authenticated admin user when one is present, which prevents one fast user behind a shared office IP from rate-limiting everyone else on the same route shape.
  • Fixed a cached lead-source regression in the admin reference-data layer so active-only lead-source reads stop leaking inactive records back into office pickers and settings screens.
  • Removed the invoice-number race from admin single-create, admin bulk-create, and API v1 invoice creation by moving all three paths onto one shared atomic allocator seeded from existing invoice numbers for the current org and day.
  • Smoothed the server-hydrated Jobs and Invoices admin pages so their initial React Query payload is treated as fresh instead of immediately double-fetching crews, trucks, and first-page list data on mount.
  • Tightened the background-job cron worker to claim a smaller bounded batch per run, and added dedicated created-at indexes for the primary Jobs and Invoices admin list queries so those Phase 4 list pages hold up more cleanly under real office traffic.
  • Followed up on the Phase 5 operations pass by making /api/health fail fast when Supabase or the internal queue snapshot is slow, instead of hanging until the hosting platform times the request out.
  • Tightened the campaign delivery worker so one failed recipient no longer aborts the rest of the batch or skips the campaign stats reconciliation step.
  • Scrubbed structured email-worker logs so recovery and manual admin-email jobs stop writing full recipient addresses into the production log stream, while still leaving masked recipient detail for debugging.
  • Fixed the background-job processor metrics so jobs that fail before they can be finalized are tracked as stuck processing work instead of being misreported as dead-letter completions they never actually reached.

2026-03-21

  • Started the Phase 1 platform-stability pass so authenticated admin, onboarding, super-admin, and logged-in customer routes now run through a shared root session proxy instead of relying on scattered session-refresh behavior. That gives the app a more reliable baseline for session continuity as traffic grows.
  • Added a baseline middleware rate limit across /api/admin/*, so every admin API route now inherits a shared protection floor even before any tighter route-specific throttles apply.
  • Followed up on that root session work so mixed-auth office API surfaces like /api/email, /api/sms, /api/estimates/*, and /api/holiday-lights/* also refresh browser session cookies when staff reach them from the app, while inbound webhook routes remain outside the proxy on purpose.
  • Added a new uncached /api/health endpoint for uptime and deployment monitoring, and tightened the shared Supabase bootstrap path so missing critical env vars now fail loudly instead of surfacing later as partial runtime breakage.
  • Added app-wide route and global error boundaries so unexpected render failures now fall back to a recovery screen with retry actions instead of dropping staff onto a blank white screen.
  • Tuned that new reliability layer so the baseline admin API limiter now buckets by normalized route shape instead of forcing every office user behind one shared IP bucket, and the shared crash fallback now routes people back to the right product surface instead of always sending everyone to /admin.
  • Began the Phase 2 async-delivery pass by moving one-time marketing sends, manual admin inbox emails, forgot-password delivery, setup-link emails, and account-reset emails onto the shared background jobs queue. Those flows now return faster and finish delivery on the minute-by-minute worker instead of waiting inline on email fan-out.
  • Added short-lived server-side caching plus tag-based invalidation across the admin services, add-ons, lead-sources, pricing, and tags reads, and added TTL caching to the office dashboard metrics so common admin screens stop hammering the same database queries on every load.
  • Followed that hardening pass with stricter admin-boundary and auth work: sensitive admin routes now rely on explicit permission helpers instead of auth-only org membership, new recovery and invite emails prefer the server-established auth callback path instead of the older client token handoff, session timebox and inactivity limits are now active in Supabase config, and the external API v1 CORS behavior is narrowed to an explicit allowlist instead of returning authenticated responses to every origin by default.
  • Started the 10K framework work by introducing a shared org-cache abstraction, aligning admin proxy org-selection with the same active-org resolution model the page layer uses, adding hot-path indexes for estimates, jobs, and campaign messages, and increasing the background-job worker duration and throughput budget for the async queue.
  • Rebuilt the admin Jobs and Invoices workspaces around paginated query-backed list APIs, moved those pages onto server-first initial loads plus React Query hydration for smoother first paint and cheaper refreshes, and removed the old fixed 200-row ceiling from those two high-traffic office views.
  • Tightened that Phase 4 framework pass again so request-scoped active-org hints are validated against the same franchise-aware membership rules instead of being blindly trusted, and the new Jobs and Invoices pagination now pushes search and status filtering through the server query path so records do not disappear just because they are not in the first loaded slice.
  • Added a structured logging layer around the background worker and internal delivery jobs, expanded /api/health so internal monitors can inspect queue backlog with the cron bearer secret, and documented the incident, monitoring, and backup/restore operating model in the platform runbooks.
  • Followed up on the Phase 2 queueing rollout by fixing two campaign correctness edges and one auth-recovery edge: active background-job dedupe keys now only block duplicate work while a matching job is still pending or in flight, campaign dispatch stat reconciliation now runs through an atomic database sync instead of an app-side last-writer-wins update, and forgot-password throttling now keys off the normalized email address while collapsing immediate duplicate recovery jobs into one queued send.
  • Followed up on the Phase 3 auth-boundary work by separating read and destructive permissions more cleanly: estimate deletes now require estimates.manage, lead detail reads now use the read-side client permission instead of the lead write tuple, and the API v1 response helper only emits Vary: Origin when it actually returned a CORS allow-origin header.

2026-03-20

  • Added shared custom date-range filtering across the admin Estimates, Leads, Jobs, Invoices, and Pipeline workspaces. Office staff can now use quick presets or open an exact start/end picker instead of being limited to fixed canned windows.
  • Added a Jump to date picker on the Schedule board so dispatch can open a specific service date immediately instead of stepping day by day through the calendar header.
  • Hardened the customer portal and logged-in customer pages so proposal lookups, estimate history, and message history stay isolated to the correct live provider workspace. Customers who share the same email across multiple companies now see a safe direct-link prompt instead of mixed account data, and proposal search results still load even when secure customer-link signing has not been configured yet.
  • Tightened admin invoice payment-link generation so the office only reuses or writes hosted payment links for live invoices in the active workspace.
  • Added the same preset and exact custom date-range controls across the Analytics report suite, including Revenue, Margins, Services, Sales Cycle, Geography, Lead Source ROI, Retention, Operations, Schedule, Franchise, Automations, and Holiday Lights.
  • Followed up on the analytics date-range rollout so incomplete custom URLs now fall back safely instead of expanding into accidental open-ended ranges, custom typed dates use the same local-day boundaries as presets, and first-load server rendering now matches the shared picker logic across the analytics pages.

2026-03-19

  • Fixed the fresh account-recovery and workspace-setup email flow so valid invite and password-reset emails no longer die on the Invalid or expired reset link screen. Recovery, invite, magic-link, and super-admin reset emails now land on a client-side auth handoff that finishes the Supabase session before sending the user into the password form or dashboard, and the expired-link card can now send a fresh access email inline without bouncing the operator through a separate forgot-password page first.
  • Followed up on that auth-link recovery rollout so the Need A New Link? retry action on the temporary Finishing Sign-In handoff now keeps the original workspace redirect attached too. Multi-workspace waitlist owners and brand-new team members no longer lose the invited org context if they need one more recovery email from the handoff step itself.
  • Closed the last false 500 save edge on residential estimate and generic quote creation. First-time draft saves now reload the just-written estimate summary when the insert succeeds but PostgREST does not return the full row immediately, so the office no longer sees a failed-save toast after the draft is already stored.
  • Hardened the invite and password-recovery flow again for provisioned workspaces. Forgot password is now rate-limited, no longer reveals whether an address is waiting on first-time setup versus a normal account, and expired invite recovery now routes through the shared forgot-password screen instead of firing another access email directly from the reset page.
  • Followed up on that auth hardening with another reliability/security pass. Customer-token estimate status updates are now write-scoped to the estimate's owning org, office-side estimate signing is rate-limited too, and pending-invite recovery now falls back to the standard reset path instead of surfacing a server error when the auth-admin lookup has a temporary hiccup.
  • Tightened team-access resend safety so the system verifies the generated auth user matches the membership before any workspace access email is sent. That closes a bad-edge case where a mismatched setup link could have been delivered before the guard tripped.
  • Fixed commercial proposal draft persistence so the selected sales rep now saves with both fleet and building proposal drafts, and brand-new building drafts keep their property rows and service line items on the first save instead of dropping that scope until a later edit.
  • Hardened commercial proposal client creation on both fleet and building draft POST routes so new proposal-side client records are normalized and validated before they are inserted. Malformed emails or bad client payloads now fail as clean input errors instead of quietly saving broken proposal contact records.
  • Fixed the admin Jobs tag filter for larger workspaces so tag searches are scoped before the 200-row list cap. Offices with more than 200 jobs now get the full matching tagged set instead of a silently incomplete subset from only the newest rows.
  • Hardened the public estimate customer-action flow so only live sent/viewed proposal links can be accepted, declined, or signed. Draft estimates no longer jump straight to an accepted state just because someone has a token, and the public-route auth test suite now covers the estimate status endpoint too.
  • Fixed invited workspace access again so waitlist-provisioned owners and brand-new team members who click Forgot password before completing first-time setup now get a fresh workspace setup link automatically instead of a generic reset email that cannot finish activation.
  • Fixed the Bulk Schedule job write path so newly created jobs are marked for the same live environment that the Admin Jobs list reads. Bulk-created jobs now appear immediately in Admin > Jobs instead of being hidden by the list filter.
  • Fixed the admin Jobs list API so the page no longer crashes on workspaces where the old jobs.tags column is absent. Jobs load again, recently created work reappears immediately in Admin > Jobs, and the tag filter continues to use the linked client tags instead of a missing job-side field.
  • Fixed the shared linked-contact tags card on commercial building proposal detail pages so stale or deleted linked contacts no longer throw a false Client not found toast during page load. Those proposals now open cleanly and show an unavailable state for the missing linked record instead of interrupting the office with an error toast.
  • Fixed the shared analytics export picker so the print-friendly export no longer downloads with a fake .pdf filename. The analytics export menu now labels that option as Print, and downloaded files keep the correct server-provided name and .html extension for browser printing or Save as PDF.
  • Fixed commercial and fleet proposal deep links that start a new quote from an existing client. New proposal pages now honor the incoming client_id on load, prefill the selected client on step 1, and avoid the loader/invalid-id failures that previously showed up when staff launched commercial or fleet proposal creation from a client shortcut.
  • Fixed admin team invite and resend emails so the generated setup link keeps the invited workspace id in the redirect path, and access-email sends now fall back to a recovery-style setup link when the direct magic-link path is unavailable. Team members now return to the same admin workspace that sent the invite instead of falling back to a generic /admin redirect or a failed resend toast.
  • Fixed the admin invoice detail API so invoice pages no longer depend on a removed customer_phone column just to load. Older invoice records now open reliably again, which also keeps the invoice-side payment workspace reachable instead of falling through to a false Invoice not found state.
  • Fixed the admin-side estimate acceptance preview so office users can complete the same View Public Page signature flow without the sign step silently failing just because they are signed in as staff instead of following a customer-token link. Accepted estimate status changes are also more resilient now, so a follow-up automation or sales-pipeline logging hiccup no longer makes the UI report a failed save after the estimate status already changed.
  • Fixed the shared office Collect Payment modal width on estimate detail so the payment workspace now honors its full desktop frame instead of collapsing into the small default dialog size and squeezing the left-side payment setup pane into unreadable vertical text.
  • Fixed the standard estimate send and resend flow so CE Pro now confirms email delivery before it reports success, and already-sent quotes can be resent without tripping the old draft-only guard that produced "Quote updated but failed to send" after a legitimate retry.
  • Fixed the commercial building proposal send flow so the review step now confirms the email destination after a successful delivery, and follow-up pipeline/history sync issues are surfaced as warnings instead of a misleading hard failure after the client email already went out.
  • Fixed the commercial building client picker and linked tags flow so native CRM contacts can be swapped with Change during edits without crashing the wizard, and older building proposals tied directly to native clients no longer show a false Client not found error on the detail page.
  • Fixed the commercial building proposal wizard so the Property Type, Primary Surface, and service-catalog pickers load again on both new and edit flows instead of failing behind silent 500s. The review step now also includes an explicit Save Draft button, and the send area explains whether the draft or client email is the missing requirement when the email action is disabled.
  • Fixed the commercial building catalog loaders so proposal editors with the broader commercial proposal permissions can still see the Property Type, Surface Type, Service Type, and pricing-setting options. When a saved workspace catalog still fails to load, the wizard now falls back to built-in building defaults instead of leaving those dropdowns and the service catalog empty.
  • Fixed the residential estimate wizard so client-prefilled New Estimate links now keep the selected CRM client attached through save instead of dropping the known client_id and trying to recreate the customer during estimate creation.
  • Fixed office-side residential estimate send fallback when secure customer-link signing is not configured. Staff can now still send the estimate email with the PDF attached, and the send flow returns a clear warning that the browser-based estimate link is temporarily unavailable instead of blocking delivery with a CUSTOMER_TOKEN_SECRET setup error.
  • Fixed invoice send retries for workspaces that still do not have secure customer-link signing configured. The office can now send the invoice email anyway, with any stored billing links included when available, instead of the whole send flow stopping on a generic setup failure.
  • Fixed the invoice detail payment workspace so long customer names and emails now wrap cleanly on the page, and the invoice-side Collect Payment keyed-card flow no longer trips the intermittent office crash caused by the dialog re-registering its Stripe submit callback every render.
  • Fixed the Manage Billing button so the Stripe portal return URL is built from the current request origin instead of a potentially stale app URL setting. That removes the "Not a valid URL" failure mode when admins open the billing portal.
  • Fixed the mobile Messages layout so the inbox uses mobile-safe viewport sizing and long customer names no longer overflow the thread header on smaller screens.
  • Fixed the analytics export whitelist so Retention, Operations, Schedule Efficiency, and Franchise reports now export through the shared CSV/PDF endpoint again instead of returning a 400 for valid report types.
  • Fixed the shared analytics export button so failed report exports now show a visible error message instead of silently doing nothing when a report-specific export throws. That gives the office immediate feedback on Retention, Operations, Schedule Efficiency, Franchise, and any other shared analytics export path that cannot generate a file.
  • Fixed the customer estimate acceptance flow so signed estimates now keep the accepted state even if follow-up automation has a transient failure, and customer request-changes notes now stay attached to the rep-facing estimate record instead of getting dropped during the save fallback path.
  • Fixed the waitlist join flow so duplicate submissions now return a retryable conflict instead of locking the form into a success state. Users can try a different email after a failed join attempt without refreshing the page.
  • Fixed the holiday-lights send queue so admin quote sends now stay on the holiday-lights email and SMS delivery path instead of falling back to the residential estimate sender behind the scenes.
  • Fixed holiday-lights proposal sending so the office now gets a real email-delivered confirmation before CE Pro reports success, failed sends stay retriable instead of silently flipping the quote to sent, and the header action becomes Resend Proposal after the first successful delivery.
  • Hardened the measurements and recurring-template admin routes so load/save/generate failures now show plain-language errors instead of raw database messages, while still preserving the expected not-found and validation statuses for the user.
  • Hardened the mobile payment, public invoice checkout, and Stripe billing-portal error paths so staff and customers now see plain-language payment or setup guidance instead of raw provider and Stripe exception text. Safe business-rule feedback like missing records or invalid amounts still stays readable, while the detailed failure context now stays in server logs.
  • Fixed the shared office Collect Payment dialog so long invoice labels, customer names, and receipt email text now wrap inside the modal on invoice detail instead of overflowing past the dialog frame.
  • Fixed invoice collect-payment retry behavior so invoice pay-link actions reuse an already stored invoice link instead of failing when the customer-token setup path is unavailable, which also keeps older invoices resilient during send/link retries.
  • Updated the Workiz setup flow so the copied value is now just the webhook endpoint while the org-specific webhook key is copied separately. That keeps the secret out of the copied URL/query string while still letting teams assemble the full Workiz callback URL when they configure the integration.
  • Fixed a late admin review batch across job detail editing, refunds, client updates, promotions, and holiday-lights estimate sidecars. Job detail no longer opens in a false dirty state with the Save button stuck on, Stripe refund actions now write an internal adjustment trail before the gateway refund is issued and failed Stripe refund attempts now mark that pending trail as failed instead of leaving it hanging, client detail updates fail safely instead of returning a truncated row after a refresh error, missing promotion deletes now return a real not-found response instead of silently succeeding, and holiday-lights sidecar creation now uses the same narrower config read path as the main holiday-lights estimator flow while keeping later client-link backfills scoped to the same workspace record.
  • Improved another backend throughput pass for automations, drip marketing, Workiz sync, and proposal expiration jobs. Matching one inbound event to multiple workflow runs now executes those runs in parallel instead of serially, large drip campaigns process due recipients faster once each message claim is secured, Workiz lead backfills upsert each page concurrently, and the nightly proposal-expiration sweep now preloads loss stages per org before it advances expired proposals.
  • Tightened another stability pass across billing, integrations, and estimate editing. The Billing page now keeps its main subscription controls visible even if invoice-history, referral, or AI-usage side data has a temporary loading failure; Workiz settings no longer leave the full inbound webhook secret sitting inline on screen; revoked external API keys now flip inactive immediately in the admin UI; and estimate detail status changes now surface the actual save failure instead of silently falling back to a generic toast.
  • Continued the admin API hardening pass across clients, schedule, commercial proposals, jobs, inbox threads, holiday-lights routes, and automation/follow-up flows. These routes now keep raw database/provider errors out of the UI while still logging the detailed failure context server-side, and a broad set of high-traffic reads now fetch explicit columns instead of whole rows by default.
  • Shipped the missing public v1 estimate update path so PATCH /api/v1/estimates/:id is now a real idempotent endpoint instead of a docs gap. External integrations can now patch estimate customer/contact metadata, address/property details, assignment, draft-or-sent status, source, and expiration through the same scoped API-key model as the rest of the public v1 surface, and the OpenAPI contract plus developer examples now match the shipped behavior.
  • Fixed a follow-up review batch in brands, operations QC, and workflow scheduling. Brand edits now leave untouched fields alone instead of resetting saved colors or optional settings, checklist-template edits no longer reactivate inactive templates unless you explicitly flip them back on, waiting workflow runs now verify that their wake time has actually arrived before the engine resumes them, and automation Least Loaded assignment now compares only active estimates, jobs, and leads instead of lifetime historical totals.
  • Fixed a follow-up review batch across automations, estimates, AI review, and recurring templates. Automation Assign User now distinguishes stable round-robin routing from real least-loaded routing based on current assigned leads/jobs/estimates, delayed workflow runs resume from Waiting correctly, bulk estimate status changes now report real batch success instead of a false failure toast, optional admin note fields no longer overwrite untouched values with null, holiday-lights AI review compares nearby work against the first-year annual contract price when present, and visit-capped recurring templates now count manual skips or catch-up occurrences correctly so they end on time.

2026-03-18

  • Followed up on the payments and analytics refactors after review. The office keyed-card flow now keeps the Stripe card form mounted when staff adjust the amount or tip, but it requires an explicit Refresh Card Entry before charging so the final Stripe intent stays in sync, and sales-cycle timing now honors commercial decline timestamps when they exist instead of stretching loss timing to a later generic close date.
  • Followed up on the latest inbox/dashboard refactor after review. Switching to a different customer conversation now clears the previous draft and resets the compose area before the new thread finishes loading, targeted inbox summary refreshes no longer apply sidebar workflow filters while patching a single row, and the admin dashboard now keeps empty commercial gross-margin cards on a clean dash while the sales rep pace card reflects new estimates created month-to-date instead of only currently sent ones.
  • Followed up on the dispatch and inbox refactors after another review pass. The route header no longer shows a misleading stop-level hourly-only label, route optimization guidance is now documented more clearly for truck-days without a saved shop origin, and targeted inbox fallback refreshes now normalize legacy phone and email thread keys before deciding a conversation row is truly missing.
  • Fixed the commercial and fleet proposal creation flows so selecting a native CRM contact or entering a new client no longer crashes the building client picker or sends synthetic ids into the proposal save APIs. Building proposals now handle sparse client-name data safely, and both commercial proposal modules now create or reuse the correct proposal-side contact record before saving.
  • Fixed the admin estimate send/resend flow so the quote builder now carries the generated customer token through the final send step, and email delivery routes now fail loudly when the mail provider is missing instead of reporting a successful send with no outgoing message.
  • Clarified the external API docs so the Developers section now explicitly shows that public v1 integrations can write notes to client, lead, and job records, with ready-to-use POST and PATCH examples for each workflow.
  • Fixed manager-led first-run onboarding for unfinished workspaces, especially franchise child locations invited through an operator workspace. Managers can now save the Company Info, Services, Pricing, and completion steps during onboarding instead of getting stuck behind generic save failures, and the onboarding UI now shows the actual API error text when a step is rejected.
  • Hardened the automation engine's internal bearer-secret handling so delayed workflow resumes, scheduled-trigger processing, direct run execution, and event-to-workflow matching now all use the same secret precedence and timing-safe bearer-token validation. This reduces edge-case failures where one internal automation endpoint could accept a request that another identical engine path rejected in the same environment.
  • Added a stricter browser Content Security Policy across the web app and aligned frame protections with the product's actual surfaces. CleanEstimate Pro still blocks external embedding, but the built-in same-origin message drawer can now load correctly while Stripe, Google Maps, Supabase realtime, analytics, and approved embedded panels continue to work under explicit allowlists.
  • Standardized high-risk billing, messaging, password-reset, webhook, and super-admin error paths so the UI now shows plain-language setup or action messages instead of leaking raw Stripe, Twilio, Mailgun, Supabase, or database error text. Common business-rule feedback like invalid payment amounts or missing records still stays readable, while low-level diagnostics now stay in server logs.
  • Added shared Zod-backed validation for the highest-risk messaging mutation routes, including manual SMS sends, manual email sends, inbox note creation, and conversation ownership/needs-response updates. Those inbox actions now reject malformed payloads consistently before they hit Twilio, Mailgun, or the unified messaging write path.
  • Expanded shared validation again across admin team and dispatch actions, including approval requests, handoffs, alert actions, rep availability changes, role-permission saves, territory edits, crew creation, ETA sends, job-status updates, route-day assignments, and crew-job assignments. These admin flows now reject malformed ids, ZIP codes, dates, and permission payloads before they can write partial schedule or settings data, and several of the touched routes now return cleaner setup errors instead of raw database messages.
  • Expanded the same validation pass into operations settings, including subcontractors, business locations, equipment records, and equipment maintenance logs. Those forms now reject malformed emails, ZIP lists, dates, rates, costs, and lifecycle payloads before they can save inconsistent operations data, and the touched routes now return cleaner admin-facing errors instead of raw database messages.
  • Extended that operations hardening into inventory items plus quality-control checklist and report workflows. Inventory writes now validate categories, quantities, reorder levels, unit costs, and reorder dates before saving, while QC templates and reports now validate nested checklist items, pass/fail states, notes, scores, and photo references before they create report history.
  • Hardened another settings/configuration batch covering brands, module-brand assignments, lead sources, and tag creation. Those admin forms now validate slugs, module keys, commission rates, sort order, ids, and tag colors before saving, use safer fallback handling when older tag tables are still rolling out, and return cleaner setup errors instead of raw database messages.
  • Tightened another workspace-settings pass covering pricing, email reply slugs, Twilio phone number storage, Google review links, and schedule dispatch defaults. These settings now validate E.164 SMS numbers, custom inbox slugs, Google review URLs, route-origin coordinates, truck-capacity values, and pricing version payloads more consistently, while the settings UI now surfaces the review-link and slug errors directly instead of failing silently.
  • Hardened the service catalog admin too. Service, add-on structure, and custom item template saves now validate slugs, ids, prices, sort order, and units more consistently, the matching admin APIs return cleaner setup errors instead of raw database messages, and the Services page now keeps edit dialogs open and shows the actual failure reason when a save or toggle request is rejected.
  • Tightened the estimate detail mutation flows for rep assignment, appointment scheduling, and estimate-to-job conversion. Those actions now validate ids, dates, time windows, and override flags more consistently, keep cross-org or archived users from being assigned as reps, reject invalid appointment ranges before they save, and return cleaner admin-facing errors instead of raw database messages.
  • Hardened the marketing campaigns, promotions, and legacy follow-up sequence admin APIs too. Campaign drafts now validate scheduled send windows and structured drip-step payloads before saving, promotions now enforce cleaner promo-code/date-window validation and safer admin-side errors, and legacy follow-up sequence/template saves now reject malformed step payloads or invalid business-hour ranges instead of partially overwriting the workflow.
  • Tightened the external API key admin flows as well. API key create/update screens now validate environments, scopes, rate limits, and expiration values more consistently before saving, the request-log endpoint now validates log-limit input, and the API key admin routes return cleaner setup errors instead of raw provider or database messages.
  • Improved commercial pipeline stage saves so bulk stage edits now validate the full payload before writing and update stage rows in parallel instead of one at a time. Reordering or renaming a full stage board should feel faster now, and malformed stage batches fail cleanly instead of leaving a partial stage configuration behind.
  • Tightened the dispatch route-sync save path as well. Daily route timing syncs now validate dates, stop ids, metrics, and map coordinates before writing, coordinate backfills run in parallel instead of nested per-stop awaits, and route-sync storage failures return cleaner dispatcher-facing errors instead of raw database messages.
  • Followed up on the same hardening pass with a cleanup batch for edge cases the review caught. Inventory items can now be patched one field at a time without re-sending every create-only field, campaign drip steps reject blank message bodies, legacy follow-up sequence APIs now respect the same marketing.view and marketing.manage permission split as the rest of Marketing, step reads stay tenant-scoped, proposal handoffs now require the same estimate-manage permission as other assignment changes, team approval/handoff routes no longer leak raw database errors, shared API sanitization now still logs unexpected failures even when a route forgets to pass a custom log context, and the public promo-code validator now rate-limits repeated bursts so anonymous code-guessing is harder.
  • Continued the performance pass in AI and automation analytics too. AI lead-score recalculation now persists refreshed scores in one bulk upsert instead of one row at a time, and the automation analytics sequence-performance panel now rolls up enrollment counts in one org-scoped query instead of issuing a separate enrollment query for every legacy follow-up sequence.
  • Finished the same loop-await cleanup in bulk job scheduling. Bulk Schedule now resolves truck assignments in parallel and appends per-truck route-stop groups concurrently, so larger multi-property batches with route mode should create jobs and initial stop order faster before dispatch opens the board.
  • Started the inbox performance pass too. The admin messages page now only refetches the open thread when that thread's own messages change, and the communications contact-summary read now uses a tighter recent-message window instead of scanning an oversized backlog on every reload.
  • Continued the inbox performance pass with a more targeted live-refresh path. The shared Messages list now refreshes only the contact rows that changed in the common case instead of reloading the entire inbox after every new message, and the communications summary route now looks up only the clients tied to the recent contact set instead of hydrating every client in the workspace just to decorate the inbox list.
  • Followed up on the inbox and promotions hardening pass after review. Service-restricted promo codes now stay blocked until the estimate includes a matching service instead of validating when service data is missing, targeted inbox sidebar refreshes now fall back to a full reload instead of dropping a conversation row on a summary miss, and inbox read-marking now catches both normalized thread keys and raw inbound phone values more reliably.
  • Cleaned up another commercial admin error-leak cluster. Commercial proposal template and fleet vehicle-type admin actions now return plain-language save/delete failures instead of raw database messages, while still logging the detailed failure context server-side for support and debugging.
  • Followed up on those commercial admin routes after review. Fleet vehicle-type edits now return a real not-found response when the record has already been removed from the active workspace, and proposal-template edits now validate PATCH payloads before save so bad field types fail cleanly as input errors instead of throwing inside the route handler.
  • Tightened another small admin error-handling cluster around AI settings, AI conversation history, estimate attachments, and estimate photos. Those routes now return plain-language load/save/upload failures instead of raw provider or database text, while the detailed diagnostics stay in server logs for support.

2026-03-17

  • Smoothed the missing Google Maps server-key error path so Property Visual previews now show a plain-language unavailable message instead of exposing the raw GOOGLE_MAPS_SERVER_API_KEY is not configured. config string in the admin UI.
  • Refreshed the /admin workspace with the CleanEstimate Pro brand kit while keeping routes, workflows, and page structure intact. The admin shell now uses the brand blue/slate palette, tighter shared surfaces, brand-safe sidebar states, stronger mono KPI treatment, and updated report/schedule/settings chrome without changing behavior.
  • Fixed the generic quote and estimate edit save-response path so successful draft saves no longer return false 500 errors after the record is already stored, and estimate detail pages now surface customer change requests with submitted totals plus included/removed line items. Manual SMS sends and Stripe billing-portal launches also return clearer setup and provider errors instead of opaque server failures.
  • Tightened the admin shell again so the top search, XP, and Help/Alerts bubbles now share the same height and visual treatment, the franchise org switcher has stronger contrast in the dark sidebar, and the oversized Operations Command hero on the main dashboard has been replaced with a smaller office snapshot plus direct report links.
  • Followed up on the admin shell spacing so the XP, Help, and Alerts bubbles now sit in cleaner individual pills with tighter alignment and less wasted space in the top bar.
  • Followed up on the admin shell cleanup by removing the non-essential role, focus, and mode card from the left rail, shrinking XP progress into a compact top-bar pill, and simplifying the sidebar create menu into a single Quick Start dropdown bubble.
  • Simplified the admin workspace header by removing the oversized command-center banner. The top of the admin app now focuses on a full-width search bar with Help and Alerts actions on the right, while workspace plan and trial badges moved into compact pills on the Settings and Account pages.
  • Updated franchise reporting visibility so it now turns on automatically when a workspace already has franchise hierarchy or multi-location capability enabled, instead of requiring a separate franchise-reporting toggle.
  • Updated the first-run onboarding screen to use the shared CleanEstimate logo header and removed the duplicate text-only wrapper so new workspaces land in a cleaner branded setup flow.
  • Updated the password setup and forgot-password screens to use the same shared CleanEstimate logo header so the activation and recovery flow matches onboarding instead of showing the older text-only branding.
  • Fixed invite and password-setup emails so shared provisioning scripts and resend flows no longer leak http://localhost links outside a real development session. Workspace setup emails now fall back to the production CleanEstimate URL automatically when a non-dev process has a local app URL configured.
  • Fixed early onboarding for newly provisioned workspaces so starter services, pricing, add-ons, and related defaults now self-heal if bootstrap records are missing. The setup UI now keeps the Services and Pricing steps usable instead of leaving those screens blank or throwing a pricing-save error.
  • Fixed early-access workspace activation so waitlist-approved users no longer get dumped back into public waitlist/signup prompts when their original invite link expires or has already been used.
  • Waitlist and team-access emails now preserve the invited workspace context more reliably, including a password-setup recovery path for provisioned users who exist in auth but have never completed first-time sign-in.
  • Improved auth fallback screens so expired invite and magic-link flows prefill the invited email, route users toward a fresh setup link, and avoid treating provisioned workspace owners like brand-new public signups.
  • Fixed the expired setup-link recovery flow again so the reset screen now resends the actual workspace setup path for pending users, keeps the UI on the recovery state after resend, and preserves the target workspace context for waitlist-provisioned owners.

2026-03-16

  • Refreshed the core UI across the admin workspace, public marketing site, and mobile app around a shared operator-first visual system. The admin shell now uses a darker command-style sidebar and updated workspace chrome, the homepage showcases the live dashboard/operations/analytics/AI/mobile story, and the mobile tabs now use the tighter card, KPI, and section styling from the same system.
  • Updated the public pricing page so every plan card now shows Pricing coming soon instead of public numeric plan prices while still keeping plan names, feature differentiation, and waitlist CTAs visible.
  • Expanded the Analytics area into a shared reporting hub without removing the existing revenue, margin, lead source, service, geography, holiday-lights, or automation reports. Revenue now has its own first-class page, and the analytics rail adds retention, operations, schedule efficiency, and franchise reporting.
  • Upgraded Margin Analysis with sold-to-job conversion coverage plus clearer callback reporting that separates explicit callback-tracking records from inferred callback signals in job notes.
  • Upgraded Lead Source ROI with module filters, rep attribution, upsell revenue, and brand-funded source rollups, and added a new brand-funded toggle to Lead Sources so franchise and ad-fund reporting can use durable source tagging.
  • Added retention reporting for healthy, at-risk, dormant, and new accounts, including quick Generate Quote actions that jump into a prefilled estimate flow for the selected client.
  • Added operations reporting that combines crew productivity, crew pay totals, AR aging, and overdue invoice reminder actions using the same AI follow-up flow already available from invoices.
  • Added schedule-efficiency reporting for daily production, schedule fill rate, on-time performance, and reschedule/cancellation breakdowns.
  • Added franchise-gated reporting for royalty, ad-fund, and territory utilization rollups using the existing organization feature-flag model.
  • Limited the admin Marketing area to the approved workspace during tester rollout. Other workspaces now see marketing in read-only mode and cannot create, activate, edit, delete, or send campaigns and promotions.
  • Fixed team access links so invited users who already belong to another CleanEstimate Pro workspace now land in the invited workspace instead of getting stuck in the wrong org or being pushed toward waitlist/signup flows.
  • Expanded the admin XP leaderboard so each user can now see every achievement already earned on their profile plus the remaining role-track and universal achievements that are still available to unlock.
  • Fixed the admin Estimates tab loading path again so the summary RPC stays bound to the Supabase client instance, resolving a server-side 500 that could leave the Estimates page blank even when the underlying estimate data was healthy.
  • Fixed the admin Estimates center so it can fall back to the unified estimates table when the newer estimate-list projection objects have not been applied in the database yet, restoring list loading and search during rollout windows.
  • Hardened admin job detail loading so optional Crew Pay and assignment lookups no longer turn the whole page into a 500 when newer payroll tables are missing from the live schema cache.
  • Hardened invoice creation and invoice detail loading around secure customer-link signing. Draft invoices now still generate even if customer-link signing is not configured yet, and invoice detail pages stay readable instead of failing outright.
  • Updated invoice send and payment-link actions to return a clear setup error when secure invoice-link signing is unavailable, instead of a generic server error.
  • Improved the billing portal error path so Stripe customer-portal configuration or customer-record problems now surface as a temporary availability/setup message instead of an opaque failure.
  • Added rollout-safe fallbacks for missing payment and crew-pay relations in the live schema cache so office billing and job workflows degrade gracefully while databases catch up.
  • Finished Franchise Phase 4 beta hardening with 15-minute franchise summary refreshes, durable org-switch and cross-org franchise audit logging, franchise plan gating for parent-workspace management, descendant-read RLS coverage on the core org-scoped workflow tables used in the beta, and live-only franchise summary rollups.
  • Added the first self-serve franchise admin workspace on top of the beta hierarchy foundation, including the admin sidebar org switcher, franchise overview, descendant workspace roster, child-workspace detail screens, child-workspace user invites, operational reports, and the new Settings --> Franchise page for rollout guidance.
  • Added the Phase 2 franchise beta APIs for active workspace switching, parent-org overview, child-org management, descendant user listing, and child-org invites, plus the new franchise.view and franchise.manage permissions that gate those flows. Parent org admins can now keep inactive child workspaces visible for management and reactivation instead of losing them from the hierarchy tree after deactivation, and successful workspace-switch responses now tell the admin client to refresh session metadata immediately.
  • Laid the franchise hierarchy foundation in the database by adding parent/child org metadata, organization relationship records, scoped multi-org membership fields, reusable hierarchy traversal helpers, and locked-down relationship-table access for upcoming franchisor, operator, and multi-location account management.
  • Reconciled the developer docs, navigation, examples, and changelog around the actual public /api/v1 surface. The shipped external API remains the core workflow resources plus webhook management; the previously documented proposal and reporting endpoints are deferred and are no longer presented as live production routes.
  • Fixed franchise bug-report visibility so parent workspaces can review descendant workspace bug reports from the parent dashboard instead of only seeing reports created directly inside the active workspace.
  • Hardened franchise workspace switching to accept readable workspace slugs as a rollout-safe fallback when a stale browser session submits an older identifier format.
  • Hardened franchise workspace switching again so stale browser sessions can also recover when they submit an exact readable workspace name instead of the current UUID or slug format.
  • Fixed franchise workspace switching for legacy root workspaces whose ids are UUID-shaped but not version-stamped UUIDs, so operators like Lancaster can switch back from child workspaces without hitting Invalid workspace identifier.

2026-03-15

  • Added crew tip collection and visibility across invoices, admin payment collection, mobile job payments, and the Crew Pay dashboard. Customers can now choose 10%, 15%, 20%, custom, or no tip from the taxable amount, while cash, check, and card tips stay visible by method in crew-pay reporting.
  • Cleaned up the remaining mobile TypeScript regressions in the dashboard proposal list, About screen asset loading, and CRM client-provider mappings so the Expo app compiles cleanly again.
  • Rebuilt Crew Pay around versioned policy settings instead of hardcoded crew percentages, adding editable pool rates, solo and leader caps, pay-period and overtime controls, minimum-wage overrides, commission exclusions, crew split templates, route-day hourly-only overrides, and job-level participant compensation editing with callback tracking.
  • Followed up on the Crew Pay rebuild with payroll hardening fixes: minimum-wage top-ups now report deficits correctly, hourly overtime only counts hourly-only hours, callback-origin jobs no longer dilute the rest of a capped crew day, route-day save errors surface in the UI, and the new crew-pay policy tables now enforce tenant-scoped RLS plus database-level payout guardrails.
  • Added rollout compatibility fallbacks for Crew Pay so the admin crew-pay pages can still render safely while older org databases catch up on the latest split-template and compensation schema changes.
  • Fixed the inverted commissionTopUp formula so minimum-wage top-ups now correctly report employer deficits instead of commission surpluses.
  • Fixed overtime threshold tracking so commission hours no longer count against the hourly-only overtime threshold, preventing incorrect overtime pay on mixed-pay crews.
  • Excluded callback-origin jobs from daily cap group accumulation so they no longer dilute the capped commission pool for other jobs on the same crew-day.
  • Added RLS policies and positive-value CHECK constraints to crew_pay_policies and job_compensation_settings to enforce tenant isolation and prevent negative rates or unbounded commission shares at the database level.
  • Added commission share sum-to-100% validation in the job compensation editor UI and error feedback on route-day assignment save failures.
  • Replaced hardcoded 57/43 lead/tech pay preview splits in the schedule day route view with the active policy's configurable leaderMaxShare.
  • Added effective-date future validation to the crew pay policy editor so past-dated policy versions cannot be created from the admin UI.
  • Tightened Supabase type safety across the app by introducing strict client factories alongside the legacy loose path, fixing high-value schema drift in the admin dashboard, automations analytics, AI estimate review, chat route, Meta and Angi webhooks, and the holiday-lights customer portal mapper.
  • Replaced the dead inbound automation webhook auth path with the external API v1 scoped key model, requiring webhooks:write scope, SHA-256 hashed key comparison, and timing-safe verification. Existing integrations using the old webhook_api_keys table or X-Webhook-Signature HMAC path must migrate to scoped API keys.
  • Fixed the safeHashEqual function in the inbound webhook auth path to return false on invalid hex inputs instead of comparing two zero-filled buffers.
  • Wired the strict middleware Supabase client into the auth session refresh path and replaced the unguarded inline service client with the singleton createStrictServiceClient.
  • Added shared JSON boundary helpers for safely parsing unknown Supabase JSON columns into typed objects, arrays, and scalars without raw as casts.
  • Added an integration connections helper to replace direct organizations.integrations JSONB reads with a dedicated integration_connections table lookup pattern.
  • Stabilized the flaky public route auth test and added a real typecheck script to package.json.
  • Started the Phase A reliability pass by fixing the commercial bulk-scheduling template conversion regression, scoping admin team activity commercial proposal reads back to org-owned proposal IDs, switching CI back to Linux, and aligning the Mailgun inbound email setup docs around Bearer-header auth instead of legacy query-string secrets.
  • Followed up on the Phase A review by adding mobile Jest coverage to CI, escaping customer delivery email templates, documenting the required CUSTOMER_TOKEN_SECRET and GOOGLE_MAPS_SERVER_API_KEY deployment settings, recording residential MapMeasure applies as property_data_source = "mapmeasure", and clarifying that Workiz lead updates stay scoped to the org that owns the sync.
  • Rebuilt the admin Estimates list and linked client estimate history on a unified indexed read model that combines residential estimates, generic quotes, holiday lights records, fleet proposals, and commercial building proposals, so search, tags, rep filters, source filters, and pagination now run in SQL instead of the old in-memory multi-table merge path.
  • Hardened map preview and measurement APIs by moving admin and holiday-lights Google geocode and Street View lookups behind internal server routes, adding burst protection to MapMeasure endpoints, and improving inline visual-panel error states when Google cannot resolve a preview.
  • Started the Phase B reliability pass by switching follow-up, automation, SMS, invoice-payment, and legacy proposal-delivery links onto the canonical tokenized customer-link builders, so customer-facing estimate, proposal, maintenance, and invoice links stay valid across web and mobile sends.
  • Fixed mobile offline proposal delivery retries so failed background send-proposal calls stay queued instead of being marked complete, and made follow-up service merge fields tolerant of both legacy string arrays and the newer structured service-selection payloads.
  • Finished the next estimate-list correctness pass by filling the missing projection upsert fields, passing estimate type filters through the summary RPC, exposing entity_type in the admin estimates API, and adding shared proposal-client tags so fleet and commercial proposal records participate in the same tag filters as residential estimates.
  • Made client-account primary-contact promotion transactional and added shared tag management to fleet and commercial proposal detail pages, so concurrent admin edits cannot leave duplicate primaries and commercial contacts can now power unified estimate-list filtering.
  • Followed up on the Phase B account work by preventing non-primary client moves from clearing another account's existing primary contact, and by adding a rollout-safe fallback when the new set_client_account_primary_contact RPC has not been applied yet during deployment.
  • Finished the client account-integrity pass by moving contact-account membership changes onto a transactional RPC path as well, so primary-contact promotions and account moves no longer depend on a separate post-update reconciliation step.
  • Fixed the remaining legacy mobile proposal-delivery gap by adding a signed /p/:proposalId customer-token path for proposals without linked estimates, restoring secure SMS and email delivery for direct mobile-created proposal records instead of leaving those sends stuck on a missing-link error.
  • Hardened customer delivery links again by moving HMAC signing onto the dedicated CUSTOMER_TOKEN_SECRET, adding token expiry to estimate, proposal, maintenance, and invoice links, tightening the legacy proposal viewer's PDF allowlist, and fixing portal proposal lookups to filter in SQL instead of silently dropping older customer proposals after an org-wide top-50 cutoff.
  • Rebuilt the Phase C security pass on top of the shipped Phase B baseline, making shared public rate limiting fail closed by default, keeping public-customer-token and property lookup paths on explicit in-memory fallback only where needed, and finishing the admin auth-wrapper migration for the remaining high-traffic client, estimate, lead, schedule, and marketing routes.
  • Locked the admin Property Visual and Street View proxies behind estimate-view permission checks, required the server-side GOOGLE_MAPS_SERVER_API_KEY for those routes, rejected non-image Street View upstream responses, and moved the holiday-lights photo designer's Street View fetches onto the same internal proxy path.
  • Followed up on the Phase C audit by restoring the intended auth-only access model on lead, estimate, follow-up sequence, client-delete, and job-delete routes that still rely on record-level role checks, adding missing org and environment guards to follow-up step replacement, invoice payment-link writes, and schedule completion re-reads, and making public property auto-fill fail closed again when shared rate-limit storage is unavailable.

2026-03-14

  • Hardened core security and customer-delivery flows by enforcing admin API permissions server-side, locking holiday-lights AI mockups behind authenticated permissions plus image-size limits, moving inbound email webhooks to bearer-header auth, rate-limiting manual email/SMS/review sends and selected AI actions, and fixing signed customer links for SMS, Stripe returns, and public estimate/proposal access.
  • Migrated app email delivery from Resend to Mailgun, including batch marketing sends, inbound reply routing, delivery/open/click/failure webhooks, and the deployment DNS guidance needed to run the new email stack.
  • Added MapMeasure to the shared satellite Property Visual Panel so residential, commercial building, fleet, and holiday lights workflows can save geodesic map measurements with live draft totals, keyboard drawing shortcuts, and workflow-aware apply actions.
  • Hardened MapMeasure persistence and edit flows with atomic commercial proposal graph saves, safer residential estimate reconstruction on reopen, and saved measurement round-tripping for map-based property values.
  • Followed up with additional MapMeasure hardening for org-scoped target validation, draft undo and double-click stability, safer inline CompanyCam previews, and stricter commercial proposal payload sanitizing.
  • Hardened background follow-up, drip, and waiting-automation processing with claim-safe batching, restored the intended follow-up and drip cron cadence, and reduced duplicate-send risk during overlapping scheduler runs.
  • Rebuilt the public homepage with an estimate-first story, stronger mobile and desktop layout, and a sharper power-washing-specific visual design.
  • Updated the shared marketing navigation, footer, and sticky mobile CTA so the public site keeps the same stronger positioning across pages.
  • Added a new docs article covering the public website and waitlist experience.
  • Shipped and hardened external API v1 with scoped live and test API keys, atomic idempotent writes, stricter state validation, request correlation, rate-limit fail-closed behavior, cursor pagination, durable outbound webhook delivery, and a typed /api/v1/openapi.json contract.
  • Followed up with the final external API pre-production hardening pass for exact key-environment verification, live-only webhook queue processing, and additional idempotency guardrails.
  • Published Phase 2 proposal and reporting API planning/docs work, but the public /api/v1 contract remained limited to the core workflow resources plus webhook management.
  • Documented the three supported SMS consent flows in the public privacy policy and mirrored that legal guidance in the docs site.
  • Hardened background follow-up, drip, and waiting-automation processing with claim-safe batching, restored the intended follow-up and drip cron cadence, and reduced duplicate-send risk during overlapping scheduler runs.
  • Began moving estimate delivery fan-out onto a durable background job queue so send actions can return faster while PDF, email, SMS, and Workiz tasks finish in the background.
  • Followed up with the first scalable-monolith hardening pass: estimate sends now return async delivery state details, Workiz sync uses the shared background job queue, org-level estimate send burst protection is enforced in Postgres, and the remaining cron sweepers now use overlap-safe database leases.

2026-03-13

  • d969ff4 Guarded empty automation stats states in the admin UI.
  • e50027e Fixed automations admin session fetches.
  • 56ffb6e Added super-admin waitlist provisioning.
  • 7ae422c Auto-seeded QA automation workflows.
  • 2a90947 Shipped automation engine templates and runtime fixes.
  • 2007595 Added the missing commercial route helper.
  • 6b50429 Fixed the remaining open bug sweep items.
  • f3d4bd9 Finished the remaining open bug sweep fixes.

2026-03-12

  • f6701ab Fixed residential estimate draft edit persistence.
  • 0f3201f Shipped missing shared modules required for Vercel builds.
  • 3d016f2 Fixed lead, job, payment, and delete UX flows.
  • e739c55 Fixed the schedule Google Maps loader conflict.
  • e56bb06 Fixed estimate override conversion to jobs.
  • 36d9d1e Followed up with additional estimate override conversion fixes.
  • 4eb08e8 Fixed automation step availability and fleet save guards.
  • bd0897f Simplified shared flows and removed dead code.
  • 019ed8b Fixed the automation test route build regression.
  • 36e8b21 Hardened automations and external URL flows.
  • 62de95c Fixed the Margin Calculator render path when costSettings is missing.
  • fcf71c2 Linked Settings to the new Automations area and added a legacy banner.
  • 24bd131 Added automations.view and automations.manage permissions.
  • 566a121 Added the AI-assisted Margin Calculator Engine with optimization suggestions.
  • 73c3203 Added Automation Engine v2 with a visual workflow builder.
  • 1b6e0fa Added bulk multi-property job scheduling.

2026-03-11

  • 65c9bfd Restored navigation discoverability surfaces.
  • e8dbea7 Fixed public /ai landing page access.
  • 3afe3c5 Added hard delete flows for core records.
  • f362481 Temporarily disabled SMS sending through an environment flag.
  • bda1ffa Rebuilt the schedule dispatch board.
  • 466ea66 Fixed payment dialog warning text layout.
  • 3b26cea Fixed payment dialog overflow behavior.
  • 88b642e Widened the payment collection dialog.
  • a94a03e Added client navigation from lead detail.
  • 3a1de78 Fixed payment history modal layout.
  • 9ca5f56 Fixed AI Hub tone and formatting.
  • 0103b5b Added client account job scheduling.
  • ab29c14 Fixed AI Hub assistant quick actions.
  • c440734 Switched AI lead scoring to legacy-safe columns.
  • 0d2e155 Hardened the AI lead scoring estimate query.
  • 3f8f694 Fixed AI lead score recalculation fallback behavior.
  • dadb963 Added a shared tag picker workflow.
  • 2e763e0 Fixed recurring template scheduling.
  • db24460 Fixed payment balance carryover and receipts.
  • ddaf665 Fixed truck assignment compatibility.
  • 3579c1f Required start and end dates for new jobs.
  • 9b12543 Removed PWA app prompts.
  • 2bf6856 Fixed crew and truck job assignment.
  • b7a1b67 Polished payment collection dialog layout.
  • 7e49990 Fixed crew pay modeling and team compensation.
  • bd8b6b8 Fixed estimate portal approvals and additional pending bugs.

2026-03-10

  • 0e57f6f Fixed bug reporting and client workflows.
  • c771115 Dispatched accepted bugs to the OpenClaw runner.
  • f407082 Added Telegram notifications when bug issues are created.
  • 4f9d925 Synced vetted bug reports to GitHub issues.
  • dc00011 Added client address book and account setup flows.
  • 021a1cc Fixed the admin gamification runtime crash.
  • 30e1c5b Polished estimate detail and XP navigation.
  • 7384a51 Improved invoice send and payment workflows.

2026-03-09

  • 9639502 Fixed messaging, conversion, and invoice workflows.
  • d9b0008 Stabilized admin performance and inbox threads.
  • 970afa2 Added recurring job templates.
  • e23fee5 Updated the quick start create menu.
  • 6c78c24 Installed missing build dependencies for Stripe and html2canvas.
  • 8c2654f Fixed the commercial cost assumptions display data key mismatch.
  • 8bd4ff9 Fixed the Resend webhook build cast.
  • b8d9a9d Fixed email reply routing fallback behavior.
  • c8d5a81 Fixed inbox sync and message delivery updates.
  • 64c3552 Fixed the admin sidebar and widened the workspace.
  • 804119f Fixed dispatch assignment and settings navigation.
  • b90ef28 Added legacy payments schema handling in the payment endpoint.
  • e9780ba Added a unified payment collection workflow.
  • 96d4081 Hardened webhook authentication and payment processing.
  • 17b3d21 Switched the marketing site to waitlist mode and disabled self-serve registration.
  • 2594332 Hardened the admin toaster and gamification seeding.
  • b8579cc Fixed a Next 15 API-route params await regression that caused 500 errors.
  • 2541fc3 Fixed gamification org membership profile lookups.
  • 4ba42d8 Fixed ambiguous org membership profile lookups.
  • 79ea433 Added automatic bug report screenshots.
  • 81c7ed3 Restored waitlist CTAs on marketing pages.
  • 875c1f5 Added the gamification foundation and leaderboard.
  • 3d4b2db Updated the privacy policy and terms of service with official legal content.
  • 35778b8 Improved team onboarding and archive lifecycle behavior.
  • ef2fc30 Fixed follow-up schema drift and restored admin links.
  • f3b3904 Added the admin help button and docs search results.
  • 9682720 Added guarded MCP inbox write tools.
  • 9b923a3 Added MCP Phase 1 agent access.
  • e69baae Added actual job cost tracking to CRM margins.
  • 064f403 Added unified inbox internal notes.
  • 95dfe22 Bridged Workiz intake and hardened CRM analytics.
  • 2ad92b4 Routed admin lead creation through intake.
  • 98fd69e Unified intake adapters and hardened sales timestamps.
  • a4aa1bc Deduplicated unified email message logging.
  • 41cfec3 Hardened lead intake and inbox filters.

2026-03-08

  • 6242c0c Added inbox ownership and response controls.
  • 9114acb Rolled out the unified messaging foundation.
  • 65d8a76 Fixed the bug report email truncation suffix.
  • 33a46c2 Kept message threads scrollable during refresh.
  • 431ce4c Added a bug report queue poller for automations.
  • cf65b48 Improved bug report email details and JSON export.
  • da618d1 Added bug report webhook alerts.
  • e5f38f3 Fixed admin message scroll locking.
  • 3e00a57 Added the bug reporting system and app hardening work.
  • 728029f Finished the CRM buildout and QA hardening push.

2026-03-07

  • 5cadd2b Restored Automations in the Settings nav.
  • 2bfbcc2 Fixed searching existing customers while scheduling jobs.
  • 85f7657 Hardened audited estimate and billing flows.
  • f7bcfef Shipped a lead-first workflow and added a jobs creation entry point.
  • d34c3b7 Fixed the client account migration rollout.
  • 582242f Added nested client accounts and estimate pagination.
  • f89aa5d Fixed holiday lights measure and drawing flow.
  • 4c0fbf7 Retired the legacy estimator and fixed the job invoice flow.
  • f2382a1 Hardened estimate intake and send permissions.
  • ad46c31 Fixed secure estimate links and auto-dial routing.
  • 787cd99 Fixed customer portal estimate links.
  • b270e16 Restricted portal actions to known customers.
  • c13fe52 Hardened public portal throttling and Workiz sync.
  • 843bea3 Finished a broad hardening and product redesign pass.

2026-03-06

  • 3c3dad3 Unified web and mobile pricing logic and deduplicated escapeHtml.

Was this article helpful?

Still need help? Contact support