Populates the five real problem-management stubs (investigation,
root-causes, solutions, verification, resolutions -- problems is
confirmed dead/unwired scaffold and stays untouched) with doc04's
sequential workflow engine:
- investigation: version-row-per-attempt (never overwritten), with a
customer-safe read path that always strips internalNotes.
- root-causes/solutions/verification: a strict existence chain
(investigation -> root cause -> solution -> approval -> implementation
-> verification), each step resolve-or-409 on its own precondition,
matching doc06's schema field-for-field with no invented columns.
- resolutions: gated on a successfully verified solution (no stored
solutionId FK, per doc06 -- resolved via a join at write time), moving
the ticket to RESOLUTION_PENDING_CUSTOMER; explicit customer
confirmation and a durable auto-close sweep (the previously-unregistered
CLEANUP queue stub, mirroring 008's breach-detection job) both resolve
it from there.
- reopen (ticketing/tickets): two real, separately-audited transitions
(RESOLVED|CLOSED -> REOPENED -> IN_PROGRESS), touching no prior
problem-resolution record and no SLARun -- closes the loop 008's own
spec.md left open.
Verification-failure escalation reuses 003/007's existing
HUMAN_ESCALATION transition directly rather than adding an eleventh
trigger type to 008's already-shipped escalation rules.
Customer-facing confirm-resolution/reopen needed a body-shape variant of
002's inbound trust boundary that didn't previously exist:
fastify.authenticateProductIntegration hard-required a full
ticket-creation-shaped body. Extracted the shared token/scope/replay
verification into verifyIntegrationIdentity and added a narrower
authenticateProductIntegrationIdentity decorator + identityOnlyRequestSchema
on top of it -- purely additive, ticket creation's own behavior is
unchanged.
Also fixes a real test-data-hygiene bug surfaced by running this
feature's suite alongside 008's: a wildcard-scoped HierarchyNode and an
intentionally-global SLAPolicy in 008's own test fixtures were silently
affecting other test files' tickets sharing the same live Postgres.
Verified against throwaway Docker Postgres/Redis: typecheck, lint,
architecture-check all clean; full regression (tests/unit +
tests/integration together, 172 tests) passes except the 2 pre-existing
MinIO-dependent attachment failures, unrelated to this feature.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>