/speckit-plan output for 003-ticketing: technical context and constitution gate check (all PASS), Phase 0 research (9 decisions: ticket code format, explicit 12-state lifecycle transition table, optimistic concurrency via version column, idempotency-key upsert reusing 002's CustomerReference pattern, explicit-reference-only recurring-problem linking, config-driven message visibility mapping, presigned-PUT attachment pipeline, a fail-closed placeholder malware scanner since none exists in this stack, and adding MinIO to Docker Compose for local/test S3-compatible storage), Phase 1 data model (Ticket/Problem/TicketMessage/TicketAttachment plus the inbound request -> ticket creation behavior), the lifecycle/messages/ attachments contract, and a 6-scenario quickstart. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
57 lines
2.8 KiB
Markdown
57 lines
2.8 KiB
Markdown
# Quickstart: Validating Ticket Creation, Messages & Attachments
|
|
|
|
Prerequisites: 002-saas-integration's inbound trust boundary working (a seeded/registered
|
|
`ProductIntegration`), MinIO running locally (research.md), migrations applied.
|
|
|
|
## Scenario 1 — a trusted request creates a ticket immediately (User Story 1)
|
|
|
|
1. Send a valid inbound request through `POST /v1/support/requests`.
|
|
2. **Expected**: `202` with a `ticketId`/`code`/`status: NEW`; the `Ticket` and its `Problem`
|
|
exist in the database immediately — no polling needed.
|
|
|
|
## Scenario 2 — idempotency key prevents duplicate tickets (User Story 1)
|
|
|
|
1. Send the same request twice with the same `idempotencyKey`.
|
|
2. **Expected**: both responses reference the *same* `ticketId`; only one `Ticket` row exists.
|
|
|
|
## Scenario 3 — recurring problem links to the existing Problem (User Story 1)
|
|
|
|
1. Create a ticket, note its `problemId`.
|
|
2. Send a second, distinct request whose `referenceIds` includes that same problem's reference.
|
|
3. **Expected**: the new ticket has the *same* `problemId` as the first — no second `Problem`
|
|
created.
|
|
|
|
## Scenario 4 — internal notes never leak to a customer-scoped read (User Story 2)
|
|
|
|
1. Post one message of each type (`CUSTOMER_MESSAGE`, `AI_MESSAGE`, `AGENT_MESSAGE`,
|
|
`INTERNAL_NOTE`, `SYSTEM_EVENT`, `INVESTIGATION_NOTE`, `SOLUTION_NOTE`) to a ticket.
|
|
2. Read the ticket's messages through a customer-scoped call.
|
|
3. **Expected**: only `CUSTOMER_MESSAGE`, `AI_MESSAGE`, `AGENT_MESSAGE`, `SYSTEM_EVENT` appear —
|
|
the other three are absent entirely, not present-but-flagged.
|
|
4. Read the same ticket's messages through an agent-scoped call.
|
|
5. **Expected**: all seven messages appear.
|
|
|
|
## Scenario 5 — an attachment is unusable until it clears scanning (User Story 3)
|
|
|
|
1. Request an upload URL, PUT a file, confirm the upload.
|
|
2. Immediately request a download URL.
|
|
3. **Expected**: `409` — `scanStatus` is `pending`.
|
|
4. Wait for the scan job to run (with the placeholder scanner, research.md — it fails closed to
|
|
`infected`).
|
|
5. Request a download URL again.
|
|
6. **Expected**: still refused — `scanStatus: infected` — confirming the pipeline correctly gates
|
|
on a real (even if placeholder) scan result rather than defaulting to available.
|
|
|
|
## Scenario 6 — a stale ticket-status update is rejected, not overwritten (Edge Cases)
|
|
|
|
1. Read a ticket's current `status`/`version`.
|
|
2. In two separate calls, attempt two different valid status transitions using the *same*
|
|
`expectedVersion`.
|
|
3. **Expected**: exactly one succeeds; the other receives `409 CONFLICT` and must re-read the
|
|
ticket to retry.
|
|
|
|
## What "done" looks like
|
|
|
|
All six scenarios pass, and together they demonstrate every functional requirement and success
|
|
criterion in `spec.md` without needing to read the implementation to know what "correct" means.
|