Files
support_backend/specs/008-sla-escalation/quickstart.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.7 KiB

Quickstart: Validating SLA and Escalation

Prerequisites: migrations applied; at least one hierarchy node/agent/product set up per 006-support-organization's and 007-orchestration-assignment's own quickstarts, since this feature starts an SLARun on a successful 007 assignment and escalation re-assigns through 007's engine.

Scenario 1 — policy definition and most-specific match (User Story 1)

  1. Create a global SLAPolicy (productId: null, ...) and a second, product-scoped policy for the same product with tighter minutes.
  2. Assign a ticket for that product (triggers Scenario 2's creation path).
  3. Expected: the SLARun resolves the product-scoped policy, not the global one.
  4. Delete the product-scoped policy's applicability (set active: false). Assign a new ticket for the same product. Expected: falls back to the global policy.

Scenario 2 — calendar-aware due dates on assignment (User Story 2)

  1. Create a BusinessCalendar with workingHours only Mon-Fri 09:00-17:00, timezone "America/New_York", and one Holiday next Monday. Attach it to an SLAPolicy with resolutionMinutes: 480 (one working day).
  2. Assign a ticket late on a Friday afternoon so that a naive createdAt + 480min would land on Saturday.
  3. Expected: resolutionDueAt lands the following Tuesday (Monday excluded as a holiday), never on the weekend.
  4. Assign a ticket for a product/category/priority combination matching no active policy. Expected: no SLARun is created; GET /tickets/:ticketId/sla-run returns 404.

Scenario 3 — durable pause/resume across a process restart (User Story 3)

  1. Assign a ticket (Scenario 2), note resolutionDueAt.
  2. Transition the ticket to WAITING_FOR_CUSTOMER. Expected: SLARun.status becomes paused, pausedAt set.
  3. Restart the application process (rebuild buildApp() fresh, simulating the restart the constitution's Principle VII requires surviving).
  4. Wait a real interval, then transition the ticket out of WAITING_FOR_CUSTOMER. Expected: SLARun.status becomes running; the new resolutionDueAt equals the original plus exactly the paused wall-clock duration — never reset to a fresh full duration.

Scenario 4 — durable breach detection (User Story 4)

  1. Assign a ticket against a policy with a very short resolutionMinutes (e.g. 1) and a 24/7 calendar (businessCalendarId: null).
  2. Wait past resolutionDueAt without resolving the ticket.
  3. Expected: within one breach-detection job tick, SLARun.status becomes breached, breachedAt set.
  4. Repeat, but resolve the ticket before resolutionDueAt passes. Expected: status reaches completed and is never later flipped to breached by a subsequent job tick.
  5. Repeat, but pause the run before resolutionDueAt passes. Expected: the run is never marked breached while paused, even after the due instant passes.

Scenario 5 — breach-triggered escalation and scoped re-assignment (User Story 5)

  1. Create an EscalationPolicy scoped to the ticket's product with an active EscalationRule (triggerType: "resolution_breach", targetNodeId set to a second hierarchy node with a different eligible agent).
  2. Reach a breached run (Scenario 4). Expected: exactly one EscalationEvent is created (ruleId set, toNodeId the rule's targetNodeId), and the ticket is reassigned to an agent eligible under that specific node — not re-resolved from the ticket's original context.
  3. Repeat with no matching EscalationRule for the resolved policy. Expected: the run is still marked breached; no EscalationEvent is created.

Scenario 6 — manual escalation (User Story 6)

  1. POST /tickets/:ticketId/escalate with a valid targetNodeId and a reason.
  2. Expected: an EscalationEvent is created (ruleId: null, triggeredBy the calling actor), and the ticket is reassigned through the same scoped path as Scenario 5.
  3. Repeat with a nonexistent targetNodeId. Expected: 404, no EscalationEvent created.
  4. Trigger a manual escalation on a ticket whose run is concurrently being auto-escalated by Scenario 5's breach path. Expected: both EscalationEvent rows are recorded; the final assignment reflects 007's already-tested concurrency handling, not a corrupted double-write.

What "done" looks like

All six scenarios pass, and together they demonstrate every functional requirement and success criterion in spec.md — including SC-002's explicit restart-survival requirement, which must be verified by an actual fresh buildApp() in the middle of the test, not merely by asserting on stored field values without ever exercising a real process boundary.