Service-to-service calls propagate remaining deadline budget, refuse work that cannot finish inside it, and distinguish client cancellation from deadline expiry in status, retries, and traces.
Package status: reference context ready for human review. The contract and test scenarios are complete, but no claim is made that an adopting implementation has passed them.
Decision
Every service-to-service hop inherits a remaining deadline from its caller, subtracts a hop safety margin, and forwards that remainder. If the remainder is below the refuse threshold, the service does not start the next downstream call or an expensive local unit of work. Deadline expiry and client cancellation are different terminal statuses: expiry means the budget ended; cancel means the caller aborted while budget remained. Neither class is retried by default. Traces record remaining milliseconds at hop start.
Scope
- In-process and RPC hops for service-to-service APIs in the adoption profile.
- Header mapping, refuse-before-start, cancellation propagation, retry prohibition, and trace attributes.
Outside this block
- Token-bucket quotas for public callers (related rate-limiting package).
- Failing fast when a dependency breaker is open (related circuit-breaker package).
Contract
- An inbound deadline becomes a monotonic remaining budget; wall-clock Date headers are not the source of truth.
- Outbound RPCs send a timeout strictly less than the current remainder minus the hop margin.
- If remainder is below the refuse threshold, no further downstream call starts and the caller receives the refused-too-late code.
- When the budget reaches zero during a hop, the status is deadline expired, not cancelled.
- When the caller aborts while remainder is positive, the status is cancelled and in-flight children are cancelled.
- Automatic retries are disabled for both expiry and cancel; a new attempt requires a new parent deadline from the original client.
Implementation guidance
- Store a deadline instant on request context at the edge and compute remainder at each hop. Pass cancellation tokens so children do not run as orphans.
- An open circuit breaker is not converted into a deadline error.
Failure handling and safeguards
- A missing inbound deadline uses the profile edge total, then propagates; it does not mean infinite time. Remainder uses monotonic time where the runtime provides it.
- Do not translate expiry into 429; that confuses quota with time.
Verification and operations
- Inject a short remainder and assert the last hop is skipped. Distinguish cancel and expiry in traces with distinct status codes and remaining-ms.
The executable-looking examples in this package are fixtures and acceptance contracts. Run
python tools/validate.py from the collection root to check package structure and metadata; then
implement and execute the scenarios in the target repository.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - The adopting team must map actors, data classes, error vocabulary, and ownership to its system.