Evolve Tasks
A single-tenant task-management and lightweight CRM platform built to give a small real-estate team ClickUp-level functionality on owned infrastructure instead of a per-seat…
- 763
- vitest tests + 6 Playwright e2e tests
- 87
- Drizzle migrations
- 424
- test files in src/

- 01Interface
- Next.js 16 (App Router, RSC, Tur…
- React 19.2
- Tailwind CSS v4
- 02Edge
- Cloudflare Workers via OpenNext
- Cloudflare D1 (SQLite)
- Cloudflare R2
- 03Identity
- arctic (OAuth)
- 04Application
- Anthropic Claude API
- 05Data
- Drizzle ORM
- 06Operations
- vitest + Playwright
Evolve Tasks holds live data, so this shows the verified technology stack by layer rather than the interior of the app. Layers, not connections — which service calls which is not something a dependency list can prove. Hosts, ports and topology are deliberately absent.
Problem
The team was running project and client work through ClickUp, a per-seat SaaS tool not built around a real-estate brokerage's actual workflow: transactions with Florida-specific compliance deadlines (inspection, financing, title, appraisal, walkthrough), commission splits, BBA/TCPA consent tracking, and wire-fraud verification steps. Fitting that domain into a generic project-management tool meant workarounds everywhere, plus a recurring per-seat cost with no path to owning the data model or the infrastructure it ran on. None of ClickUp's native fields modeled a Florida real-estate transaction as a first-class object. Deadlines, disclosures, and commission splits had to live in custom fields and naming conventions bolted onto a generic task. That meant every new compliance requirement was another manual convention for the whole six-person team to remember and apply consistently, instead of a rule the software itself enforced. The team also had no AI assistant wired to its own data inside ClickUp — any AI help meant copying context out to a separate tool by hand. And there was no way to bound what an AI feature would cost per user per day, since that lever didn't exist in the vendor's product at all.
What was built
Evolve Tasks is a team task-and-CRM platform purpose-built for one real-estate brokerage's actual workflow, deployed on Cloudflare's edge network instead of a third-party SaaS vendor. It gives the team everything ClickUp did: list, board, table, calendar, and Gantt views, sprints, goals, time tracking, a command palette, real-time-feeling chat with deal-specific rooms, and a docs surface where selecting text can spin off a linked task. It also adds things ClickUp didn't have: client, listing, and transaction records with Florida disclosure and Buyer Broker Agreement tracking, commission-split ledgers, and wire-fraud verification callouts on large transactions. On top of that sits an AI assistant (Brain) that drafts and answers questions against the team's own data under a hard daily spend cap per user. Sign-in is Google OAuth gated to the team's own email domain. Admins can view-as any user, deactivate accounts, and force a global sign-out in one action.
Technical approach
The app is a Next.js 16 App Router application deployed as a Cloudflare Worker via the OpenNext adapter (Cloudflare deprecated the older next-on-pages path for SSR apps). It's backed by a single Cloudflare D1 SQLite database accessed through Drizzle ORM and an R2 bucket for attachments. It's deliberately single-tenant, with no tenant_id column anywhere, since it's one team's own workspace. Auth is app-level Google OAuth via the arctic library, not NextAuth or Cloudflare Access (Access's default email-OTP UX was rejected). It issues an HS256 JWT session cookie whose domain claim is checked both in edge middleware and again at page-render time. A tokenVersion claim lets any single user's sessions be invalidated instantly without touching anyone else's. Content-Security-Policy uses per-request nonces on script-src with no unsafe-inline and no strict-dynamic, plumbed through a custom header Next 16 stamps onto every inline script it emits. That hardening happened in two passes, since CSP3 browsers additionally block unsigned inline <style> tags, which had to be nonced separately. The most consequential engineering thread across the project's history is a series of formal authorization sweeps. Three separate audits found and closed 19, then 27, then 61 server-action authorization gaps — IDOR-class, meaning a user could read or act on another user's or another space's record by guessing an ID. That sat on top of an initial security-baseline wave covering CSP/HSTS headers, role gates on destructive actions, and a race-condition fix across 14 call sites for drag-and-drop position updates. Real-time chat was originally built on a Durable Object, but that implementation was reverted after a production regression and replaced with 1.5-second HTTP polling — a deliberate downgrade in mechanism kept for reliability over architectural purity. A dedicated compliance layer implements CCPA right-to-erasure (redact-in-place while preserving FREC hold requirements) and DSAR export, plus SHA-256 integrity verification on file attachments with magic-byte MIME sniffing. A wire-fraud safeguard surfaces verified phone numbers with click-to-call on large active transactions, instead of trusting whatever contact info is on file.
Creative approach
Craft
The visual system carries forward Monarch design tokens through a Tailwind v4 @theme inline bridge, paired with Plus Jakarta Sans for UI text and Instrument Serif for brand moments. It's a deliberate two-typeface split between a functional, dense operational surface (lists, boards, tables) and a handful of brand-forward touchpoints. Inbox notifications use adjacent-row coalescing — twelve status changes by one person on one deal collapse into a single row with a ×12 badge — instead of a flooded activity feed. A global EmptyState component and ConfirmDialog/Toast provider pair replaced native window.confirm/window.alert calls across the app, so destructive actions and empty views read consistently everywhere instead of varying screen to screen.
Reframe
The core product insight was refusing to treat 'task management' and 'real-estate CRM' as two different tools bolted together. Transactions, clients, and listings live as first-class records in the same schema as tasks and can be linked to them directly. A compliance deadline or a commission split is not a separate system to reconcile against the task board — it is the task board. The build sequence made this explicit: an initial ten-session roadmap shipped the generic project-management surface first. A much longer series of 'hardening waves' followed, documented as role-based gap audits run against the live tool. Each one progressively grafted on the real-estate-specific domain — FL statute disclosure tracking, wire-fraud verification, 1099-NEC export, BBA lifecycle — onto the same task/records substrate, instead of starting a parallel CRM module.
Process and what failed
The project's own README documents its failures candidly, instead of smoothing them over. A real-time chat feature built on a Cloudflare Durable Object was shipped, caused a production regression, and was reverted wholesale to plain HTTP polling — a deliberate step down in sophistication, kept because it was the version that didn't break. A search LIKE query using Drizzle's like() helper was silently returning zero results for any query containing a literal % or _ character, because Drizzle doesn't emit an ESCAPE clause by default. It was caught and fixed in a dedicated round of the post-launch test sprint. A recurring monthly-task scheduler was overflowing dates incorrectly — Jan 31 plus one month landed on March 3 instead of clamping to Feb 28 — also caught by the same test-hardening effort. The project ran three distinct, separately-numbered authorization audits over its life (19, then 27, then 61 confirmed IDOR/authz findings), instead of treating access control as solved after the first pass. Each subsequent audit found a new category of gap the previous one missed: personal-space leaks in the whiteboard/mind-map feature, an OAuth open-redirect, and impersonation-actor logging.
Outcome
Evolve Tasks is live in production at the team's custom domain, gating sign-in to the brokerage's own Google Workspace email domain, and has replaced ClickUp as the team's day-to-day task and CRM system. The test suite — 763 unit tests plus Playwright end-to-end and accessibility checks, the latter run through axe-core — and CI-gated deploy pipeline run on every push to main. A weekly automated D1 backup-restore drill verifies the backup is actually restorable, not just that a backup job ran.
Operational hardening kept pace with feature work instead of trailing it. An insider-threat wave added admin-gated bulk-export routes and sign-in audit logging with Cloudflare-supplied IP, country, and user-agent. It also added a token-version claim wired to a "Sign out everywhere" control. That control cuts the window a stolen or lost phone stays signed in from up to seven days down to a single request. Cron-driven anomaly detection watches for mass-download or mass-export behavior. A maintenance cron sweeps trash garbage collection, notification garbage collection, and per-job error isolation on a schedule, instead of relying on manual cleanup. It carries ntfy failure alerts and a heartbeat ping, so a silently-failed cron run gets surfaced instead of discovered later. The Workers bundle is tracked against Cloudflare's free-tier size ceiling with an early-warning threshold at 85% utilization, so a dependency addition that would tip the deploy over the cliff gets caught before it ships, not after wrangler rejects the upload.
Related work
- Live2026Evolve University PlatformA self-hosted learning, community, and accountability platform for real estate teams built to beat Skool, Circle, and Kajabi on the dimensions that matter to sales-driven orgs…
- Live2026Faith & FrictionA standalone Next.js membership platform for a men's brotherhood — invite-only application flow, gamified accountability, and a YouTube/podcast funnel into it.
- Live2026Monarch MovementA self-hosted community and onboarding platform that turns a real estate team's recruiting SOP into software members can't skip a step of.