Scheduler Use Cases & Capability Scope — input for Fable 5's design doc
Date: 2026-07-02
Purpose: define the use cases we want the phone/voice scheduler to support,
map each to the capability that already exists (or must be built), and give Fable 5
enough to design the tutorial flows + the "offers are quotes" architecture.
Companion: scheduler-demo-problem-statement.md (why the current demo is
fragile). This doc is the forward-looking "what to build."
0. TL;DR — is there enough to design from?
Yes. The calendar backend (cal-provider, ~/src/cal-provider) already exposes
the full CRUD-on-time surface across 3 calendar systems (google,
google-oauth, caldav). What's missing is (a) surfacing all of it as
caller-facing flow tools, (b) a robust LLM↔tool contract (the #20 "offers are
quotes" fix), and (c) tutorial flows + deterministic tests per capability. This
doc scopes those.
1. The capability surface that already exists
CalendarProvider (the cal-provider protocol, provider-agnostic across
google/caldav) offers:
| Method | What it does | Exposed as a riff flow tool today? |
|---|---|---|
list_calendars |
list the caller/tenant's calendars | only in boot_check (not caller-facing) |
get_events |
list meetings/events in a time window | NO — must be built |
get_available_slots |
list free/available slots | yes (via check_availability/propose_booking) |
create_event |
schedule/book an appointment | yes (via schedule_event) |
update_event |
reschedule/modify an event | NO — must be built |
cancel_event |
cancel an event | partial (cancel_booking in adapter; no flow) |
Calendar systems (CAL_PROVIDER): google | google-oauth | caldav. The
riff adapter (riff/providers/cal_provider_adapter/) is the single seam that
imports cal_provider; flows/tools are provider-agnostic — a tutorial flow
written once runs on any of the three systems by swapping the env var. (Tutorial
flows should be designed to this abstraction, not to Google specifics.)
2. The use cases — the full calendar-operation taxonomy
Think broadly: everything a caller (or operator) might want to do with a calendar over voice. Each use case lists the caller intent, the backend capability, and whether it exists as a flow tool today. Bold "NEW" = the backend can do it but it is not surfaced as a caller-facing tool yet.
Design instruction to Fable 5: treat this taxonomy as the menu of capabilities. A v0 need not build all of them, but the design should say which tier each lands in (v0 / v1 / later) and why. Prefer covering the categories broadly over polishing one path.
A. READ — availability & discovery
| # | Caller intent | Capability | Status |
|---|---|---|---|
| UC-1 | "When are you free Thursday afternoon?" | get_available_slots(date, window, duration) |
exists (read-only entry NEW) |
| UC-2 | "What's on the calendar Thursday?" / "what's blocked?" | get_events(range) |
NEW (list_events) |
| UC-3 | "Are you free at 2 PM Thursday?" | availability/events filtered to the slot | NEW |
| UC-4 | "What's the next available appointment?" | get_available_slots(soonest) |
exists (scheduling_v1 "soonest") |
| UC-5 | "Any openings this week?" | get_available_slots over a range |
exists (range) |
| UC-6 | "First opening that's at least 90 min, mornings only, not Mondays" | constrained availability search (duration + time-of-day + day filters) | partial |
| UC-7 | "What's my 2 PM about?" | get_events → event detail |
NEW |
| UC-8 | "How booked am I Friday?" / "how many appointments this week?" | get_events → summary/count |
NEW |
| UC-9 | "When are both Dr. A and Room 2 free?" | mutual availability across calendars/resources | NEW |
| UC-10 | "What are your hours?" / "are you open Sunday?" | business/working-hours query | NEW |
| UC-11 | "Read me my upcoming appointments." | lookup by caller identity (see §D) → get_events |
NEW |
B. WRITE — booking & mutation
| # | Caller intent | Capability | Status |
|---|---|---|---|
| UC-12 | "Book an hour at 2 PM Thursday." | create_event (after a vetted offer) |
exists — fragile (the demo bug) |
| UC-13 | "Move my Thursday appointment to Friday at 10." | update_event(event_id, new_time) |
NEW (needs lookup) |
| UC-14 | "Cancel my Thursday appointment." | cancel_event(event_id) |
partial (cancel_booking; no flow) |
| UC-15 | "Hold that slot while I check with my wife." | tentative/hold (pending confirm), auto-expire | NEW |
| UC-16 | "Book me every Tuesday at 3 for a month." | recurring create_event |
NEW |
| UC-17 | "Add my partner to the appointment." | create_event/update_event with attendees/invites |
NEW |
| UC-18 | "Make my 2 PM 90 minutes instead of 60." | update_event (extend/shorten) |
NEW |
| UC-19 | "Note that it's a leaky faucet." | update_event (notes/details/purpose) |
partial (purpose on create) |
| UC-20 | "Block off Friday morning — I'm out." | create_event as a busy/blocked block (not a customer meeting) |
NEW |
| UC-21 | "Yes, confirm the tentative hold." | confirm/accept a held slot | partial (confirm_booking) |
| UC-22 | "Book my next cleaning in six months." | forward/follow-up booking | NEW |
| UC-23 | "Sign me up for the 10 AM class." | group/class booking (many attendees → one slot, capacity) | NEW |
C. POLICY & RULES — constrain every read/write
Not caller "commands" but rules the flow must enforce (design must specify where they live — flow YAML vs cal-provider):
- UC-24 min/max advance + lead time ("nothing sooner than 2 hours; nothing past 60 days").
- UC-25 duration inference by service type (a "cleaning" = 90 min; a "consult" = 30).
- UC-26 capacity / overbooking (N bookings per slot; classes vs 1:1).
- UC-27 cancellation / reschedule policy (notice window, fees, "too late to cancel").
- UC-28 buffer / travel time between appointments.
- UC-29 blackout dates / holidays / working-hours enforcement.
D. IDENTITY & MULTI-TENANT — "whose calendar, which appointment"
- UC-30 identify the caller's existing appointment over the phone (by phone number, name, or a confirmation code) — prerequisite for UC-13/14/18.
- UC-31 multiple staff / resources / rooms — pick which (a specific technician, any available).
- UC-32 multi-tenant — which business's calendar this call is for.
- UC-33 list the tenant's calendars (
list_calendars).
E. LIFECYCLE & NOTIFICATIONS
- UC-34 confirmation + read-back before any write (already an invariant we test — never fabricate).
- UC-35 reminders (SMS/call before the appointment).
- UC-36 waitlist / cancellation backfill ("text me if something opens up Thursday").
- UC-37 change notifications (tell the caller/attendees when something moves or cancels).
F. CROSS-CUTTING (non-functional — apply to every use case)
- UC-X1 · Robust time understanding. Natural phrasings ("between 12 and 3",
"2-4pm", "after lunch", ISO datetimes) must map or re-ask — never
hard-escalate (the current bug; repro:
pytest tests/providers/test_slot_mapper.py -k time_range). - UC-X2 · Deterministic offers ("offers are quotes", #20). The caller picks a
backend-vetted
offer_id, not an LLM-formatted time — fixes UC-12 and makes the offer step testable (the robustscheduling_v1locked_choice model). - UC-X3 · Provider-agnostic. Every use case works on
google/google-oauth/caldavunchanged (the adapter abstracts it). - UC-X4 · Timezone + DST correctness. All of the above must be right across timezones and DST transitions (a known scheduling footgun; the adapter refuses unset/ambiguous TZ by design).
- UC-X5 · Degraded mode + idempotency. If the calendar is unreachable, degrade gracefully (callback), never fabricate a booking, and never double-book on retry (the B-CAL invariants we already test).
3. Proposed tutorial flows (for Fable 5 to design)
One small, focused tutorial flow per capability cluster (mirrors the existing
tutorial_* flows), each with a deterministic simulator like the one that caught
B-CAL-1/2. Suggested set (Fable 5 picks the v0 subset):
v0 (core reads + a fixed booking):
tutorial_list_availability— UC-1/3/4/5: read-only "when are you free."tutorial_list_events— UC-2/7/8: "what's on the calendar / what's blocked / how booked am I" (drives the newlist_eventstool).tutorial_book_appointment(fix the existing one) — UC-12 via the UC-X2 offer_id model so it stops escalating.
v1 (mutation + identity):
4. tutorial_manage_appointment — UC-13/14/18: reschedule / cancel / resize
(drives new update_event / cancel_event tools + the UC-30 lookup).
5. tutorial_hold_and_confirm — UC-15/21: tentative hold → confirm/expire.
later (advanced):
6. tutorial_recurring — UC-16/22: recurring + follow-up booking.
7. tutorial_multi_resource — UC-9/31/33: pick a staff/room, mutual
availability, multi-calendar.
8. tutorial_waitlist — UC-36: cancellation backfill / "text me if it opens."
Each tutorial demonstrates one capability cleanly, runs on all three calendar
systems, and ships with a SchedulerSim-style deterministic test suite.
Each tutorial flow demonstrates one capability cleanly, runs on all three calendar
systems, and ships with a SchedulerSim-style deterministic test suite.
3b. "Several different ways" — the demonstration matrix
The tutorials should not be a single happy-path script; they should show the same use case done several different ways, so an operator/learner sees the range. Four independent axes to demonstrate (a tutorial can hold one axis fixed and vary another):
| Axis | The "different ways" to show |
|---|---|
| Calendar system | the same flow on google, google-oauth, and caldav — proving provider-agnosticism (swap CAL_PROVIDER, nothing else changes) |
| Input modality | voice (spoken), DTMF/keypad (press 1–3 for a slot), and tap (pick a slot on the companion screen — ties to the remote-control work) — all resolving to the same decision |
| Offer / selection model | deterministic locked_choice offers (robust, testable — the scheduling_v1 style we hardened) vs LLM free-text (flexible but fragile — the book_appointment_v1 style that breaks today); the tutorials should make the trade-off visible and steer toward the offer_id model (#20) |
| Domain / vertical | the same scheduling capability across plumbing / massage / dental / apartment-viewing / generic appointment — showing the flow is a reusable module, not a one-off |
Concretely: tutorial_book_appointment should exist in a "voice + LLM-offers"
form (today's, fragile), a "voice + locked_choice-offers" form (robust), and a
"DTMF/tap" form — each documented so the difference is teachable, plus a note that
the same YAML runs on any of the three calendar backends.
3c. Documentation standard (every tutorial must ship with)
Nuance-rich designs get documented extensively — for humans and for the LLMs that read them. Each tutorial flow ships with:
- A spec (the design/contract): the use case, the states, the tools it calls, the offer model, and the calendar-systems it's verified on.
- A college-readable HTML tutorial (rendered via
scripts/render_doc.py): what the caller experiences, an annotated example transcript, and why each design choice was made (e.g., why offers are a locked_choice, why the LLM never formats a time). - Inline flow comments in the YAML explaining each state + guard (the flows are read by both operators and the model).
- A deterministic test suite (the
SchedulerSimpattern) whose test names read as the capability's behavior spec, listed in the tutorial doc. - A "run it" section: the exact
voice.html?flow=...URL, theCAL_PROVIDERenv to try each backend, and thepytestcommand for the deterministic tests.
Target audience for the docs: an operator who has never seen the flow should be able to run it in an afternoon, and Fable 5 (or a future agent) should be able to extend it from the spec alone.
4. What must be BUILT (the design/eng scope)
- New adapter tools:
list_events(get_events) andreschedule/cancel(update_event/cancel_event) surfaced as flow tools with the same slot-mapping discipline. (get_available_slots+create_eventalready surfaced.) - The #20 offer object: cal-provider returns concrete bookable offers with an
offer_id+policy_hash; booking passes anoffer_id; cal-provider revalidates. Removes the LLMtime_rangehand-formatting failure entirely. - Slot-mapper robustness (interim, if #20 is later): normalize ranges/ISO or re-ask (the repro test flips green when done).
- Tutorial flows + deterministic sims (§3), reusing
tests/scheduler_sim.pypatterns.
5. Open design questions for Fable 5
- One engine or two? Converge
scheduling_v1(deterministic locked_choice offers, hardened this session) andbook_appointment_v1(LLM offers, live) onto one canonical path — likely the offer_id model for both. - Read-only vs transactional entry points. UC-1/UC-2 are read-only queries; should they be their own top-level intents, or sub-flows of booking?
- Event lookup for reschedule/cancel. How does the caller identify their event over the phone (by time? by confirmation? by a code)? — affects UC-7/8.
- Multi-calendar / multi-tenant. Does the tutorial assume one calendar, or
surface
list_calendars? - How much does the LLM touch time at all post-#20 (ideally: never — it only picks an offer_id and reads back).
6. Handoff to Fable 5 — produce a v0 design from this
Next step: Fable 5 thinks through the above and produces a v0 design doc. This document is the input, not the design — it gives the use-case list (UC-1…UC-8), the capability surface that already exists, the "several different ways" demo matrix (§3b), the documentation standard every tutorial must meet (§3c), and the open questions to resolve (§5).
The backend already does the full read/write surface (list events, list free
slots, create/update/cancel) across three calendar systems; the v0 design should
cover: which capabilities to surface as new caller tools (list_events,
reschedule, cancel), the canonical offer model (the #20 offer_id — so the LLM
never formats a time), the set of tutorial flows and which "different ways" each
demonstrates (§3b), and the documentation deliverables per tutorial (§3c) — each
tutorial backed by a SchedulerSim-style deterministic test suite like the one
that caught B-CAL-1/2.
A good v0 answers §5's open questions, picks the tutorial-flow set, and specifies
the offer_id contract precisely enough that engineering can build the first
tutorial_list_availability + fixed tutorial_book_appointment behind it.