Scheduling Cluster Cross-PRD Explainer v0.1
For Zareef, Matt, Ryan

Four PRDs,
one scheduling surface.

The scheduling problem at a funeral home is four problems wearing one coat. This explainer separates them and shows how they fit back together.

The shape, in one paragraph

The cluster has four peer PRDs, not a parent and three children. Resources and Staff Availability are the data layer — what we own and who can work. Automatic Scheduling is the engine — it reads those two (plus case and custody state) and resolves slot proposals against conflicts. Calendar is the display layer — it surfaces everything to humans and is where manual edits happen.

The doctrinal rule that justifies the four-way split: people, resources, and the schedule are categorically different things. Folding any two of them into one entity produces the bugs the skeleton PRD was already producing.

The cluster, at a glance.

Two data PRDs at the bottom. The engine in the middle, reading from both. The calendar on top, reading from all three. Workflows are the primary caller of the engine, but they live outside this cluster.

Display Logic Data Calendar PRD Day / week / month views · Run-sheets · Manual event creation Reads from everything below. Owns no data. Automatic Scheduling & Conflict Handling PRD Slot-finding · Conflict detection · Path-to-yes guidance · Priority signals Deterministic engine. Suggest, human confirms. Resources PRD Chapels, vehicles, prep rooms Catalog state · Custody locations Staff Availability PRD Working patterns · Time off · On-call People are not resources. availability availability Workflows Primary caller (outside) Chain of Custody (future PRD) Reads resource catalog · References resources as locations custody state Legend writes / commits reads

Three categorically different things.

When this looked like one PRD, the model collapsed people into resources, folded availability into the calendar, and put conflict detection wherever it happened to land. Pulling the three things apart is the load-bearing rule for the whole cluster.

People

Have shifts and time off

Margaret, David, Jessica, Rob, Sophie. They have working patterns and exceptions to them. They get tired, take leave, and go on-call. They have an HR layer behind them — not a booking layer.

Resources

Have states and locations

Chapel A. Hearse 2. The prep room. They're owned things with operational properties — turnover times, custody-validity, out-of-service windows. They cannot be hard-deleted because custody records reference them forever.

The Schedule

Has events that claim both

A viewing at 2pm Thursday — claims a chapel and a director and a hearse. The event is the thing that intersects people and resources in time. It is not either of them.

Mash any two together and you get the bugs the skeleton PRD already had: resources.user_id linking a person into the resource table, cascading deletes that would have broken chain-of-custody, and a calendar that owned the data instead of just displaying it.

Each PRD, what it owns.

Each card answers four questions: what does it own, what does it explicitly not own, what does it read from, who reads from it. If you read nothing else in this explainer, read these four cards.

Resources
Data · Catalog

The catalog of physical things the home owns, and the rules that keep that catalog readable to chain-of-custody for years after a resource is retired.

Owns
Resource entities, categories, operational timing constraints, out-of-service windows, decommissioning, renaming, custody-location flag, branch scoping.
Not
People (Staff Availability), the calendar surface (Calendar), slot-finding (Engine), custody events themselves (Chain of Custody).
Reads
Branch list (from Tenant Configuration / Case Substrate).
Read by
Calendar, Engine, Chain of Custody, Workflows.
Staff Availability
Data · People

What hangs off the staff entity: working patterns, exceptions, on-call rotations, time-off requests, and the rules around who sees whose schedule for what reason.

Owns
Working patterns, availability exceptions, time-off request workflow, on-call assignments, employment-standards warnings, coverage-gap surfacing.
Not
The user/staff entity itself (Tenant Configuration), payroll/HR, the calendar surface, slot-finding, resources.
Reads
The users table (read-only), branch list.
Read by
Calendar, Engine, Workflows.
Automatic Scheduling & Conflict Handling
Logic · Engine

The deterministic engine that finds feasible slots, surfaces structured conflicts, and never silently reassigns work. AI sits above it, never in place of it.

