Files
support_backend/specs/008-sla-escalation/plan.md
T
saqib mirandClaude Sonnet 5 bb31e9d641 tasks: task breakdown for SLA and escalation feature (008)
48 tasks across 9 phases (6 user stories + setup/foundational/polish),
sequenced US1 (policy definition) -> US2 (calendar-aware run creation) ->
US3 (durable pause/resume, P1-complete MVP) -> US4 (breach detection) ->
US5 (breach-triggered escalation) -> US6 (manual escalation), each
dependent on the last since every story builds on the previous one's
mechanism rather than being independently orderable.

Also folds in two design refinements found while cross-checking the
existing scaffold against research.md's plan: TICKET_ASSIGNED (defined in
domain-events.ts since 007, never published) becomes the real wiring point
for SLA-run creation, and src/jobs/sla//src/jobs/escalation/ turn out to
already exist as their own stub scaffolding, reused rather than
duplicated.

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

11 KiB

Implementation Plan: SLA and Escalation

Branch: 008-sla-escalation | Date: 2026-09-03 | Spec: spec.md

Input: Feature specification from specs/008-sla-escalation/spec.md

Summary

Populate the existing platform/business-calendars, orchestration/sla, and orchestration/escalation stub directories (today: isWorkingHour hardcoded true, a SlaDueDateCalculator doing naive createdAt + hours addition, an EscalationEngine that always returns { escalated: false }) with the real engine: business-calendars walks a BusinessCalendar's workingHours/Holiday records via luxon to compute calendar-aware durations; sla resolves the most-specific matching SLAPolicy on a ticket's first successful 007 assignment, computes firstResponseDueAt/resolutionDueAt through the calendar walk, durably pauses/resumes on WAITING_FOR_CUSTOMER transitions (a TICKET_UPDATED domain-event subscriber), and detects breaches via a repeatable BullMQ job; escalation resolves the applicable EscalationPolicy, fires an EscalationEvent per matching active EscalationRule on a breach (or on a manual request), and re-assigns through a new, specifically-scoped entry point added to 007's AssignmentEngine.

Technical Context

Language/Version: TypeScript 5.4 / Node.js 20+.

Primary Dependencies: Prisma (new models), Zod, BullMQ (already a dependency — new repeatable job, same queue infrastructure as 003's attachment scan and 005's AI session queues), luxon (new — the first date/timezone library in this codebase; research.md).

Storage: PostgreSQL via Prisma (new SLAPolicy, SLARun, BusinessCalendar, Holiday, EscalationPolicy, EscalationRule, EscalationEvent models). No new infrastructure — reuses src/infrastructure/queue for the breach-detection job, same as every prior BullMQ consumer.

Testing: Vitest — unit tests for the calendar-walk algorithm (weekend/holiday exclusion, partial-day clipping, timezone correctness), the most-specific SLA-policy match, and pause/resume arithmetic; integration tests for the full assignment→SLA-run→pause/resume→breach→escalation flow against real Postgres/Redis, including one test that rebuilds buildApp() mid-test to verify pause/resume state survives a genuine process-restart boundary (Constitution Principle VII, quickstart Scenario 3) — the first feature in this codebase whose correctness depends on that guarantee specifically, not just within-process concurrency safety.

Target Platform: Same Fastify modular monolith. Populates existing module directories: src/modules/platform/business-calendars/, src/modules/orchestration/{sla,escalation}/. Adds one new BullMQ worker registration alongside the existing ones in src/infrastructure/queue.

Project Type: Backend service — single project.

Performance Goals: The breach-detection job must complete a full scan-and-mark pass in well under its own tick interval even as SLARun rows accumulate — indexed on (status, resolutionDueAt) so the query stays a targeted range scan, not a table scan. Not otherwise performance-sensitive.

Constraints: MUST NOT compute due dates naively (FR-004); MUST create an SLARun only on a successful assignment with a matching policy (FR-005); MUST survive a process restart for pause/resume and breach detection (FR-007/FR-008/FR-009, Constitution Principle VII); MUST never mark a completed-in-time or paused run breached (FR-010/FR-011); MUST re-assign scoped to the rule's exact targetNodeId, not a fresh unscoped resolution (FR-014).

