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>
18 KiB
description
| description |
|---|
| Task list for 009-problem-resolution |
Tasks: Problem Resolution
Input: Design documents from specs/009-problem-resolution/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/problem-resolution-contract.md, quickstart.md
Tests: Included as first-class tasks. This feature's pure logic is the existence-chain validation (each step's precondition) and the auto-close due-window predicate; the rest is sequential-workflow wiring best proven end-to-end against real Postgres.
Organization: Tasks are grouped by user story (US1 = P1 investigation, US2 = P1 root cause, US3 = P1 solution states, US4 = P2 verification, US5 = P1 resolution/confirmation/auto-close, US6 = P2 reopen).
Format: [ID] [P?] [Story] Description
All file paths are relative to supporthub-api/ (repo root).
Phase 1: Setup
- T001 [P] Populate
src/modules/problem-management/investigation/with the full standard shape (controller/,routes/,schema/,repository/,service/,types/,mapper/,constants/,index.ts), replacing theInvestigationService.getInvestigationStatusstub - T002 [P] Populate
src/modules/problem-management/root-causes/the same way, replacing theRootCausesService.getRootCausestub - T003 [P] Populate
src/modules/problem-management/solutions/the same way, replacing theSolutionsService.getSolutionsstub - T004 [P] Populate
src/modules/problem-management/verification/the same way, replacing theVerificationService.verifySolutionstub - T005 [P] Populate
src/modules/problem-management/resolutions/the same way, replacing theResolutionsService.getResolutionsstub — this module additionally gets the auto-close sweep and the two new customer-facing routes (later tasks) - T006 [P] Add
src/config/problem-resolution.ts(problemResolutionConfig .autoCloseWaitingHours, reading a newRESOLUTION_AUTO_CLOSE_WAITING_HOURSenv var, default72) and register it insrc/config/index.ts's re-export list
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Schema for every entity, shared by every user story.
⚠️ CRITICAL: No user-story stage work can begin until this phase is complete.
- T007 Add
Investigation,RootCause,Solution,SolutionImplementation,SolutionVerification,Resolutionmodels toprisma/schema.prismaper data-model.md, plusProblem.investigations/Problem.rootCauses/Problem.solutionsandTicket.resolutionback-relations (depends on T001-T005) - T008 Run
npm run prisma:generateand create the migration (npm run prisma:migrate) for T007 (depends on T007)
Checkpoint: Schema migrated. User stories can now be built.
Phase 3: User Story 1 - Structured investigation, preserved across attempts (Priority: P1) 🎯 MVP (part 1)
Goal: Investigation CRUD with the version-row-per-attempt guarantee and internal-notes exclusion from customer-facing reads.
Independent Test: Quickstart Scenario 1.
Tests for User Story 1
- T009 [US1] Integration test covering Quickstart Scenario 1 (create, retrieve with every
field intact, customer-safe variant omits
internalNotes, a second investigation preserves the first) against a real Postgres intests/integration/problem-resolution-flow.test.ts(depends on T008)
Implementation for User Story 1
- T010 [US1] Add
InvestigationRepository(create,findAllForProblemordered newest first,findMostRecentForProblem) ininvestigation/repository/(depends on T008) - T011 [US1] Add Zod create schema (
investigator,findings,evidence?,internalNotes?,status?) ininvestigation/schema/ - T012 [US1] Add
InvestigationService.record/listForProblem(agent-facing, includesinternalNotes) andlistForProblemCustomerSafe(stripsinternalNotes, FR-003) ininvestigation/service/(depends on T010, T011) - T013 [US1] Add
POST/GET /admin/problems/:problemId/investigations(gated byfastify.authenticate) andGET /problems/:problemId/investigations(ungated, customer- safe) routes ininvestigation/controller/+routes/, registered fromsrc/api/routes.ts(depends on T012) - T014 [US1] Run Quickstart Scenario 1 locally and confirm all 3 steps pass
Checkpoint: Investigations can be recorded and read correctly, with the customer-safe redaction guarantee in place.
Phase 4: User Story 2 - Root cause requires an investigation on file (Priority: P1) 🎯 MVP (part 2)
Goal: RootCause CRUD gated on an existing investigation, with a validated type enum.
Independent Test: Quickstart Scenario 2.
Tests for User Story 2
- T015 [US2] Integration test covering Quickstart Scenario 2 (rejected with no investigation,
accepted after one exists, rejected with an invalid type) — implemented as the "Scenario 2"
case in
tests/integration/problem-resolution-flow.test.ts(depends on T009, T014) - T016 [P] [US2] Unit test for the type-validation Zod schema (five valid values, everything
else rejected) in
tests/unit/problem-management/root-cause-schema.test.ts
Implementation for User Story 2
- T017 [US2] Add
RootCauseRepository(create,findAllForProblem) inroot-causes/repository/(depends on T008) - T018 [US2] Add Zod create schema (
typeas a 5-value enum,description) inroot-causes/schema/ - T019 [US2] Add
RootCausesService.record: resolve-or-409on the problem having at least one investigation (T010'sfindMostRecentForProblem, viainvestigation's publicindex.ts) — inroot-causes/service/(depends on T012, T017, T018) - T020 [US2] Add
POST /admin/problems/:problemId/root-causesroute (gated byfastify.authenticate) inroot-causes/controller/+routes/, registered fromsrc/api/routes.ts(depends on T019) - T021 [US2] Run Quickstart Scenario 2 locally and confirm all 3 steps pass
Checkpoint: Root causes are correctly gated on investigation existing first.
Phase 5: User Story 3 - Solution proposed, approved, implemented as distinct states (Priority: P1) 🎯 MVP (part 3)
Goal: Solution CRUD gated on root cause existing; approval as an explicit action; implementation gated on approval, one-to-one.
Independent Test: Quickstart Scenario 3.
Tests for User Story 3
- T022 [US3] Integration test covering Quickstart Scenario 3 (rejected with no root cause,
created with
approved: false, implementation rejected before approval, accepted after, a second implementation rejected) — "Scenario 3" case intests/integration/problem-resolution-flow.test.ts(depends on T015, T021)
Implementation for User Story 3
- T023 [US3] Add
SolutionRepository(create,findById,approve,findMostRecentForProblem) andSolutionImplementationRepository(create,findBySolutionId) insolutions/repository/(depends on T008) - T024 [US3] Add Zod schemas (
proposed; implementation'snotes?,implementedBy) insolutions/schema/ - T025 [US3] Add
SolutionsService.propose: resolve-or-409on the problem having at least one root cause (T017's repository, viaroot-causes's publicindex.ts) —approve—recordImplementation: resolve-or-409onapproved: trueand no existing implementation — insolutions/service/(depends on T019, T023, T024) - T026 [US3] Add
POST /admin/problems/:problemId/solutions,PATCH /admin/solutions/:solutionId/approve,POST /admin/solutions/:solutionId/implementationroutes (gated byfastify.authenticate) insolutions/controller/+routes/, registered fromsrc/api/routes.ts(depends on T025) - T027 [US3] Run Quickstart Scenario 3 locally and confirm all 5 steps pass
Checkpoint: All three P1 record-keeping user stories are complete — the full investigation through implementation chain is enforced and correct. This is the feature's structural MVP.
Phase 6: User Story 4 - Verification, and failure re-investigates or escalates (Priority: P2)
Goal: SolutionVerification CRUD gated on implementation existing, one-to-one; a failed
verification supports either a fresh investigation or the existing HUMAN_ESCALATION transition.
Independent Test: Quickstart Scenario 4.
Tests for User Story 4
- T028 [US4] Integration test covering Quickstart Scenario 4 (successful verification;
failed verification recorded but unusable for resolution; failure + reinvestigate creates a
fresh Investigation row; failure + escalate transitions the ticket to
HUMAN_ESCALATIONand 007 auto-assigns) — "Scenario 4" case intests/integration/problem-resolution-flow.test.ts(depends on T022)
Implementation for User Story 4
- T029 [US4] Add
SolutionVerificationRepository(create,findBySolutionId) inverification/repository/(depends on T008) - T030 [US4] Add Zod schema (
methodas a 4-value enum,result,evidence?) inverification/schema/ - T031 [US4] Add
VerificationService.record: resolve-or-409on the solution having an implementation (T023's repository) and no existing verification — inverification/ service/(depends on T023, T029, T030) - T032 [US4] Add
POST /admin/solutions/:solutionId/verificationroute (gated byfastify.authenticate) inverification/controller/+routes/, registered fromsrc/api/routes.ts(depends on T031) - T033 [US4] Run Quickstart Scenario 4 locally and confirm all 4 steps pass (steps 3-4 call
T012's
InvestigationService.recordandticketsService.updateStatusdirectly — no new production code beyond what US1/003/007 already provide, per research.md's decision to reuse the existing transition rather than add new escalation machinery)
Checkpoint: Verification is correctly gated and its failure path reuses existing mechanisms rather than inventing new ones.
Phase 7: User Story 5 - Resolution, customer confirmation, and durable auto-close (Priority: P1)
Goal: Resolution gated on a successful verification; explicit customer confirmation via 002's trust boundary; a durable, directly-callable auto-close sweep as the fallback.
Independent Test: Quickstart Scenario 5.
Tests for User Story 5
- T034 [P] [US5] Unit test for the auto-close due-window predicate (a pending ticket older
than the configured waiting period is due; a pending ticket younger than it is not; a
non-pending ticket is never selected) in
tests/unit/problem-management/auto-close-sweep.test.ts - T035 [US5] Integration test covering Quickstart Scenario 5 (resolution rejected without a
successful verification; accepted after, ticket reaches
RESOLUTION_PENDING_CUSTOMER; customer confirmation via the trust-boundary route reachesRESOLVED; a second ticket aged past the configured window reachesRESOLVEDvia a direct call to the sweep) — "Scenario 5" case intests/integration/problem-resolution-flow.test.ts(depends on T028)
Implementation for User Story 5
- T036 [US5] Add
ResolutionRepository(create,findByTicketId) inresolutions/repository/(depends on T008) - T037 [US5] Add Zod schema (
outcome,resolvedBy) inresolutions/schema/ - T038 [US5] Add
ResolutionsService.record(ticketId, outcome, resolvedBy): resolves the ticket'sproblemId, resolve-or-409on aSolutionwithverification.result: 'success'existing for it (T023/T029's repositories), creates theResolution, and transitions the ticket toRESOLUTION_PENDING_CUSTOMERviaticketsService.updateStatus— inresolutions/service/resolutions.service.ts(depends on T023, T029, T036, T037) - T039 [US5] Add
ResolutionsService.confirmByCustomer(ticketId)/runAutoCloseSweep(): the former transitionsRESOLUTION_PENDING_CUSTOMER → RESOLVEDdirectly; the latter queries everyRESOLUTION_PENDING_CUSTOMERticket whoseupdatedAtis older thanproblemResolutionConfig.autoCloseWaitingHoursand transitions each the same way — a single, directly-callable, side-effect-only method (research.md — no worker process needed to invoke it in tests) — inresolutions/service/resolutions.service.ts(depends on T006, T038) - T040 [US5] Add
POST /admin/tickets/:ticketId/resolution(gated byfastify.authenticate) andPOST /v1/support/tickets/:ticketId/confirm-resolution(gated byfastify.authenticateProductIntegration+fastify.checkIntegrationRateLimit, verifying the token's tenant/user matches the ticket's own — research.md) routes inresolutions/controller/+routes/, registered fromsrc/api/routes.ts(depends on T038, T039) - T041 [US5] Replace
registerCleanupWorker()'s stub body insrc/jobs/cleanup/index.ts: schedule a repeatable job (every 5 minutes) onQueueName.CLEANUPwhose processor calls T039'srunAutoCloseSweep— and register it fromsrc/bootstrap/queue.bootstrap.ts(depends on T039) - T042 [US5] Run Quickstart Scenario 5 locally and confirm all 4 steps pass
Checkpoint: Every P1 user story is complete. The full investigation-to-resolution chain
works, gated correctly at every step, with both an explicit and a durable-fallback path to
RESOLVED. This is the feature's MVP.
Phase 8: User Story 6 - Reopen (Priority: P2)
Goal: A resolved or closed ticket can be reopened by the customer or an agent, re-entering
IN_PROGRESS through two real, audited transitions, touching nothing else.
Independent Test: Quickstart Scenario 6.
Tests for User Story 6
- T043 [US6] Integration test covering Quickstart Scenario 6 (customer reopen reaches
IN_PROGRESSviaREOPENED; the priorResolutionand anySLARunare unchanged; agent reopen of aCLOSEDticket produces the same result attributed to the agent) — "Scenario 6" case intests/integration/problem-resolution-flow.test.ts(depends on T035)
Implementation for User Story 6
- T044 [US6] Add
TicketsService.reopen(ticketId, actor)(007/003's existingticketing/ticketsmodule): resolve-or-409if status isn'tRESOLVED/CLOSED, then two sequentialupdateStatuscalls (REOPENED, thenIN_PROGRESS) — inticketing/tickets/ service/tickets.service.ts(depends on T008 — no new schema, reuses 003's own state machine and repository) - T045 [US6] Add
POST /v1/support/tickets/:ticketId/reopen(customer, trust boundary) andPOST /admin/tickets/:ticketId/reopen(agent,fastify.authenticate) routes inticketing/tickets/controller/+routes/(depends on T044) - T046 [US6] Run Quickstart Scenario 6 locally and confirm all 4 steps pass
Checkpoint: All six user stories work independently and together — the full doc 04 workflow, from first investigation through resolution, confirmation, auto-close, and reopen.
Phase 9: Polish & Cross-Cutting Concerns
- T047 [P] Update
specs/009-problem-resolution/checklists/requirements.mdNotes with any implementation-time findings - T048 Run
npx tsx scripts/check-architecture.tsandnpm run lint/npm run typecheck - T049 Full regression:
npm run test:unit(scoped totests/unit) to confirm nothing broke elsewhere, then the full integration suite (including 003's and 007's own suites, since T044 modifiesticketing/tickets) against real Docker-provisioned Postgres/Redis
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies
- Foundational (Phase 2): Depends on Setup — BLOCKS all user stories
- User Story 1 (Phase 3): Depends on Foundational — no dependency on US2-US6
- User Story 2 (Phase 4): Depends on US1 (the investigation it's gated on)
- User Story 3 (Phase 5): Depends on US2 (the root cause it's gated on)
- User Story 4 (Phase 6): Depends on US3 (the implementation it's gated on)
- User Story 5 (Phase 7): Depends on US4 (the successful verification it's gated on)
- User Story 6 (Phase 8): Depends on US5 (a ticket has to reach
RESOLVED/CLOSEDbefore reopening it means anything) - Polish (Phase 9): Depends on all six user stories
This feature's user stories are more strictly sequential than 007's or 008's — doc 04's own workflow is a straight chain (investigation → root cause → solution → verification → resolution → reopen), not a set of independently orderable capabilities, so each phase's dependency here is real, not just priority-driven sequencing.
Parallel Opportunities
- T001-T006 (independent scaffolding)
- T016 (unit test) alongside T017-T018 (the schema it tests)
- T034 (unit test) alongside T039 (the sweep it tests)
- T047 in Polish
Implementation Strategy
MVP First (User Stories 1-3, then 5)
- Setup + Foundational (T001-T008)
- User Story 1 (T009-T014) → investigations recorded and readable
- User Story 2 (T015-T021) → root causes correctly gated
- User Story 3 (T022-T027) → solutions proposed/approved/implemented correctly
- User Story 4 is P2 — skippable for a first MVP cut if verification's own gating isn't needed yet, but User Story 5 (Resolution) depends on it structurally (a successful verification is Resolution's own precondition), so in practice build order is 1→2→3→4→5 regardless of priority label — same "dependency order isn't always priority order" note 006 and 007's own tasks.md already made.
- User Story 5 (T034-T042) → resolution, confirmation, and auto-close all work
- STOP and VALIDATE: Quickstart Scenarios 1-5 pass.
Incremental Delivery
- Setup + Foundational → schema migrated
- Add User Story 1 → investigations exist
- Add User Story 2 → root causes correctly gated
- Add User Story 3 → solutions move through real states
- Add User Story 4 → verification gated, failure path reuses existing mechanisms
- Add User Story 5 → resolution + confirmation + auto-close (P1-complete, MVP)
- Add User Story 6 → reopen, closing the loop 008 left open
- Polish → full regression