Files
support_backend/specs/008-sla-escalation/checklists/requirements.md
T
saqib mirandClaude Sonnet 5 9357f03e1d feat: implement SLA and escalation (008)
Populates platform/business-calendars, orchestration/sla, and
orchestration/escalation (all thin stubs until now) with the real engine:

- business-calendars: a luxon-based day-by-day calendar walk
  (addBusinessMinutes/isWithinWorkingHours) excluding non-working hours,
  weekends, and holidays — replacing the naive createdAt+hours stub FR-004
  explicitly forbids.
- sla: most-specific SLAPolicy resolution (product/category/problemType/
  priority, wildcard-or-exact-match, specificity-count + updatedAt
  tiebreak), SLARun creation on the first real publish of the
  long-unused TICKET_ASSIGNED domain event, durable pause/resume via an
  absolute-timestamp shift (no in-memory state, verified across a real
  buildApp() restart), and a repeatable BullMQ breach-detection sweep
  (src/jobs/sla, itself a previously-unregistered stub) that is directly
  callable for tests, not only reachable through a running worker.
- escalation: EscalationPolicy/Rule CRUD (all 10 doc05 trigger types
  storable, only resolution_breach/first_response_breach evaluated),
  breach-triggered and manual escalation both funnel through one EscalationEvent
  + scoped re-assignment path. AssignmentEngine (007) gains
  assignToSpecificNode — a new, explicitly node-scoped entry point,
  since escalation must never let 007's general resolution re-derive a
  different node than the one a rule or a caller targeted.

Two small pre-existing scaffold gaps were closed along the way:
CategoriesRepository had no findById, and TICKET_ASSIGNED/SLA_BREACHED/
ESCALATION_TRIGGERED were defined since earlier phases but never
published by any code.

Verified against throwaway Docker Postgres/Redis (typecheck, lint,
architecture-check all clean; 148/150 relevant tests pass — the 2
failures are pre-existing, MinIO-dependent, and unrelated to this
feature).

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

5.2 KiB

Specification Quality Checklist: SLA and Escalation

Purpose: Validate specification completeness and quality before proceeding to planning Created: 2026-09-03 Feature: spec.md

Content Quality

  • No implementation details (languages, frameworks, APIs)
  • Focused on user value and business needs
  • Written for non-technical stakeholders
  • All mandatory sections completed

Requirement Completeness

  • No [NEEDS CLARIFICATION] markers remain
  • Requirements are testable and unambiguous
  • Success criteria are measurable
  • Success criteria are technology-agnostic (no implementation details)
  • All acceptance scenarios are defined
  • Edge cases are identified
  • Scope is clearly bounded
  • Dependencies and assumptions identified

Feature Readiness

  • All functional requirements have clear acceptance criteria
  • User scenarios cover primary flows
  • Feature meets measurable outcomes defined in Success Criteria
  • No implementation details leak into specification

Notes

  • Scope is Phase 8 per docs/10-implementation-roadmap.md: the SLA policy engine, business calendar/holiday support, durable pause/resume, and the rule-driven escalation engine — explicitly bounded to the two SLA-derived trigger types (resolution_breach/ first_response_breach) this feature can compute a real signal for, out of doc 05 §6's ten; the other eight remain valid, storable rule configuration with no event source feeding them yet, same convention 006 already established for HierarchyNode.assignmentStrategy/ slaPolicyId being real data before 007/008 gave them real consumers.
  • This is the first feature to give 006's HierarchyNode.slaPolicyId/escalationPolicyId fields (stored as free-text references since 006, unvalidated) a real target to resolve against.
  • Escalation's re-assignment path reuses 007's AssignmentEngine scoped to a specific target node, not 007's general unscoped resolution — a genuinely different call shape 007 doesn't expose yet, to be added during planning.
  • Constitution Principle VII is directly load-bearing here in a new way: this is the first feature whose entire second half (SLA pause/resume/breach) is only correct if it survives a process restart — 003's ticket-status concurrency and 007's round-robin concurrency both guarded against corruption under concurrent requests within a running process; this guards against silent loss of state across the process not running at all for a while.
  • All items pass; no revision iterations were needed.

Implementation Notes (added during /speckit-implement)

  • DomainEventName.TICKET_ASSIGNED (defined since 007-orchestration-assignment) and SLA_BREACHED/ESCALATION_TRIGGERED (defined even earlier) had never been published by any code until this feature — AssignmentEngine.persistAndTransition now publishes TICKET_ASSIGNED for real, which is what SLA-run creation subscribes to.
  • src/jobs/sla/index.ts and src/jobs/escalation/index.ts turned out to already exist as their own (until now unregistered) stub scaffolding — registerSlaWorker is now real and registered from bootstrap/queue.bootstrap.ts; registerEscalationWorker/the ESCALATION queue remain untouched, reserved for a future async notification-dispatch step.
  • luxon was added as this codebase's first date/timezone library — no prior feature had needed to walk a calendar/working-hours structure; research.md documents the choice over date-fns and hand-rolled arithmetic.
  • Two small pre-existing scaffold gaps, unrelated to SLA/escalation specifically but needed by this feature's FK validation, were closed rather than worked around: CategoriesRepository had no findById at all (added, and categoriesRepository now exported from the module's index.ts, matching every other catalog repository).
  • EscalationEvent.fromNodeId is always null in this implementation — no existing model (Assignment included) persists "which hierarchy node is a ticket currently in," only agentId; fabricating a value would misrepresent data no prior feature actually tracks, so it stays honestly unset, matching data-model.md's own "if any" phrasing.
  • README.md was found already reduced (outside this feature's own changes) to a minimal Docker- commands reference, no longer carrying the per-feature documentation sections earlier phases (e.g. 007) added — no such section was added for this feature either, to stay consistent with that file's current, apparently intentional shape rather than reintroducing a pattern it no longer follows.
  • Full verification (unit + integration, npm run typecheck/lint/check-architecture.ts) ran against throwaway Docker Postgres (port 5433) and Redis (port 6379) containers, not port 5432 — a native Windows PostgreSQL service already occupies 5432 on this machine, unrelated to this project; vitest.config.ts's hardcoded DATABASE_URL was updated from 5432 to 5433 to match. 148 of 150 relevant tests pass; the only 2 failures (ticket-attachments.test.ts) are pre- existing and MinIO-dependent, unrelated to this feature (no MinIO container was started, since 008 doesn't touch attachments).