Owns
Slot-finding, conflict detection, hard-vs-soft conflict distinction, path-to-yes guidance, slot-proposal-vs-commitment protocol, priority signals, post-commit conflict surfacing.
Not
The display of conflicts (Calendar), the resource catalog or staff availability (siblings), workflow internals (Workflows), case priority data (Case Substrate).
Reads
Resources, Staff Availability, case context, custody state, tenant-configured rules.
Read by
Workflows (primary caller), Calendar, staff making manual bookings.
Calendar
Display · Surface

The surface staff live in alongside the case view. Shows everything that's scheduled, alongside the availability the events draw on, persona-shaped without becoming five separate products.

Owns
Day / week / month views, manual event creation (case-scoped & non-case-scoped), event modification surface, run-sheet view, print & export.
Not
The resource catalog, staff availability data, slot-finding or conflict detection, workflow-event mechanics.
Reads
Resources, Staff Availability, Engine output, Workflows (for case context on workflow-created events).
Read by
Humans. Margaret weekly, Jessica all day, David on his phone, Rob via printed sheet, Sophie on mobile.

Two scenarios, end-to-end.

The four-way split only pays off when you watch a real flow cross between the documents. Each scenario is collapsed by default — open the one that maps to the work you're trying to understand.

01 Workflow-driven · Heavy path

The cremation workflow needs a slot

Workflows
Case K-2026-0143 advances past authorisation-signed. The cremation workflow module needs a slot on a cremator, with authorised staff, inside the BC regulatory window (≥48h after death, before the legislated outside bound). It calls into the engine in-process — no message bus.
Engine
Receives case_id, event type cremation, resource requirement cremator, staff requirement cremation_authorised_director, time window [2026-03-12, 2026-03-19], priority regulatory_window_bound.
Resources
The engine reads the catalog: which cremators are active (not decommissioned, not out-of-service), what their declared turnover times are, what hours they operate.
Staff Availability
The engine reads working patterns and exceptions for staff carrying the cremation_authorised_director attribute. David is off Friday. Sophie is on a course Tuesday morning.
Custody
The engine reads custody state — is the deceased actually in a cooler that's a valid custody location? Does the slot leave time for transport from cooler to cremator?
Engine
Composes buffers (cremator turnover + staff handover) against the constraint surface. Finds Wednesday 10:00 with Cremator 1 + Sophie. Returns a slot proposal.
Workflows
The workflow module commits the proposal. The engine re-evaluates against current state (first-commit-wins per §5.5), persists the event, audits the commit per ADR-014.
Calendar
The event now appears on Wednesday 10:00 in every persona's view. Jessica sees it on the week. David's run-sheet doesn't show it (he's not assigned). Sophie's run-sheet shows it as her first job of the day.
Staff Availability
Two days later: Sophie submits a time-off request for Wednesday — family emergency. Approved by Margaret. The approved exception becomes a hard signal.
Engine
Detects post-commit conflict: Sophie is no longer available for her committed event. The engine does not silently reassign — it surfaces the conflict for human decision.
Calendar
The event renders with a conflict indicator. Jessica opens it, sees "Sophie unavailable — reassign or reschedule," picks David instead. The path-to-yes is right there because the engine produced it.
02 Human-driven · Lighter path

A director books an arrangement meeting from the calendar

Director
Ends a first call with the Patel family. Agrees: they'll come in Thursday at 2pm for the arrangement meeting. Opens the calendar from the case view — case context is already supplied.
Calendar
Director picks Thursday 14:00, selects "Arrangement meeting" as the event type. The event-type vocabulary suggests defaults: arrangement room, assigned director (himself), 90 minutes.
Engine
The calendar calls the engine to evaluate the proposed slot. Arrangement Room 1 is free; the director is on shift; no buffer violations. Returns slot available.
Calendar
Director confirms. Calendar persists the event with a valid case reference (the §6.1 rule — case-scoped events cannot be persisted without one). The audited gem records a Layer 2 entry; the case's activity narrative gets a Layer 3 entry.
Calendar
The event appears on every persona view that touches Thursday afternoon. No workflow is aware of it — this was a manual case-scoped event, not workflow-initiated.
03 When the engine can't find a slot

