Phase 0 research resolves module placement (problem-management/problems confirmed dead/unwired, left untouched), Investigation's version-row-per- attempt shape, the strict investigation->root-cause->solution-> implementation->verification existence chain, why Resolution has no solutionId FK (matches doc06 exactly), why verification-failure escalation reuses 003/007's plain HUMAN_ESCALATION transition instead of adding an eleventh trigger type to 008's already-shipped escalation rules, the customer-facing route design (reusing 002's inbound trust boundary rather than fastify.authenticate), and the auto-close sweep design (the already-defined-but-unused CLEANUP queue, mirroring 008's breach-detection job). Phase 1 adds data-model.md, the admin/customer-facing contract, and six quickstart scenarios covering the full sequential workflow through customer confirmation, auto-close, and reopen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
8.8 KiB
Implementation Plan: Problem Resolution
Branch: 009-problem-resolution | Date: 2026-09-03 | Spec: spec.md
Input: Feature specification from specs/009-problem-resolution/spec.md
Summary
Populate the five real problem-management/{investigation,root-causes,solutions,resolutions, verification} stubs (each currently a one-file placeholder — getInvestigationStatus always
PENDING, getResolutions always [], etc.) with the real doc-04-workflow engine: a strict
existence chain from investigation through root cause, solution, implementation, and
verification; a Resolution record gated on a successfully verified solution, moving the ticket
to RESOLUTION_PENDING_CUSTOMER; explicit customer confirmation (reusing 002's inbound trust
boundary) or a durable auto-close sweep (reusing the unregistered CLEANUP queue stub) into
RESOLVED; and a reopen path (customer or agent) that re-enters IN_PROGRESS through 003's
existing REOPENED state without touching any prior record or 008's SLARun.
Technical Context
Language/Version: TypeScript 5.4 / Node.js 20+.
Primary Dependencies: Prisma (new models), Zod, BullMQ (reused CLEANUP queue). No new
runtime dependency.
Storage: PostgreSQL via Prisma (new Investigation, RootCause, Solution,
SolutionImplementation, SolutionVerification, Resolution models). Reuses
src/infrastructure/queue for the auto-close sweep, same as 008's breach-detection job.
Testing: Vitest — unit tests for the existence-chain validation logic and the auto-close
due-window predicate; integration tests for the full sequential workflow (investigation through
resolution), the customer-confirmation and auto-close paths, and reopen leaving prior records and
an SLARun untouched.
Target Platform: Same Fastify modular monolith. Populates
src/modules/problem-management/{investigation,root-causes,solutions,resolutions,verification}/.
Adds two new customer-facing routes under /v1/support/tickets/:ticketId/... alongside the
existing POST /v1/support/requests (002).
Project Type: Backend service — single project.
Constraints: MUST reject out-of-order writes (root cause before investigation, etc. — FR-006/
FR-008/FR-009); MUST NOT expose internalNotes on any customer-facing read (FR-003); MUST gate
Resolution on a real successful verification (FR-014); MUST auto-close durably, not via an
in-memory timer (FR-016, Constitution Principle VII); MUST NOT create a new SLARun on reopen
(FR-018).
Scale/Scope: Five populated modules, one new BullMQ repeatable job (reusing an existing queue), two new customer-facing routes reusing 002's trust boundary, one new agent-facing reopen route. Explicitly excludes: a rendered customer confirmation UI (010's territory), a new escalation-rule trigger type for verification failure (reuses 003/007's existing transition instead), per-scope auto-close policy (one system-wide config value).
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 | Customer-facing routes authenticate via 002's product-integration token, never a SupportHub-native customer login — and additionally verify the token's tenant/user matches the ticket's own recorded values. | PASS |
| II. Configuration Over Hardcoding | The auto-close waiting period is env-configured (research.md), never a hardcoded number; validated-value sets (root-cause type, verification method) are Zod-enforced closed lists matching doc 04's own documented values, not ad hoc. | PASS |
| III. Layered Architecture With Enforced Module Boundaries | Five modules follow the standard shape; each references ticketing/tickets's Problem (one-directional, already established), and the verification-failure-escalation path calls ticketsService.updateStatus directly rather than reaching into 008's EscalationService — no new module dependency edge into 008 at all. |
PASS |
| IV. AI Recommends, Deterministic Policy Decides | Resolution.resolvedBy accepts "ai" per doc 06's own shape, but this feature adds no AI-driven decision logic of its own — every gate (approval, verification result, escalate-vs-reinvestigate) is an explicit human/deterministic action. |
PASS |
| V. Evidence-Based Verification | This principle's own domain — SolutionVerification.evidence/Investigation.evidence are exactly the durable evidence records Principle V requires before a resolution is trusted. |
PASS |
| VI. Durable Audit & History | Every investigation attempt is its own preserved row (never overwritten); reopen produces two real, separately-audited status transitions rather than one collapsed hop. | PASS |
| VII. Concurrency-Safe, Durable Job Handling | Auto-close is a repeatable BullMQ job querying durable DB state (Ticket.status/updatedAt), never an in-memory timer — same discipline 008's breach-detection sweep already established. |
PASS |
| VIII. Problem and Ticket Are Separate, Related Entities | This principle's own domain — every investigation/root-cause/solution record is scoped to Problem, never Ticket, while Resolution (necessarily ticket-scoped, since a shared Problem could span multiple tickets) is the one exception doc 06 itself defines. |
PASS |
| Technology & Platform Constraints | Prisma + Zod + existing BullMQ infrastructure only, no new dependency. | 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 VIII
explicitly: Resolution.ticketId (not problemId) is the one place in this whole feature where
a record is ticket-scoped rather than problem-scoped — a deliberate, doc-06-defined exception
(a shared Problem can have multiple tickets, each needing its own outcome), not an
inconsistency with the rest of this feature's problem-scoped chain.
Project Structure
Documentation (this feature)
specs/009-problem-resolution/
├── 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/
├── prisma/
│ └── schema.prisma # MODIFIED — add Investigation, RootCause,
│ Solution, SolutionImplementation,
│ SolutionVerification, Resolution
├── src/
│ ├── config/
│ │ └── problem-resolution.ts # NEW — autoCloseWaitingHours
│ ├── jobs/
│ │ └── cleanup/index.ts # REPLACED stub — schedules the repeatable
│ │ auto-close sweep (research.md)
│ └── modules/
│ ├── ticketing/tickets/ # MODIFIED — reopen calls updateStatus twice
│ └── problem-management/
│ ├── problems/ # UNTOUCHED — dead duplicate scaffold
│ │ (research.md) — not this feature's Problem
│ ├── investigation/ # REPLACED stub — full standard shape
│ ├── root-causes/ # REPLACED stub — full standard shape
│ ├── solutions/ # REPLACED stub — full standard shape
│ ├── verification/ # REPLACED stub — full standard shape
│ └── resolutions/ # REPLACED stub — full standard shape,
│ including the auto-close sweep + the two
│ new customer-facing routes
└── tests/
├── unit/problem-management/ # existence-chain validation, auto-close
│ due-window predicate
└── integration/ # full sequential workflow, customer
confirmation, auto-close, reopen
Structure Decision: Single project. Every module gets the full standard shape (each has its
own real CRUD/read surface, unlike 007's internal-only routing) — matching 008's precedent for
a multi-module feature where every module has genuine callers beyond another module in the same
feature.
Complexity Tracking
No constitution violations — table intentionally omitted.