I Replaced Calendly With Agents. Here Are the Receipts. A developer replaced Calendly with an agent-built open-source scheduling tool, punctual.sh, at $0 per month after Cal.com's MIT-licensed cal.diy stripped 411,000 lines of code and was deemed non-production-ready. The 10-day build produced 162 commits, 30,658 lines of TypeScript, 737 test assertions, and 60 Linear tickets, but a two-model review of the codebase history found 14 real issues among 15 candidate findings, including a 404 bug on team event types and broken Microsoft conflict-checking. The developer advocates adversarial code review and platform constraints over agent-written tests. We paid $60 a month for Calendly. Three people, one job: a link that shows when I am free. I asked Claude for an open-source alternative and was told nothing serious was left. Half true. Cal.com did relicense to cal.diy https://github.com/calcom/cal.diy under MIT in April 2026, but the same commit cut teams, workflows, insights and SSO: minus 411,000 lines. The second line of their own README says strictly recommended for personal, non-production use. No release since March, Docker images archived, and standing it up still wants a server with Node and PostgreSQL. So I built one. Started on a Saturday, on Cloudflare, because at our volume it is free and I am still a fan. Ten days later: 162 commits, 30,658 lines of TypeScript across 85 files, 737 test assertions, 60 Linear tickets, one Worker, $0 a month. Google Calendar and Microsoft 365, .ics emails and reminders, teams with round-robin and collective scheduling, REST API, webhooks, an embed widget, and an MCP server so agents can book meetings too. MIT, live at punctual.sh, currently 56 stars. That is the part people screenshot. Here is the part they do not. TL;DRAgents write code fast. Nothing they produce tells you whether it is right. I bought that answer in two places: a review process adversarial by construction two models, severity triage, hard time box , and a platform where whole classes of bug are not expressible. When a machine writes the code, prefer a constraint the database enforces over a protocol the code must remember. 1. The number that matters is not 162 On day five I ran the two-model review from chapter 3 against the entire codebase history rather than the latest diff. It surfaced 15 candidate findings. 14 were real. The headline one: team-owned event types, a flagship feature with green domain tests, returned 404 on every single HTTP request. One join in one query quietly dropped every row that belonged to a team rather than a person. Perfectly tested at the domain layer, completely unreachable through the product. Underneath it: Microsoft conflict-checking silently broken wrong identifier type fed to Graph's getSchedule , REST cancel and reschedule never firing webhooks, the per-day cap undercounting collective hosts across timezone boundaries. Plus two regressions from my own first round of fixes, caught by a second pass the same day. That is what 737 passing tests look like from the inside. Tests written by the agent that wrote the code check what the agent believed it was building, not whether the feature is reachable from outside the process. So the question stopped being whether agents can write code. It is what independent thing tells you they got it right. 2. The constitution Every repo has a CLAUDE.md . This one is not documentation, it is enforcement, and four rules earn their place. The public boundary. engine/ is public the moment it is pushed, so strategy, pricing and customer data never go in, commit messages and code comments included. An agent holding your strategy doc in context will paste it into a public README as helpful background, and git history keeps it forever. A ticket before code. Every non-trivial change gets a Linear issue first, 60 of them in ten days. It is the only place the reasoning survives a context window ending. Commits are per repo, English everywhere. Four repos under one workspace, so one git add never covers it, and private notes stay in English because translating at the boundary is where meaning gets lost. None of this is for the agent's benefit. It is blast radius. 3. Two reviewers that fail differently tools/review.sh puts Codex and Claude on the same diff, in parallel. Two models because they fail differently: Codex tends to catch driver-level and ordering problems, Claude catches domain-invariant and consistency ones. On that 15-finding run, 8 came from Codex, 7 from Claude, exactly one overlapped. Three rules make it usable instead of a tax. Fix P0 through P2. Never fix P3. P0 is data loss, a security hole or a broken invariant. P1 is a user-visible feature broken or silently wrong. P2 is wrong under a realistic edge case: a DST boundary, a concurrent request, a provider error. P3 is style, naming and "consider", and both reviewers are told to omit it entirely rather than list it. A review that reports everything gets ignored wholesale, which is worse than one that reports less. The time box is hard. 600 seconds each, killed at the deadline, partial report delivered rather than blocking the branch. Re-running until clean is the failure mode this replaces. The script fixes nothing. It emits a report a human acts on. Auto-applying review findings is how a one-line change becomes a forty-file diff. Only P0 exits non-zero. An exit code that fires on everything teaches everyone to ignore the exit code. The brief both reviewers receive fits in a paragraph, and you can paste it into any model today: Review this diff for DEFECTS ONLY. You are not a style checker. P0 - data loss, a security hole, or a broken invariant. Ship-blocking. P1 - a user-visible feature is broken or silently wrong. P2 - wrong under a realistic edge case: a timezone boundary, a concurrent request, a provider error. P3 - style, naming, structure, hypotheticals, "consider". Report P0-P2. Do NOT report P3. Zero findings is a valid result. Each finding: SEVERITY | file:line | what breaks | the input that triggers it | the minimal fix. Before reporting anything, try to REFUTE it by tracing the code path. A wrong finding costs more than a missed one. The detail I did not see coming: an unauthenticated reviewer returns zero findings, on screen indistinguishable from a clean review. The script now detects that and prints NOT AUTHENTICATED. Silent success is the most expensive output in an automated pipeline, and this was the first of two places it showed up. 4. Six agents, six worktrees One evening I dispatched six backlog tickets to six agents in parallel, each in its own git worktree, a separate checkout of the same repository so no two agents ever touch the same files. Each wrote a fix plus a regression test, ran typecheck and the full suite, committed independently. I reviewed every diff, rebased onto main one at a time, re-verifying after each merge. Two things only surface at that scale. The agent on ICS sequence numbers found that its ticket's literal scenario was unreachable through any current call path, said so, and hardened the underlying primitive defensively anyway. Better than a fix, and no test would have told me. Another agent's new test file landed outside vitest's configured include globs. It would have silently never run. That is the second silent success: a test that does not execute is worse than no test, because it reports green. A third pass then found a structural problem in the most invariant-sensitive code in the project: the slot grid anchor depended on which busy data a query happened to load. I filed it rather than attempt a third same-session patch there. Knowing when to stop patching is the one thing in this workflow no agent offered to do. 5. Why the whole thing is one Worker Cloudflare shows up twice in this story, and the marketing reason is the weaker one. There is almost nothing to misconfigure. One wrangler deploy ships the whole product: Workers for compute, D1 for the database, Durable Objects for booking locks and rate limits, Queues for email and webhooks, KV for the freeBusy cache, R2 for avatars, Cron for reminders. No server to harden, no Postgres to tune, no Docker, no Kubernetes manifests, no deploy pipeline to build. Each is a surface where an agent produces plausible, locally-working, subtly wrong config. The cheapest defense against that is not owning the surface. Self-hosting takes fifteen minutes into your own account, and at three-person volume the bill is zero. Guarantees instead of protocols. This is the real reason, and it took a redesign to learn. The obvious way to never double-book a host is one Durable Object per host. A Durable Object handles one request at a time https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/ , so routing every attempt through the host's own DO puts them in a queue and the platform hands you correctness for free. That holds until a collective meeting needs three hosts at once. Now one booking has to change three independent objects at once, which is the problem banks have when money moves between two accounts: partial success leaves two hosts booked and one free, and two racing bookings can interleave into the exact double-booking the DO existed to prevent. The fix was to stop asking the DO layer to be correct and start asking it to be fast. The invariant moved into D1: CREATE TABLE slot locks host user id TEXT NOT NULL, bucket start INTEGER NOT NULL, -- UTC epoch ms, 5-minute grid booking id TEXT NOT NULL, PRIMARY KEY host user id, bucket start ; Every booking writes one row per five-minute bucket per participating host across its buffered range. A 60-minute collective meeting for three hosts is 36 rows. The booking row and all of its lock rows go into a single D1Database::batch https://developers.cloudflare.com/d1/worker-api/d1-database/ batch . If any bucket is already taken, the primary key constraint fails and the entire batch rolls back. "These two meetings overlap" is not something a database can be told to forbid. "This exact five-minute box is already taken" is. Double-booking, for any number of hosts at once, stops being a rule the code has to remember and becomes something the database physically will not store. That distinction is the whole argument when a machine writes the code. A protocol is a promise every future code path must keep, including the ones an agent adds next week while you sleep. A constraint does not care who wrote the query. The DO still earns its place - serializing attempts, running the mandatory freeBusy re-check right before commit, holding five-minute advisory locks while a guest fills the form - but its worst failure is now a wasted API call or a clean 409, never a double-booked calendar. Leases are always taken in the same order, sorted by host ID, so two bookings can never end up waiting on each other forever, and a failed lease just falls through to the batch. I verified that rollback against production D1 with a deliberate constraint violation before any booking code shipped, because the entire invariant rests on that one behaviour holding in practice. The same reasoning produced a rule an agent can check mechanically: every database read goes through one helper, and a raw query anywhere in the codebase blocks the branch. No judgement call, no debate about whether this one is fine. Rules a machine can check are the only rules that survive contact with a machine. 6. What only a human found The bugs that reached production got past tests, reviewers and agents alike. I found them by using the product. The booking page did not show today's slots on first load. New accounts had no availability until the dashboard form was saved once, so connecting a calendar did not make you bookable. The confirm page asked "What would you like to discuss?" twice. CI stayed red for days on a flaky wall-clock test in the rate limiter, unnoticed because local runs were green. That became its own rule: after every push, confirm the run finished green rather than inferring it locally. Each is invisible to a test suite and obvious within thirty seconds of real use. Which settles the division of labour. Agents write and review each other. The platform enforces what it can make impossible. The human uses the product, and decides when to stop patching. A month ago I wrote that you own every word your agent sends under your name. The engineering version is the same sentence with one noun changed: you own every line your agent commits. Ten days of that gave me something my company now runs its meetings on, and a subscription I cancelled. MIT, no seat limits: github.com/CCCrafts/punctual https://github.com/CCCrafts/punctual , self-hosting guide https://github.com/CCCrafts/punctual/blob/main/docs/self-hosting.md included. wrangler deploy into your own account, fifteen minutes, $0 at small volume. Steal the review brief from chapter 3. It works on any diff and any model, and P3 silence is the part that makes it stick. Live at , which is also where you can book time with me. punctual.sh https://punctual.sh Replaced a SaaS with something an agent built? Tell me. I am collecting the ones that survived production. Next issue: the DST rules almost nobody writes down, and the five timezones that break scheduling code structurally rather than numerously.