Path-to-yes instead of "no"

Workflows
The transfer workflow needs a hearse + driver to leave the hospital in the next two hours.
Engine
Searches. Both hearses are claimed in the window. The one driver on shift is finishing a chapel service. Returns a conflict report, not a refusal.
Engine
The report includes structured path-to-yes alternatives: "Hearse 2 frees at 14:45 (45 min late) — accept?" · "On-call driver Mark could come in (employment-standards warning: under 11h rest)" · "Transfer van available instead of hearse (mismatch with case preferences flag)."
Calendar
Jessica sees the structured options. Picks "On-call Mark" and overrides the soft employment-standards warning, capturing reason. The engine commits with the override audited.
Outcome
The engine never made the decision. It produced the structured options that let a human make it quickly and visibly. This is the suggest, human confirms rule structurally — not as a UX flourish.

Where does X live?

Use this as a wayfinding reference when you're not sure which PRD owns a question. Bookmark this section.

QuestionLives inWhy
"Does this chapel still exist?"ResourcesThe catalog owns active / decommissioned state.
"Is Chapel A available Tuesday at 2pm?"Calendar reads ResourcesAvailability display is the calendar's surface; the underlying state is the catalog's.
"Is David working Saturday?"Staff AvailabilityWorking patterns and exceptions live there.
"Can David also take Saturday at 3pm without violating BC employment standards?"Engine reads Staff AvailabilityEmployment-standards rules are warnings produced by the engine, against availability data.
"Find me a cremation slot next week."EngineSlot-finding is the engine's primary capability.
"Why couldn't the workflow find a slot?"EngineThe conflict report — including path-to-yes alternatives — is the engine's output.
"How does the day view render an on-call window?"CalendarHow availability data renders is the display surface's call.
"How does turnover time get composed into buffer?"Resources declares · Engine composesThe catalog declares the constraint; the engine composes it into slot decisions.
"Can a viewing be scheduled in the prep room?"Resources (custody-location flag) · Engine (enforces)The flag is on the resource; the engine enforces it as a custody-state conflict.
"What time-off requests are pending?"Staff AvailabilityThe request workflow is owned by the availability PRD.
"What's the printable schedule for Rob tomorrow?"Calendar (the run-sheet view)The run-sheet is owned by the calendar; the priority signals that order it come from the engine.
"Can Jessica drag an event to a new time on the calendar?"Calendar (surface) · Engine (conflict check)The calendar owns the affordance; the engine checks the modified slot.
"Did Sophie's reassignment get recorded?"Calendar writes · audit per ADR-014Modifications happen at the calendar; auditing is cross-cutting per ADR-014.
"Who's on-call this weekend?"Staff AvailabilityOn-call rotations are a third availability state (working / off / on-call).
"What does the schema actually look like?"docs/database/mvp-rails-schema.mdNone of these PRDs own schema. Per the doctrine — PRDs describe intent, not DDL.

What's outside this cluster.

Three other PRDs come up constantly in the four scheduling documents. They are not part of the cluster — they read from it or call into it. Knowing where they sit prevents reading the wrong PRD.

Primary caller

Customizable Workflows

Workflow modules schedule the majority of case-scoped events. They ask the engine for slots and commit them. Module internals, the JSONB context envelope, the pipeline state machine — all live in the Workflows PRD, not here.

Reader · Future PRD

Chain of Custody

Custody records reference resources as the deceased's physical location. The no-hard-delete and decommissioned-resolvability rules in Resources exist for this consumer. The records themselves and the regulatory reporting on them are owned in a future PRD.

Reader · Substrate

Case Substrate

Case priority, deceased characteristics, family preferences, the assigned director — read by the engine when evaluating slots. The case reference itself is what makes case-scoped events case-scoped. Branches as tenant configuration also live here.