You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add one optional, response-only structured field, schedule, to the cancellation policy introduced by #780 (dev.ucp.lodging.policy.cancellation). The schedule expresses an RFC 3339 anchor, ordered anchor-relative cutoff tiers, and the outcome before and after those cutoffs. The existing required refundability classification and human-readable description remain unchanged.
This is an additive follow-up to #780, not a request to expand or delay its focused v1. It implements the direction explicitly welcomed by the Lodging DTC: keep classification-only v1, then add a structured schedule and evaluation vectors in a follow-up PR.
Motivation
The current #780 schema makes refundability machine-readable, while cutoff times and financial effects remain in description. A policy such as “free until 48 hours before check-in; one-night penalty thereafter” therefore still requires prose parsing before a Platform can answer “What is the penalty cutoff?” or identify the applicable outcome. Refundability is also time-dependent, but the wire object does not carry the transition schedule or its time anchor.
This policy class was raised while the shared policies[] primitive was designed in #572 and was subsequently tested in #776 and the public policy-cancellation prototype. That prototype includes a schema, evaluator, methodology, and test vectors, and has been evaluated against approximately 40 real policies across lodging, ticketing, and services. The proposed UCP change deliberately carries only the small lodging-specific deterministic core.
In this #780 response, Jing Li confirmed that the Lodging DTC chose classification plus description for the initial version, agreed that deterministic anchor-relative tiers and cutoffs are the right direction, and welcomed follow-up PRs containing a structured schedule and evaluation vectors.
The beneficiaries are Platforms and agents that need to compare or explain cancellation terms without scraping prose, and Businesses that want their terms represented consistently while retaining the legal human-readable fallback.
Goals
Add an optional, backward-compatible schedule to dev.ucp.lodging.policy.cancellation.
Deterministically represent common buyer-initiated lodging cancellation windows relative to a timestamped anchor.
Represent the recurring outcome families found in the corpus: Business-stated refund percentage, fixed cancellation fee, and unit deduction such as one night.
Define exact boundary, ordering, duration, timezone, and fallback semantics.
Preserve refundability, description, url, applies_to, and mixed policy types without changing their existing contracts.
Ship validated examples and evaluation vectors, including exact-cutoff and timezone cases.
Keep classification-only payloads valid and useful.
Non-Goals
Executing a cancellation, issuing a refund, or defining a post-purchase cancellation API.
Changing the lodging Booking lifecycle or the shared policies[] primitive.
Replacing refundability or the required human-readable description.
Parsing legal text, OCR, or defining a general-purpose legal rules language.
Modeling seller cancellation, seller postponement, no-show behavior, rescheduling, waivers, or force majeure in this first increment.
Purchase-relative cooling-off periods, business-day calendars, months or years, jurisdictional overrides, or compound operations such as lesser_of.
Guaranteeing a cash amount for a percentage or unit deduction when the response does not provide an unambiguous price basis.
Detailed Design
Schema surface
The only new property on cancellation_item is optional:
If schedule is present, anchor, a non-empty tiers array, and after_last_tier are required as one atomic value. If schedule is absent, the #780 representation is unchanged.
The new definitions live in source/schemas/lodging/policy_cancellation.json. The proposal does not modify booking.json, the shared policy type, transports, or request schemas.
Time and tier semantics
anchor is a calendar-valid RFC 3339 date-time and MUST contain Z or an explicit UTC offset. For lodging, it normally represents the property's stated arrival/check-in cutoff.
Schedule arithmetic uses Unix time (POSIX), with exactly 86400 seconds per date and no leap seconds. The supported timestamp profile for anchor and the evaluation instant at requires seconds from 00 through 59; leap-second labels with :60 are unsupported. Offsets including -00:00 identify the UTC instant without inferring a property timezone.
until is an ISO 8601 elapsed duration before anchor, limited in this first version to days, hours, minutes, and seconds. Months, years, local-calendar days, and business days are excluded.
A day in until is exactly 24 elapsed hours.
tiers MUST be ordered from the farthest cutoff before anchor to the nearest cutoff. Durations are compared after normalization to elapsed seconds, MUST be strictly decreasing, and MUST NOT repeat; equivalent forms such as P1D and PT24H denote the same cutoff.
Intervals are half-open, matching UCP's existing time-window convention in fix!: define deterministic operating hours for Location service #687. Given an evaluation instant at, a consumer evaluates tiers in array order. The first tier for which at is strictly earlier than anchor minus until applies.
At exactly anchor minus until, evaluation advances to the next tier. If no tier matches, after_last_tier applies.
The consumer supplies the evaluation instant; the schedule does not freeze “now” into the policy.
Before selecting any tier, a Platform MUST validate the entire schedule, including ordering, and the calendar validity of anchor and at. Passing JSON Schema validation alone is insufficient when format is annotation-only. Invalid or unsupported timestamps make structured evaluation unavailable; they MUST NOT be silently repaired, rounded, or normalized to a different instant.
Outcomes
kind is an open string discriminator. This proposal defines three well-known kinds:
percentage: requires buyer_bps, an integer from 0 through 10000 describing the Business-stated refund percentage under the policy. Zero means no refund and 10000 means a full refund. This increment does not identify the monetary basis.
fixed_fee: requires penalty.amount. The amount is a cancellation charge in UCP integer minor units and uses the root currency field of the Booking response, whether retained from an earlier payment or charged later.
unit_deduction: requires penalty.measure using the shared UCP measure shape. The measure has a positive value, a stable open unit identifier, required display_text, and an effective scale of zero so that this version represents whole lodging units. night is the well-known lodging unit for this proposal.
Known kinds receive conditional schema validation. For a known kind, a Business emits only the fields defined for that kind and a Platform ignores unrelated outcome members. Additional kind and unit values should use reverse-domain identifiers. An implementation that encounters an unknown kind MUST tolerate the value, MUST NOT infer its meaning or claim a deterministic evaluation, and MUST render description as fallback.
Percentage and unit-deduction outcomes are deterministic symbolic terms, but they do not necessarily determine a cash amount. A Platform MUST NOT convert either outcome to money unless the governing Booking data supplies an unambiguous price basis; otherwise it presents the symbolic percentage or unit outcome.
Interaction with existing fields
refundability remains the current high-level classification defined by #780. It is the Business's authoritative point-in-time summary at response generation, and the Business MUST ensure that it accurately summarizes the schedule outcome selected at that instant for the governed scope. A Platform treats refundability as authoritative for that snapshot and MUST NOT replace it with a classification inferred from kind or a guessed monetary basis. For evaluation at an explicit instant, schedule governs: the Platform selects the applicable outcome and does not extrapolate the earlier refundability snapshot to that different instant. Crossing a cutoff later does not make the earlier payload contradictory.
description remains the universal human-readable and legal fallback. It MUST carry the complete cancellation timeline and agree with the current refundability summary. Businesses MUST NOT emit a structured schedule that contradicts description. If a Platform detects a contradiction or cannot evaluate a structured value safely, it treats structured evaluation as unavailable and presents description rather than acting on a guessed result.
applies_to continues to define the policy scope. No new targeting mechanism is introduced.
Targeting does not define charge multiplicity. A Platform MUST NOT multiply a fixed fee or unit deduction solely by the number of matched nodes. For example, a USD 7500 fixed fee governing two room rates does not by itself establish an aggregate charge of USD 15000. Aggregation requires explicit Business terms and sufficient Booking data; otherwise the declared terms are presented without an inferred aggregate.
Risks and Mitigations
Financial misinterpretation: an incorrect boundary or basis could show a wrong refund. Mitigation: normative half-open-boundary and elapsed-duration rules, positive and negative vectors, explicit symbolic handling for unit deductions, and no execution behavior in this proposal.
Contradictory representations: refundability, schedule, and description could disagree. Mitigation: a Business consistency requirement plus a safe Platform fallback to description instead of guessed evaluation.
Timezone and DST errors: local dates or calendar arithmetic can shift deadlines. Mitigation: an RFC 3339 anchor with explicit offset, elapsed durations only, and timezone/DST boundary vectors.
Backward compatibility: older clients will not understand schedule. Mitigation: the field is optional and response-only; existing required fields remain, and UCP classifies adding an optional response field as backward-compatible.
Vocabulary complexity: a closed union would block future domain outcomes, while an open union can be unsupported. Mitigation: open kind values, strict validation of well-known kinds, and required fallback behavior for unknown kinds.
Maintenance and performance: tier evaluation adds logic. Mitigation: one ordered linear array, no recursive expressions or calendar engine, and a deliberately narrow first increment.
Security and privacy: the field introduces no credentials or PII. Implementations still must treat policy text and URLs under the existing UCP security rules.
Valid percentage, fixed-fee, and one-night schedules pass.
A mixed policies[] array containing cancellation and unrelated policy types remains valid.
Missing anchor, empty tiers, missing after_last_tier, malformed RFC 3339 timestamps, unsupported duration units, invalid basis points, invalid amounts, and fractional or non-positive unit measures fail.
Examples are validated against the composed cancellation extension schema, not only the base lodging Booking schema.
Existing UCP schema lint, generated Python model, pre-commit, strict documentation build, and example validation checks pass.
Evaluation vectors
The stacked Draft includes a portable JSON fixture at scripts/fixtures/lodging_cancellation_schedule.json and a test-only runner at scripts/test_cancellation_schedule.py, integrated with the documentation workflow and local pre-commit checks. It currently contains 44 selection/fallback vectors and 22 explicit schema expectations. The test metadata is not part of the wire protocol. The runner validates complete schedules before selection, uses exact arithmetic, and neither calculates money nor derives refundability.
These vectors cover:
before the earliest cutoff;
exactly at each cutoff, advancing to the next tier;
one instant after each cutoff;
multiple ordered tiers;
after_last_tier;
anchors with Z and non-zero offsets;
a duration crossing a DST transition, confirming elapsed-time behavior;
selection of fixed-fee and one-night symbolic outcomes;
absent schedule;
unknown kind fallback.
Additional executable cases cover invalid calendar dates, normalized duplicate/reversed durations, invalid unselected outcomes, exact fractional instants, large durations, mixed duration components, trailing-newline rejection, and POSIX arithmetic across a leap-second boundary. Contradiction with external legal prose remains a normative fallback requirement, not a machine-tested assertion in this runner.
Cross-check the vectors against an adapted version of the existing public evaluator and at least one independent implementation before advancement to Candidate. The adaptation will intentionally change its current inclusive boundary to UCP's half-open convention. The current test oracle does not satisfy that two-implementation graduation criterion by itself. The maintainers can decide whether to retain the neutral fixture here or move it to the UCP conformance repository.
Schema, documentation, generated-model, and example validation checks pass.
Boundary and timezone vectors produce identical results in two independent evaluators.
The shape has been checked against at least 10 representative lodging policies from at least three sources, with methodology and limitations documented.
At least one Business-side producer and one Platform/agent-side consumer have completed an interoperability test.
Lodging DTC/TC majority vote to advance.
Candidate → Stable:
Adoption feedback from at least two independent organizations has been collected and addressed.
Conformance vectors, implementation guidance, and backward-compatibility guidance are published.
No unresolved ambiguity remains for anchors, boundaries, ordering, monetary basis, unit deductions, or fallback behavior.
Summary
Add one optional, response-only structured field, schedule, to the cancellation policy introduced by #780 (dev.ucp.lodging.policy.cancellation). The schedule expresses an RFC 3339 anchor, ordered anchor-relative cutoff tiers, and the outcome before and after those cutoffs. The existing required refundability classification and human-readable description remain unchanged.
This is an additive follow-up to #780, not a request to expand or delay its focused v1. It implements the direction explicitly welcomed by the Lodging DTC: keep classification-only v1, then add a structured schedule and evaluation vectors in a follow-up PR.
Motivation
The current #780 schema makes refundability machine-readable, while cutoff times and financial effects remain in description. A policy such as “free until 48 hours before check-in; one-night penalty thereafter” therefore still requires prose parsing before a Platform can answer “What is the penalty cutoff?” or identify the applicable outcome. Refundability is also time-dependent, but the wire object does not carry the transition schedule or its time anchor.
This policy class was raised while the shared policies[] primitive was designed in #572 and was subsequently tested in #776 and the public policy-cancellation prototype. That prototype includes a schema, evaluator, methodology, and test vectors, and has been evaluated against approximately 40 real policies across lodging, ticketing, and services. The proposed UCP change deliberately carries only the small lodging-specific deterministic core.
In this #780 response, Jing Li confirmed that the Lodging DTC chose classification plus description for the initial version, agreed that deterministic anchor-relative tiers and cutoffs are the right direction, and welcomed follow-up PRs containing a structured schedule and evaluation vectors.
The beneficiaries are Platforms and agents that need to compare or explain cancellation terms without scraping prose, and Businesses that want their terms represented consistently while retaining the legal human-readable fallback.
Goals
Non-Goals
Detailed Design
Schema surface
The only new property on cancellation_item is optional:
If schedule is present, anchor, a non-empty tiers array, and after_last_tier are required as one atomic value. If schedule is absent, the #780 representation is unchanged.
The new definitions live in source/schemas/lodging/policy_cancellation.json. The proposal does not modify booking.json, the shared policy type, transports, or request schemas.
Time and tier semantics
Outcomes
kind is an open string discriminator. This proposal defines three well-known kinds:
Known kinds receive conditional schema validation. For a known kind, a Business emits only the fields defined for that kind and a Platform ignores unrelated outcome members. Additional kind and unit values should use reverse-domain identifiers. An implementation that encounters an unknown kind MUST tolerate the value, MUST NOT infer its meaning or claim a deterministic evaluation, and MUST render description as fallback.
Percentage and unit-deduction outcomes are deterministic symbolic terms, but they do not necessarily determine a cash amount. A Platform MUST NOT convert either outcome to money unless the governing Booking data supplies an unambiguous price basis; otherwise it presents the symbolic percentage or unit outcome.
Interaction with existing fields
refundability remains the current high-level classification defined by #780. It is the Business's authoritative point-in-time summary at response generation, and the Business MUST ensure that it accurately summarizes the schedule outcome selected at that instant for the governed scope. A Platform treats refundability as authoritative for that snapshot and MUST NOT replace it with a classification inferred from kind or a guessed monetary basis. For evaluation at an explicit instant, schedule governs: the Platform selects the applicable outcome and does not extrapolate the earlier refundability snapshot to that different instant. Crossing a cutoff later does not make the earlier payload contradictory.
description remains the universal human-readable and legal fallback. It MUST carry the complete cancellation timeline and agree with the current refundability summary. Businesses MUST NOT emit a structured schedule that contradicts description. If a Platform detects a contradiction or cannot evaluate a structured value safely, it treats structured evaluation as unavailable and presents description rather than acting on a guessed result.
applies_to continues to define the policy scope. No new targeting mechanism is introduced.
Targeting does not define charge multiplicity. A Platform MUST NOT multiply a fixed fee or unit deduction solely by the number of matched nodes. For example, a USD 7500 fixed fee governing two room rates does not by itself establish an aggregate charge of USD 15000. Aggregation requires explicit Business terms and sufficient Booking data; otherwise the declared terms are presented without an inferred aggregate.
Risks and Mitigations
Test Plan
Schema and documentation validation
Evaluation vectors
The stacked Draft includes a portable JSON fixture at scripts/fixtures/lodging_cancellation_schedule.json and a test-only runner at scripts/test_cancellation_schedule.py, integrated with the documentation workflow and local pre-commit checks. It currently contains 44 selection/fallback vectors and 22 explicit schema expectations. The test metadata is not part of the wire protocol. The runner validates complete schedules before selection, uses exact arithmetic, and neither calculates money nor derives refundability.
These vectors cover:
Additional executable cases cover invalid calendar dates, normalized duplicate/reversed durations, invalid unselected outcomes, exact fractional instants, large durations, mixed duration components, trailing-newline rejection, and POSIX arithmetic across a leap-second boundary. Contradiction with external legal prose remains a normative fallback requirement, not a machine-tested assertion in this runner.
Cross-check the vectors against an adapted version of the existing public evaluator and at least one independent implementation before advancement to Candidate. The adaptation will intentionally change its current inclusive boundary to UCP's half-open convention. The current test oracle does not satisfy that two-implementation graduation criterion by itself. The maintainers can decide whether to retain the neutral fixture here or move it to the UCP conformance repository.
Graduation Criteria
Working Draft → Candidate:
Candidate → Stable:
Implementation History
Code of Conduct