Skip to content
A robot comparing a tenant scheduling policy with a clinic override and checking an effective policy snapshot
The same appointment service can produce a different result after composing each clinic’s policy.

Why can the same request produce a different result?

Section titled “Why can the same request produce a different result?”

A patient can select the same product and request the same time while receiving a different result at another clinic. Clinic A may require a staff member to confirm the request. Clinic B may confirm it immediately when the conditions have already been validated. This is not a matter of scattering clinic-name exceptions through booking code. The policy used for the decision is composed from a tenant default and a clinic-specific rule.

To explain the difference, keep four boundaries separate.

BoundaryQuestion it answers
ScopeDoes this rule apply to every clinic or only to one clinic?
SelectionDoes the clinic inherit the default, replace its value, or disable an optional feature?
TimeWhen was the decision made, and when will the visit service happen?
HistoryCan we explain the original decision after the policy changes?

A settings table alone cannot answer the last two questions. The design therefore stores policies as versioned contracts and produces an effective policy snapshot for the booking decision.

A tenant default and a clinic override are composed deliberately

Section titled “A tenant default and a clinic override are composed deliberately”

Policy scope has two layers.

ScopeRoleExample
Tenant defaultThe baseline used by every clinic in the tenantDefault approval mode, safety ceilings, operating hours
Clinic overrideA clinic’s deliberate operational differenceExtra review for one service, disabling an optional feature

A clinic override has three explicit intents.

IntentMeaningResult
INHERITUse the tenant default as-isThe default value is recorded in the effective policy
SETChoose a clinic-specific valueThe clinic policy becomes the source for that path
DISABLEDo not use an optional featureThe disabled path is recorded separately

INHERIT must not be treated as null. Null can mean “not known yet”; INHERIT means “deliberately follow the tenant value.” Conversely, a required policy or safety ceiling cannot be disabled or relaxed through DISABLE.

Flow from a tenant default and clinic override through composition and activation checks to an effective policy snapshot
The tenant default and clinic override pass through an explicit composition step before becoming an effective policy snapshot.

Keeping this composition in one place avoids reinterpreting the same rule in every caller. It also gives operations a direct answer to “why was this clinic handled differently?” The implementation separates INHERIT, SET, and DISABLE in the payload contract, while the compiler records the source of each policy path.

A policy is a versioned contract, not just a setting

Section titled “A policy is a versioned contract, not just a setting”

Changing a number or option on an operations screen must not silently affect every booking. A change needs validation, approval, an activation time, and a boundary for failure or recovery.

The policy lifecycle is intentionally constrained.

DRAFT ──▶ SCHEDULED ──▶ ACTIVE ──▶ RETIRED
└──────────────▶ ACTIVE
└────────────────────────────▶ RETIRED
StageMeaningWhat operations should verify
DRAFTA policy that is not active yetRequired fields and safety ceilings pass validation
SCHEDULEDA policy to apply at a future timeThe activation time and expected impact are clear
ACTIVEThe policy used for current readsIt was activated with the expected generation and versions
RETIREDA policy no longer used for new decisionsIts historical record remains traceable

Drafting, validation, preview, approval, scheduled or immediate activation, and retirement are separate steps. Approval is not the same thing as a lifecycle transition. An approved policy still needs a fresh check when another operator has changed the generation or activated a different version first.

An effective policy snapshot explains the decision later

Section titled “An effective policy snapshot explains the decision later”

The booking decision needs more than “the clinic settings right now.” It needs the policy bundle that was effective at that moment. The snapshot carries the following fields together.

FieldRole
decisionAtWhen the policy was evaluated
serviceAtWhen the visit service is expected to happen
generationThe tenant and clinic policy generation vector
sourceVersionsThe tenant and clinic source versions used for each policy kind
sourceByPathWhether each value came from the tenant default or clinic override
snapshotHashA hash that confirms identical input produced identical output

Conceptually, this is an immutable value object:

data class EffectiveSchedulingPolicy(
val decisionAt: Instant,
val serviceAt: Instant,
val generation: PolicyGenerationVector,
val sourceVersions: Map<SchedulingPolicyKind, SourceVersion>,
val sourceByPath: Map<String, PolicyValueSource>,
val snapshotHash: String,
)

The compiler also rejects a serviceAt that is earlier than decisionAt. The resulting snapshot keeps booking requests, previews, and operations from reading policy independently, and makes the decision reproducible later. It does not mean the policy is automatically applied to every booking; it means the policy to apply has been fixed as one verifiable result.

Close activation conflicts by preserving the previous policy

Section titled “Close activation conflicts by preserving the previous policy”

Two operators can try to activate a policy for the same clinic at the same time. If the last request always wins, an earlier approved change can be overwritten without notice. The activation request therefore carries an expected revision and generation, and the write uses a CAS (Compare-And-Set) boundary.

When the generation has changed, the system should preserve the previous active policy rather than forcing the new one through:

  • Reject activation when the expected generation no longer matches.
  • Return the same result when a request is retried with the same idempotency key.
  • Keep the previous ACTIVE policy when a scheduled activation misses its activation point.
  • Let the operator preview and approve a new generation after understanding the conflict.

This removes the assumption that a button click means “the policy must have changed.” Activation success and a fresh effective-policy read are separate checks that let operations confirm the outcome.

A policy change does not rewrite an existing confirmed commitment

Section titled “A policy change does not rewrite an existing confirmed commitment”

Policy changes are future-only. Recalculating a visit that has already been confirmed with the current policy would change a commitment the system has already made to the patient.

Existing stateDefault treatment when policy changes
PROPOSEDMay be evaluated with the new policy
HELDMay use the new policy when the hold expires
CONFIRMEDProtect the confirmed visit commitment; do not rewrite it automatically

If a confirmed time or price must change, create a new proposal and obtain the patient’s consent again instead of overwriting the existing commitment. This is why the policy snapshot and the visit-commitment snapshot are separate. A policy is a rule for future decisions; a confirmed visit is an outcome already promised externally.

The boundary that is implemented, and the boundary that is still separate

Section titled “The boundary that is implemented, and the boundary that is still separate”

This foundation creates the boundary for authoring, validating, reading an effective snapshot, and activating a policy safely. Connecting the booking-creation path so it consumes that policy is a separate integration step. The feature flags are therefore staged and disabled by default:

  1. shadow compile
  2. effective read
  3. admin write
  4. preview worker
  5. scheduled activation

Staging the flags makes it possible to verify that persisted policy data does not unexpectedly affect the existing booking flow. “The system can store a policy” and “booking outcomes change according to that policy” are different release claims.

Clinics have different booking rules not because exceptions are scattered through the code. The service composes a tenant default with an explicit clinic override, then fixes the result as an effective policy snapshot with generation, source, and hash information.

The boundaries are deliberate:

  • INHERIT, SET, and DISABLE express different intent.
  • A policy has versions and a lifecycle.
  • CAS and generation checks close activation conflicts.
  • An existing CONFIRMED visit commitment is not rewritten by a future policy.

The next article follows this boundary into the waitlist. Who should receive a proposal when a slot becomes available depends on when the policy is read and on the rule that protects a confirmed visit commitment.

Comments

Leave a note or reaction with your GitHub account.