Scale/Scope: Three populated modules, one new dependency, three admin CRUD surfaces (SLA policies, business calendars, escalation policies/rules), one manual-escalation endpoint, one new BullMQ repeatable job, one new method on 007's AssignmentEngine. Explicitly excludes: notification delivery, 8 of doc 05's 10 escalation trigger types, investigation/customer-response timers, SLA restart on ticket reopen (see spec.md Assumptions).

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle / Section Check Result
I. SaaS Is the Sole Identity & Access Authority SLA/escalation reference Ticket/HierarchyNode/Agent — all SupportHub's own domain. No SaaS identity touched. PASS
II. Configuration Over Hardcoding Every SLA target, calendar, and escalation rule is admin-configured data, not a hardcoded constant — replacing the literal hardcoded-true/naive-arithmetic stubs is the point of this feature. PASS
III. Layered Architecture With Enforced Module Boundaries Three modules follow the standard shape; orchestration/slaplatform/business-calendars and orchestration/escalationorchestration/sla (for breach signals) and →orchestration/assignments (007, for the new scoped-assignment method) are all one-directional — no cycle, since 007 doesn't import anything from 008. PASS
IV. AI Recommends, Deterministic Policy Decides No AI involvement in this feature at all — every decision (policy match, breach, escalation) is deterministic. PASS — N/A
V. Evidence-Based Verification Not applicable — no resolution/verification concept in this feature. PASS — N/A
VI. Durable Audit & History EscalationEvent is the durable, append-only record doc 06 defines for every escalation, automatic or manual — mirrors AssignmentHistory's established shape. PASS
VII. Concurrency-Safe, Durable Job Handling This principle's "state must survive a process restart" clause is directly load-bearing here for the first time as the primary correctness requirement (not just a concurrent-request race) — pause/resume and breach detection are both pure-DB-state-plus-polling-job, no in-memory timer anywhere (research.md, quickstart Scenario 3). PASS
VIII. Problem and Ticket Are Separate, Related Entities SLA/escalation reference Ticket, not Problem — doesn't touch the distinction. PASS — N/A
Technology & Platform Constraints Prisma + Zod + existing BullMQ infrastructure, plus the one new luxon dependency (justified in research.md — no timezone-correct alternative already exists in this codebase). PASS

No violations requiring Complexity Tracking justification.

Post-Design Constitution Re-check

All gates above remain PASS after Phase 1 design. Worth calling out against Principle VII explicitly: pause/resume shifts a single absolute DateTime column and breach detection is a plain polling query — by design there is no code path in this feature that could even appear to depend on in-memory state surviving a restart, which is what makes the restart-boundary integration test (quickstart Scenario 3) a meaningful verification rather than a formality.

Project Structure

Documentation (this feature)

specs/008-sla-escalation/
├── plan.md              # This file
├── research.md          # Phase 0 output
├── data-model.md         # Phase 1 output
├── quickstart.md          # Phase 1 output
├── contracts/              # Phase 1 output
└── tasks.md                # Phase 2 output (/speckit-tasks — not created here)

Source Code (repository root)

supporthub-api/
├── package.json                                # MODIFIED — add luxon, @types/luxon
├── prisma/
│   └── schema.prisma                            # MODIFIED — add SLAPolicy, SLARun,
│                                                    BusinessCalendar, Holiday, EscalationPolicy,
│                                                    EscalationRule, EscalationEvent
├── src/
│   ├── events/
│   │   └── handlers/index.ts                     # MODIFIED — first real publish of the
│   │                                                existing-but-unused TICKET_ASSIGNED event
│   │                                                (from 007's persistAndTransition), plus two
│   │                                                new TICKET_UPDATED subscribers (pause/
│   │                                                resume, completion) — research.md
│   ├── jobs/
│   │   └── sla/index.ts                          # REPLACED stub — schedules the repeatable
│   │                                                breach-detection job (research.md); jobs/
│   │                                                escalation/ stays untouched (reserved for a
│   │                                                future notification-dispatch step)
│   └── modules/
│       ├── platform/
│       │   └── business-calendars/               # REPLACED stub — full standard shape +
│       │       ├── controller/ routes/ schema/      calculators/ for the day-walk algorithm
│       │       │   repository/ service/ types/
│       │       │   mapper/ constants/ index.ts
│       │       └── calculators/
│       └── orchestration/
│           ├── assignments/                      # 007, MODIFIED — persistAndTransition
│           │   └── engine/assignment.engine.ts       publishes TICKET_ASSIGNED; new
│           │                                          assignToSpecificNode() method for
│           │                                          escalation's scoped re-assignment
│           ├── sla/                              # REPLACED stub — full standard shape, keeps
│           │   ├── controller/ routes/ schema/      its existing calculators/ dir (due-date
│           │   │   repository/ service/ types/      calculator replaced, not removed) and adds
│           │   │   mapper/ constants/ index.ts      engine/ for breach evaluation
│           │   ├── engine/                          (policy resolution + breach detection)
│           │   └── calculators/                     (due-date calculator, replaced)
│           └── escalation/                        # REPLACED stub — full standard shape, keeps
│               ├── controller/ routes/ schema/       engine/ for rule matching + firing
│               │   repository/ service/ types/
│               │   mapper/ constants/ index.ts
│               └── engine/
└── tests/
    ├── unit/
    │   ├── platform/business-calendars/            # calendar-walk algorithm
    │   └── orchestration/{sla,escalation}/          # policy match, breach logic, rule match
    └── integration/                                 # full flow incl. restart-boundary test

Structure Decision: Single project. business-calendars gets a full standard shape (not internal-only) since it needs its own CRUD surface for calendars/holidays, unlike 007's internal-only routing module. sla and escalation each keep the engine/ extension doc 07 §8 reserves for modules with real decision logic, matching 005/007 precedent.

Complexity Tracking

No constitution violations — table intentionally omitted.