← All packs
Scheduling & Availability Core
verifiedBookings and availability that survive timezones, repeating events and daylight saving — without double-booking.
Your product lets people book time, and the calendar maths is going wrong.
How you actually use this
You don’t install a library or wire up an SDK. Your own coding agent builds the code in your project, and you keep it — no runtime dependency on us.
Step 1
Download and unzip
You get a folder: the docs that tell an agent what to build, a starting skeleton, and the test suite that decides when it's done.
Step 2
Open it in Claude Code or Cursor
Point your coding agent at the folder. Nothing to install, no account with us, no API key.
Step 3
Paste one prompt
The pack contains the exact prompt. Paste it as your first message and leave it alone — it works through the build itself, choosing a cheaper or stronger model per task.
Step 4
Run ./verify.sh
One command. It prints a pass or fail for every check. Green means the build is done — the same script we ran to produce the receipt on this page.
Typical build: about 22 minutes of your agent working, mostly unattended. Then you integrate the working module into your app the way you would any code you’d written yourself.
Technically: Recurrence, timezones, DST and double-booking done correctly inside your own product: RFC 5545 expansion in floating wall-clock time, IANA zone-identity handling with a pinned gap/overlap policy, buffers and multi-seat capacity, and a booking path that is safe under real concurrency — an agent-executable blueprint with a 39-check acceptance harness that runs property tests over real DST transitions across six zones and races 16 concurrent bookers against real PostgreSQL.
Execution receipt✓3 of 3 runs passed- Model
- claude-sonnet-5
- Run at
- 2026-08-03T07:24:04+00:00
- Pack version
- 9204347f440c
- Tokens in / out
- 10,300,621 / 131,905 · per-run mean · billed input includes 9,744,133 cache reads, priced far below fresh input
- Wall time
- 22 min · per-run mean
- Run cost
- $8.40 · what this verification run cost us at API pricing, per build pass · your plan and model prices differ
- Deploy
- not deployed
Acceptance checks
- node-version✓ PASS
- install✓ PASS
- typecheck✓ PASS
- postgres-up✓ PASS
- schema-idempotent✓ PASS
- tz:tzdb-present✓ PASS
- tz:rejects-offsets✓ PASS
- tz:roundtrip-property✓ PASS
- tz:gap-uses-pre-transition-offset✓ PASS
- tz:ambiguous-picks-first✓ PASS
- tz:fractional-offset-zones✓ PASS
- tz:offset-storage-drifts✓ PASS
- rrule:rfc-examples✓ PASS
- rrule:wkst-changes-result✓ PASS
- rrule:invalid-dates-ignored✓ PASS
- rrule:count-until-exclusive✓ PASS
- rrule:unsupported-parts-rejected✓ PASS
- rrule:unsynchronized-dtstart-rejected✓ PASS
- rrule:floating-expansion-zone-independent✓ PASS
- rrule:until-is-utc-boundary✓ PASS
- slot:dst-local-time-stable✓ PASS
- slot:gap-and-overlap-instances✓ PASS
- slot:window-crossing-fallback✓ PASS
- slot:grid-alignment✓ PASS
- slot:buffers-additive✓ PASS
- slot:adjacent-not-overlapping✓ PASS
- slot:lead-time-and-horizon✓ PASS
- slot:capacity-remaining✓ PASS
- slot:exceptions-block-and-open✓ PASS
- race:exclusion-constraint-enforced✓ PASS
- race:naive-check-then-insert-double-books✓ PASS
- race:on-conflict-silently-skips✓ PASS
- race:capacity-one-single-winner✓ PASS
- race:capacity-n-exact-fill✓ PASS
- race:idempotent-booking-key✓ PASS
- race:cancel-frees-capacity✓ PASS
- race:cancel-idempotent✓ PASS
- race:per-resource-lock-not-global✓ PASS
- race:dst-booking-instant-exact✓ PASS
Versions lock
node-postgres
| node | 24.18.1 |
| typescript | 6.0.3 |
| tsx | 4.23.4 |
| pg | 8.22.0 |
| @types/node | 24.13.3 |
| @types/pg | 8.20.3 |
| postgres | 18.4 |
Scaffold integrity
1 scaffold file modified or deleted by the buyer-agent during verification
Scheduling looks like CRUD until the clocks change and two people click Book at the same second — and then it is recurrence expansion across a 23-hour day, a wall clock that happens twice, a 30-minute DST shift on Lord Howe Island, and a write-skew race that passes every single-threaded test you would think to write. This pack builds the hard core of that: RFC 5545 recurrence expanded in floating wall-clock time, IANA zone-identity handling with a pinned answer to the two ambiguous cases, buffers and multi-seat capacity, and a booking transaction guarded by a per-resource lock and a PostgreSQL exclusion constraint. The proof is verify.sh: 39 checks, including >20,000 property samples over 12 real DST transitions across six zones, the RFC's own published recurrence examples, and 16 concurrent bookers racing against real PostgreSQL — plus one check that asserts the naive check-then-insert pattern genuinely does double-book in that same database, so the race test is proven to have teeth rather than assumed to.
Status: draft — no receipt yet. Every verified pack in this catalog publishes a measured pass rate as M of N clean-room builds. This one has not been through the harness, so it publishes nothing: no pass rate, no measured token count, no measured dollar cost. The figures below that are not version pins or citations are labelled as estimates. When it runs, whatever comes out gets published.
Example use cases
- A dental or physio practice-management tool where three chairs are three seats on one resource, appointments carry a 15-minute turnaround buffer, and the recurring "Tuesdays 9–5, no Wednesdays" template has to still mean 9am on the Tuesday after the clocks change.
- A gym or studio class-booking app where a 6am HIIT class holds twelve people — exactly twelve, including the morning fifty people refresh the page at once — and a cancellation two minutes before must free exactly one spot even if the customer taps Cancel four times.
- A field-service / trades dispatcher covering vans across several regions in different zones, where the booking horizon is "up to 30 days out, at least 4 hours' notice", and a job's travel buffer must block the slot on either side without eating into the technician's shift window.
- An indie SaaS adding "book a call with me" inside its own product rather than sending users to a third-party scheduling link — same brand, same session, same database, one
POST /bookings. - A tutoring or lessons marketplace where a tutor in
Pacific/Auckland and a student in America/New_York are looking at the same slot list on opposite DST schedules, and both need to be right. - An agency re-stamping one scheduling core across client projects — clinic, salon, driving school — instead of rediscovering the DST and double-booking bugs once per contract.
Why this pack
No "always" claims — agents are stochastic, and no scheduling design is free of tradeoffs. This is what the pack pre-solves, what it costs, and what you give up.
| Bare agent, no pack | Embedded-scheduling API (Cal.com Platform / Cronofy / Nylas) | Hand-rolled in-house | This pack |
|---|
| Cost | "Free" (agent time only) | Recurring, and priced in three incompatible units: Cal.com Platform bills per booking ("charged for a scheduled booking only when the booking starts", plus overage above the plan's included volume); Cronofy's published API tiers start at $819/mo billed annually; Nylas prices per connected calendar account (Calendar plan $10/mo including 5 accounts, then $1.50/account/mo) | Engineering time — realistically weeks, paid however you pay engineers | $99 one-time |
| Time to correct behaviour | Unknown. An agent asked to "add booking" has no reason to know these bug classes exist until a customer finds them | Fast to integrate, and correctness is theirs — genuinely their strongest argument | Slow. These bugs are usually found by users in March and October, not by tests | 3 sessions on a $20/mo agent plan, per the pack's own budget (estimate — no receipt yet) |
| Correctness risk | High and seasonal — see the bug classes below | Low for what they cover; you inherit their semantics and their bugs, and cannot fix either | High until you have independently rediscovered every failure mode below | Bounded by verify.sh's 39 checks. No published pass rate yet — this pack is draft |
| Lock-in | None, but no guardrails either | Real. Your availability model, booking lifecycle and data live behind their API; leaving means rebuilding the core and migrating booking history | None | None — your repo, plain TypeScript and PostgreSQL, one runtime dependency (pg), no runtime call to us or anyone |
| Fits inside your product | Yes | Depends. Their scheduling primitives, your UI | Yes | Yes — three functions, no UI, no HTTP layer, no opinion about your framework |
Buy the product instead, if that's what you actually want
**If what you need is a booking product — a scheduling page, a shareable link, calendar sync, reminder emails, round-robin across a team — buy that. Calendly's Standard is $10/seat/mo and Teams $16/seat/mo billed yearly (Enterprise starts at a published $15k/yr); Cal.com is free for one user, $12/user/mo for Teams and $28/user/mo** for Organizations billed yearly. Either will be live this afternoon and will do a dozen things this pack explicitly does not: no UI, no calendar-provider sync, no notifications, no payments, no round-robin, no .ics import/export (all listed as non-goals in the PRD, not omissions).
This pack is for the other case: scheduling that lives inside a product you own, where the booking flow is your product's flow, the data is in your database, and sending users to a third-party page is not an option. That is a real and different job, and it is the one where the timezone and concurrency work stops being someone else's problem.
One thing worth knowing if you were planning to self-host Cal.com to get the best of both: Cal.com went closed-source in April 2026. The public repository is now calcom/cal.diy — an MIT-licensed, community-maintained fork with Teams, Organizations, Insights, Workflows and SSO removed, no hosted version, and a production codebase that Cal.com states "has significantly diverged, including major rewrites of core systems". "Self-host Cal.com free under AGPL" is no longer a thing that exists. That changes the build-vs-buy arithmetic and it is why it is in this table rather than a footnote.
What a bare agent typically ships that this pack pre-solves
These are specific bug classes, not a claim that every agent hits every one. Each has a named check in ACCEPTANCE.md.
- Storing a UTC offset instead of a zone identifier.
+05:30 or a timestamptz availability anchor looks correct and is correct until the zone's next transition, then wrong by exactly the transition size, forever. Every recurring appointment breaks twice a year. tz:offset-storage-drifts reproduces and measures the drift. The trap has a second lip: EST and PST8PDT are resolvable legacy tz entries that carry no location, so a resource "in EST" silently opts out of DST permanently — the pack rejects them at the boundary. - Expanding RRULEs in UTC instead of local wall clock. Adding 86,400,000 ms per day is right until a 23- or 25-hour day, after which "9am every Tuesday" is 8am or 10am. And it is not uniform: Lord Howe Island shifts 30 minutes, not 60, so a whole-hour correction is wrong there too, and Chatham sits at +12:45/+13:45 so anything assuming whole-hour offsets fails before DST even enters the picture.
- Picking the disambiguation policy by accident. Twice a year every zone produces a wall clock that happens twice and one that never happens. Most implementations resolve them however their date library happens to; this pack pins first occurrence for the overlap and pre-transition offset for the gap, which is RFC 5545 §3.3.5 FORM #3 and is byte-for-byte
Temporal's disambiguation: "compatible" — cited, not chosen. - Silently ignoring an RRULE part you didn't implement. An agent that parses
FREQ, INTERVAL and BYDAY and drops BYSETPOS, WKST or BYWEEKNO produces availability the caller believes they restricted and the system did not. This pack rejects every unsupported part at parse time with a named error. (WKST is not cosmetic: RFC 5545 ships the demonstration that changing only WKST from MO to SU changes which dates a rule produces.) - Check-then-insert with no constraint and no lock. Two READ COMMITTED transactions both see a free slot, both insert, both commit. It passes every single-threaded test that will ever be written against it, and double-books the first time two people click at once.
race:naive-check-then-insert-double-books proves this happens in the pinned database before asserting that the shipped path does not. ON CONFLICT DO NOTHING as the "fix" for the constraint violation. Verified on PostgreSQL 18.4: it does not error — it inserts nothing and returns nothing. A handler that does not inspect rowCount tells the customer "booked" while the database holds no booking, and it looks like a happy path in every log line. (ON CONFLICT (cols) raises 42P10 and DO UPDATE raises 42809; there is no upsert path for an exclusion constraint at all.)- A
seats_remaining counter. The obvious model, and it makes cancellation non-idempotent: a retried cancel decrements twice and the resource is quietly overbooked forever after. This pack has no counter anywhere — capacity is counted from confirmed rows, and a partial exclusion constraint frees the seat the instant a status changes. - Checking raw ranges instead of buffered ones. A 15-minute post-appointment buffer that the availability check ignores is not a buffer. Worse, if the app's overlap rule and the database's constraint index different ranges, they will eventually disagree about what a conflict is.
These bugs are live in funded, mature products
Not hypothetical, and not cherry-picked from hobby projects:
- Microsoft Bookings shifts availability by an hour around DST transitions. Microsoft's own support responder, on a question asked 2026-03-18: Bookings "relies on time-zone conversion (using UTC internally), and during Daylight Saving Time transitions, availability can temporarily appear shifted by one hour if time-zone settings are not fully aligned" (Microsoft Q&A, retrieved 2026-08-02). A separate January 2026 thread has slots displayed 4:00am–1:00pm instead of 9:00am–4:00pm, with the recommended fix being the "Always show time slots in business time zone" setting (Microsoft Q&A) — while another thread reports that same setting causing wrong slots plus a "Slot is not available" error on submit, resolved by turning it off.
- Microsoft Bookings ships a user-visible double-booking race message. "Someone beat you to it. This time is no longer available. Please choose another one." — reported on a 1:N multi-attendee service (max 4 per slot), i.e. exactly the capacity>1 case an exclusion constraint alone cannot express (Microsoft Q&A, 2024-12-14, retrieved 2026-08-02).
- Calendly maintains a standing timezone troubleshooting guide in its community (Time Zone Troubleshooting Guide), an official time zones overview, and a dedicated Troubleshoot Tool article for "unavailable times that should be available". A scheduling company shipping a permanent diagnostic tool for "why isn't this slot bookable" is the clearest statement available that this is hard.
- Cal.com — a scheduling company — has open bugs of both classes in its own engine. #23974 "Reservation-to-Booking flow allows double-booking reserved slots" (opened 2025-09-22, still open, labelled urgent/S1-Critical) describes a textbook time-of-check-to-time-of-use: "another user could book the slot before reservation deletion". #24350 (opened 2025-10-08, open) is a DST offset bug — backend stores correct UTC, frontend re-derives it wrong at the March/October transition. #22801 "Duplicate double bookings using recurring booking links" (opened 2025-07-30) is open too. All retrieved 2026-08-02.
None of this is a claim that those products are bad. It is the opposite: these are well-funded teams who have thought about scheduling for years, and this is still where the bugs are. That is the argument for buying the answers rather than deriving them.
Scale envelope
Derived from the shipped design, not aspiration, and labelled as estimates where they are estimates. Nothing here is load-tested — this pack has no receipt yet, so it has no measured performance numbers at all.
The serialisation point is one pg_advisory_xact_lock per resource. That is the ceiling to reason about, and it is deliberately per-resource rather than global so that a busy clinic does not queue behind an idle one.
- Bookings per resource (estimate, not load-tested): a booking transaction is a lock acquisition, four small indexed reads, and one insert. On managed Postgres in the same region that is typically tens of milliseconds, putting a rough order-of-magnitude ceiling in the low hundreds of bookings per second for a single resource — sustained. A "resource" is one practitioner, room, chair or van. No real one is booked hundreds of times a second, which is why this ceiling is comfortable rather than clever.
- Resources in parallel: unbounded by this design — different resources take different lock keys and never contend. The real limit becomes your
pg.Pool size (default max: 10) and the database's max_connections (PostgreSQL's default is 100), both of which you tune, not this pack. - Availability reads:
slotsForResource is pure computation over the rules for one resource plus one indexed range query for overlapping bookings. Expanding a year of weekly rules is a few hundred instances; the hard cap is 10,000 instances per call. Cache it if you serve the same month to many visitors — this pack does not, deliberately, because cache invalidation on booking is your product's decision. - Rows: one row per booking, one per availability rule, one per exception. Millions of bookings is an ordinary PostgreSQL table for this schema, with a partial GiST index on the confirmed ones. Storage is not the constraint.
- First ceiling you will actually hit: a single hot resource (a popular class, a "book the whole shop" flow) where concurrent booking attempts on the same resource start queuing on its advisory lock. That is added latency, not incorrect behaviour.
- Upgrade path when you hit it: replace the lock with the lock-free variant — try seat 0, catch
23P01, ROLLBACK TO SAVEPOINT, try seat 1 — which is correct by the same exclusion constraint and bounded by capacity attempts. ARCHITECTURE.md names it as the documented upgrade with the tradeoff (savepoints and a retry loop in the hot path) already stated.
What this comfortably covers as shipped: a multi-location clinic, a studio with a full class timetable, a trades business with a fleet, a marketplace with thousands of independent providers — anything where load is spread across resources. What it isn't sized for out of the box: a single resource under genuine thundering-herd load (ticket-drop shaped, not appointment shaped). That is the upgrade path above, not a change this build makes for you.
What it costs to skip the pack: honestly, we do not know yet, and we will not invent it. The verified packs in this catalog publish a measured per-run token cost from their receipts; this one is draft and has none. What can be said without measuring: the timezone layer is roughly 35 lines and the recurrence layer a few hundred, so do not buy this to save typing. Buy it because the 39 checks encode which 35 lines, and because several of those checks map directly onto bugs that are open right now in products built by companies whose entire business is scheduling.
Verified against
- claude-opus-5
- claude-sonnet-5
- claude-haiku-4-5
Supported stacks
- node-postgres
| node | 24.18.1 |
| typescript | 6.0.3 |
| tsx | 4.23.4 |
| pg | 8.22.0 |
| @types/node | 24.13.3 |
| @types/pg | 8.20.3 |
| postgres | 18.4 |
Estimated buyer token cost
688,000 tokens for one build pass (estimate, not a guarantee).
FAQ
What if the build fails?
verify.sh is the arbiter, and it's deterministic — no network calls beyond npm install and the first Docker image pull, no credentialed API, no wall-clock dependence, and all 12 DST fixtures are past transitions that cannot be invalidated by a tz database update. Full refund within 14 days of purchase if verify.sh fails on a supported configuration (the pinned stack and a model from the pack's matrix) — reply to your Stripe receipt email with the verify output or FAILURE-REPORT.md attached. If your agent modified a frozen file (verify/, src/core/types.ts, src/db/schema.sql, docker-compose.yml, or the pinned versions) or added a date/recurrence library to get a check to pass, that falls outside the guarantee — the fix is to fix the implementation, not the test, per the pack's own verify prompt.
Does this pack have a receipt?
No. It is draft, which is why this listing publishes no pass rate, no measured token count and no measured cost. It becomes verified only after OneShot's harness runs it end to end in a clean workspace N times, and the result is published as M of N — whatever M turns out to be. If it cannot be got green, it does not get listed. The estimates on this page (3 sessions, ~688k fresh tokens) are extrapolated from a comparable verified pack in the catalog and will be replaced by measurements, not defended.
What models does this need?
Written against claude-opus-5, claude-sonnet-5 and claude-haiku-4-5 — but with no receipt yet, that matrix is the intended target, not a verified result. Tasks are pre-tagged cheap/standard/capable in TASKS.md and routed accordingly; five tasks are Opus-tier (the zone layer, the recurrence layer, slot generation, the booking path, and the verify/fix pass), which is what forces a session split on a $20/mo plan rather than raw token volume. Anthropic doesn't publish an exact token-per-window figure for Pro's rolling-window and weekly caps, so treat the 3-session estimate as an expectation, not a guarantee.
Why does it have no UI?
Because a UI would be the least valuable and most opinionated part, and shipping one would make this a worse version of a product you can already buy. What is hard about scheduling is not the calendar grid; it is what the calendar grid is told. The pack ships three functions and tells you exactly where to attach your own handlers. If you want the grid, buy Calendly or Cal.com — the "Buy the product instead" section above says so plainly and means it.
Why hand-roll the timezone and recurrence layers instead of using a library?
Two different reasons, both stated in ARCHITECTURE.md rather than assumed. For timezones: the disambiguation policy is the product, and a scheduling core that outsources it has outsourced the one decision the buyer is paying to have made, to a library whose policy they never chose and cannot cite — and it is 35 lines against a dependency, because full ICU (the entire IANA database) has shipped inside Node by default since Node 13. For recurrence: rrule@2.8.1 is the obvious candidate, its last npm publish was 2023-11-10 and its last commit 2024-06-27 with 212 open issues, and — by its own README — it computes in floating time and hands back Date objects that must be re-interpreted before they are instants. Since that re-interpretation is the timezone layer we own anyway, the library solves the easy half and hands the hard half back. So the pack scopes the RRULE subset instead, and rejects what it does not implement rather than ignoring it. The result is one runtime dependency: pg.
Which RRULE features are actually supported?
FREQ (DAILY, WEEKLY, MONTHLY, YEARLY), INTERVAL, COUNT, UNTIL, WKST, BYDAY (with and without ±ordinals), BYMONTHDAY (including negative), BYMONTH, BYSETPOS, plus EXDATE and RDATE. Explicitly rejected at parse time with a named error: BYWEEKNO, BYYEARDAY, BYHOUR, BYMINUTE, BYSECOND, RSCALE/SKIP (RFC 7529), and FREQ of SECONDLY/MINUTELY/HOURLY. That is a real limit and it is enforced rather than papered over — a rule your system silently ignored is worse than one it refused. If you need ISO week numbers or non-Gregorian calendars, this is the wrong core and the pack will tell you so at write time rather than at booking time.
Can I customise it after the build?
Yes, and the seams are named. The disambiguation policy is one function. Buffer semantics (post + pre, pinned) are one expression. maxHorizonDays is exact 24-hour days; making it calendar days in the resource's zone is a one-function change. The RRULE subset is extensible if you need BYWEEKNO. Auth and tenancy are a WHERE clause in your own handlers. In every case verify.sh is still there to tell you what else your change moved — which is the actual reason to own the code.
Do I own the code?
Yes. Once built it's your repo — plain TypeScript and SQL with one runtime dependency (pg), no runtime call to any third-party scheduling service or to us, no license key, no recurring anything.
What happens when a country changes its DST rules?
Your running app doesn't break — it runs on the tz data baked into its Node runtime and keeps working; only future instants in the affected zone go stale. The fix is a runtime bump plus a verify.sh run, and MIGRATION.md ships a copy-paste prompt that has your agent read the tzdb NEWS file since your runtime's version, work out whether the release touches a zone you actually operate in, apply the change and re-run the suite until green. The occasional case that needs real work is a zone rename or split (Europe/Kiev → Europe/Kyiv; America/Coyhaique split out of America/Santiago in 2025) — a rename is a no-op if you stored identifiers, and a split is a WHERE clause. If you had stored offsets, neither would be recoverable. We re-verify listed packs on our own schedule and prior buyers get every re-verified version free, but we promise no delivery date and this is not a subscription or a maintenance contract — section 3 of MIGRATION.md exists so your build never has to wait on us.
Does it sync with Google Calendar or Outlook?
No, and that is a stated non-goal rather than a gap. Calendar-provider sync is an OAuth-credentialed surface that could not be verified hermetically, and this catalog does not ship what it cannot prove. If "check my Google busy times" is a core requirement, Cronofy and Nylas exist and that is precisely what they sell.
Specific problems this pack pre-solves
Prescribed services
Disclosure: some links on this page are affiliate links. We may earn a commission if you sign up through them, at no extra cost to you. We only link to services the pack actually verified against.