Files
support_backend/specs/008-sla-escalation/contracts/sla-escalation-contract.md
T
saqib mirandClaude Sonnet 5 199bd4eb4e plan: design for SLA and escalation feature (008)
Phase 0 research resolves the business-calendar working-hours algorithm
(day-by-day walk via luxon, the first date/timezone dependency in this
codebase), the workingHours JSON shape, most-specific SLA-policy match
(reusing 005/006's resolution pattern), durable pause/resume (absolute
due-date shift, no in-memory state), and a repeatable-job breach-detection
design over per-run delayed jobs. Phase 1 adds data-model.md (one additive
refinement beyond doc06: SLARun.firstResponseBreachedAt), the admin/read
contract, and six quickstart scenarios including a genuine process-restart
boundary test for Constitution Principle VII.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 12:23:35 +05:30

4.6 KiB

Contract: SLA and Escalation

Every admin CRUD/manual-escalation route below is gated by fastify.authenticate (research.md — known limitation inherited from 002/003/004/005/006/007). SLA-run creation, pause/resume, and breach detection have no public trigger endpoint — they run automatically off the domain event bus and the breach-detection BullMQ job (research.md), matching 007's "orchestration has no manual trigger endpoint" precedent.

SLA Policy admin

  • POST /admin/sla-policies — body { name, productId?, categoryId?, problemTypeId?, priority?, firstResponseMinutes, investigationMinutes?, resolutionMinutes, customerResponseMinutes?, businessCalendarId? }. 404 if productId/categoryId/businessCalendarId is given but doesn't exist.
  • GET /admin/sla-policies — list, optionally filtered by productId.
  • GET /admin/sla-policies/:id404 if not found.
  • PATCH /admin/sla-policies/:id — partial update, same existence checks as create.
  • DELETE /admin/sla-policies/:id — soft delete (active: false), never a hard delete (matches 005/006 precedent for policy-shaped config the system may still reference).

Business Calendar admin

  • POST /admin/business-calendars — body { name, timezone, workingHours }. 400 if timezone isn't a valid IANA zone name, or if any workingHours entry's start/end isn't a valid HH:mm pair with start < end.
  • GET /admin/business-calendars / GET /admin/business-calendars/:id404 if not found.
  • PATCH /admin/business-calendars/:id — same validation as create.
  • POST /admin/business-calendars/:id/holidays — body { date, description? }.
  • DELETE /admin/business-calendars/:id/holidays/:holidayId.

Escalation Policy / Rule admin

  • POST /admin/escalation-policies — body { name, productId? }. 404 if productId given but doesn't exist.
  • GET /admin/escalation-policies / GET /admin/escalation-policies/:id.
  • POST /admin/escalation-policies/:id/rules — body { triggerType, condition, targetNodeId, notify, active? }. triggerType validated against doc 05 §6's full 10-value set (research.md — only 2 are ever evaluated, all 10 are valid config). 404 if targetNodeId doesn't reference an existing HierarchyNode (FR-012).
  • PATCH /admin/escalation-policies/:id/rules/:ruleId — same validation as create.
  • DELETE /admin/escalation-policies/:id/rules/:ruleId — soft delete (active: false).

SLA run reads

  • GET /tickets/:ticketId/sla-run — the current SLARun for the ticket, or 404 if none was ever created (e.g. the ticket was never assigned, or no policy matched at assignment time).

Manual escalation

  • POST /tickets/:ticketId/escalate — body { targetNodeId, reason }. 404 if ticketId or targetNodeId doesn't exist (FR-017). Records an EscalationEvent with triggeredBy set to the calling actor and re-assigns via the same scoped-assignment path a rule-fired escalation uses (research.md).

Guarantees (callable contract)

  1. An SLARun is created the moment a ticket receives its first successful assignment (007), if and only if an active SLAPolicy matches the ticket's context — never for an unassigned ticket, never inventing a default policy when none matches (FR-005, US2).
  2. firstResponseDueAt/resolutionDueAt are always computed by walking the resolved policy's business calendar, excluding non-working hours, weekends, and holidays — never a naive createdAt + N hours addition (FR-004, SC-001).
  3. A ticket entering WAITING_FOR_CUSTOMER pauses its running SLARun; leaving it resumes with the remaining time preserved exactly — the paused duration is neither double-counted nor dropped, and this holds even if the process restarts while paused (FR-007/FR-008, SC-002).
  4. A breach is detected within one breach-detection job cycle of its due date passing, even if the process wasn't running at the exact due instant — never silently missed (FR-009, SC-003).
  5. A run that completes before its due date is never marked breached; a paused run is never marked breached (FR-010/FR-011).
  6. Every resolution_breach or first_response_breach detection evaluates every active EscalationRule matching that trigger type under the ticket's resolved EscalationPolicy, firing one EscalationEvent (and one scoped re-assignment) per matching rule — a breach with no matching rule is still recorded as breached, with no EscalationEvent (FR-013/FR-014/ FR-015, SC-004).
  7. A manual escalation to a nonexistent targetNodeId always returns 404 and creates neither an EscalationEvent nor a reassignment (FR-017, SC-005).