# Contract: Problem Resolution Agent-facing write routes are gated by `fastify.authenticate` (known limitation inherited from 002-008). Customer-facing routes are gated by `fastify.authenticateProductIntegration` + `fastify.checkIntegrationRateLimit` (002's inbound trust boundary, research.md) and additionally verify the caller's token identifies the same tenant/user as the ticket's own recorded `externalTenantId`/`externalUserId` — a `403` if they don't match. ## Investigation - `POST /admin/problems/:problemId/investigations` — body `{ investigator, findings, evidence?, internalNotes?, status? }` (`status` defaults to `open`). `404` if `problemId` doesn't exist. - `GET /admin/problems/:problemId/investigations` — every investigation for the problem, newest first, including `internalNotes` (agent-facing). - `GET /problems/:problemId/investigations` — customer/public-safe variant: same list, with `internalNotes` always omitted (FR-003). ## Root Cause - `POST /admin/problems/:problemId/root-causes` — body `{ type, description }`. `400` if `type` isn't one of the five validated values. `409` if no investigation exists yet for the problem. ## Solution - `POST /admin/problems/:problemId/solutions` — body `{ proposed }`. `409` if no root cause exists yet for the problem. - `PATCH /admin/solutions/:solutionId/approve` — sets `approved: true`. - `POST /admin/solutions/:solutionId/implementation` — body `{ notes?, implementedBy }`. `409` if the solution isn't approved, or already has an implementation. - `POST /admin/solutions/:solutionId/verification` — body `{ method, result, evidence? }`. `400` if `method` isn't one of the four validated values. `409` if the solution has no implementation yet, or already has a verification. ## Resolution - `POST /admin/tickets/:ticketId/resolution` — body `{ outcome, resolvedBy }`. `409` if the ticket's problem has no solution with a successful verification. Transitions the ticket to `RESOLUTION_PENDING_CUSTOMER` on success. - `POST /v1/support/tickets/:ticketId/confirm-resolution` — customer-facing (trust boundary above). `409` if the ticket isn't in `RESOLUTION_PENDING_CUSTOMER`. Transitions to `RESOLVED`. ## Reopen - `POST /v1/support/tickets/:ticketId/reopen` — customer-facing. `409` if the ticket isn't `RESOLVED` or `CLOSED`. - `POST /admin/tickets/:ticketId/reopen` — agent-facing, same precondition. Both reopen routes transition `RESOLVED|CLOSED → REOPENED → IN_PROGRESS` (research.md's two-hop decision) and touch nothing else — no new `SLARun`, no mutation of any prior investigation/root- cause/solution/verification/resolution record (FR-018, SC-005). ## Guarantees (callable contract) 1. **Every investigation/root-cause/solution/implementation/verification/resolution record, once created, is retrievable exactly as given and is never silently overwritten by a later action in the same problem's lifecycle** (SC-001). 2. **`internalNotes` never appears in a customer-facing investigation read**, verified by a direct comparison against the agent-facing read of the same record (SC-002). 3. **A `Resolution` can never be recorded without a successfully verified solution already on file for the ticket's problem** (SC-003). 4. **A ticket in `RESOLUTION_PENDING_CUSTOMER` with no explicit confirmation reaches `RESOLVED` within one auto-close job cycle of its configured waiting period elapsing** (SC-004). 5. **Reopening a ticket leaves every prior problem-resolution record and its `SLARun` (008) untouched** (SC-005). 6. **A verification failure choosing escalation moves the ticket to `HUMAN_ESCALATION` through 003's existing state machine, and 007's orchestration re-runs automatically from that transition alone** — no new escalation mechanism is introduced by this feature.