Skip to content

Commit fb3b515

Browse files
author
aligneddev
committed
SPEC-PLAN: Feature 007 - Allow deletion of rides (plan complete via speckit.plan)
- plan.md: Implementation architecture, constitution checks pass - research.md: 7 key technical decisions (event sourcing, idempotency, UX, projections, auth, state, errors) - data-model.md: RideDeleted event, Ride aggregate transitions, read projections, API schema - quickstart.md: Testing workflow, validation commands, acceptance criteria, debugging guide - contracts/ride-delete-api.yaml: OpenAPI 3.0 DELETE endpoint spec - contracts/ride-deleted-event.schema.json: Event schema Next: speckit.tasks to generate actionable task list
1 parent f60ef1d commit fb3b515

8 files changed

Lines changed: 1328 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Specification Quality Checklist: Allow Deletion of Rides
2+
3+
**Purpose**: Validate specification completeness and quality before proceeding to planning
4+
**Created**: 2026-03-30
5+
**Feature**: [spec.md](../spec.md)
6+
7+
## Content Quality
8+
9+
- [x] No implementation details (languages, frameworks, APIs)
10+
- [x] Focused on user value and business needs
11+
- [x] Written for non-technical stakeholders
12+
- [x] All mandatory sections completed
13+
14+
## Requirement Completeness
15+
16+
- [x] No [NEEDS CLARIFICATION] markers remain
17+
- [x] Requirements are testable and unambiguous
18+
- [x] Success criteria are measurable
19+
- [x] Success criteria are technology-agnostic (no implementation details)
20+
- [x] All acceptance scenarios are defined
21+
- [x] Edge cases are identified
22+
- [x] Scope is clearly bounded
23+
- [x] Dependencies and assumptions identified
24+
25+
## Feature Readiness
26+
27+
- [x] All functional requirements have clear acceptance criteria
28+
- [x] User scenarios cover primary flows
29+
- [x] Feature meets measurable outcomes defined in Success Criteria
30+
- [x] No implementation details leak into specification
31+
32+
## Validation Notes
33+
34+
- **Content Quality**: All sections properly filled with concrete details. No implementation specifics mentioned (e.g., no API endpoints, database technologies, or UI frameworks specified).
35+
- **Requirements**: 13 functional requirements clearly stated with MUST language. All are testable without knowledge of technical implementation.
36+
- **Success Criteria**: 5 measurable outcomes with specific metrics (95% usability, 100% persistence, 35% support reduction). Technology-agnostic and user-focused.
37+
- **Edge Cases**: 7 edge cases identified covering authorization, empty state, concurrent requests, filtering, errors, offline scenarios, and persistence.
38+
- **Dependencies**: Assumptions clearly state that history table, authentication, and event sourcing already exist.
39+
40+
## Checklist Status
41+
42+
**READY FOR PLANNING** - All quality items pass. Specification is complete and ready for `/speckit.plan`.
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
openapi: 3.0.0
2+
info:
3+
title: Bike Tracking - Ride Delete API
4+
version: 1.0.0
5+
description: Delete endpoint for removing rides from a user's history
6+
contact:
7+
name: Bike Tracking Team
8+
license:
9+
name: MIT
10+
11+
servers:
12+
- url: http://localhost:5000/api
13+
description: Local development (Aspire)
14+
- url: https://api.biketracking.local/api
15+
description: User machine (deployed)
16+
17+
paths:
18+
/rides/{rideId}:
19+
delete:
20+
summary: Delete a ride
21+
description: |
22+
Delete a ride from the authenticated user's history. Returns 200 OK even if the ride
23+
is already deleted (idempotent). Requires valid authentication token.
24+
operationId: deleteRide
25+
tags:
26+
- Rides
27+
parameters:
28+
- name: rideId
29+
in: path
30+
required: true
31+
description: UUID of the ride to delete (e.g., 550e8400-e29b-41d4-a716-446655440000)
32+
schema:
33+
type: string
34+
format: uuid
35+
security:
36+
- BearerAuth: []
37+
responses:
38+
'200':
39+
description: Ride deleted successfully or already deleted (idempotent)
40+
content:
41+
application/json:
42+
schema:
43+
$ref: '#/components/schemas/DeleteRideSuccessResponse'
44+
examples:
45+
success:
46+
summary: Successful deletion
47+
value:
48+
rideId: 550e8400-e29b-41d4-a716-446655440000
49+
deletedAt: "2026-03-30T14:22:15Z"
50+
message: Ride deleted successfully.
51+
isIdempotent: false
52+
idempotent:
53+
summary: Already deleted (idempotent)
54+
value:
55+
rideId: 550e8400-e29b-41d4-a716-446655440000
56+
deletedAt: "2026-03-30T13:15:00Z"
57+
message: Ride was already deleted.
58+
isIdempotent: true
59+
'400':
60+
description: Invalid request format
61+
content:
62+
application/json:
63+
schema:
64+
$ref: '#/components/schemas/ErrorResponse'
65+
examples:
66+
badUuid:
67+
summary: Malformed UUID
68+
value:
69+
error: INVALID_RIDE_ID
70+
message: Invalid ride ID format. Must be a valid UUID.
71+
rideId: not-a-uuid
72+
timestamp: "2026-03-30T14:22:15Z"
73+
'401':
74+
description: Authentication required or invalid
75+
content:
76+
application/json:
77+
schema:
78+
$ref: '#/components/schemas/ErrorResponse'
79+
examples:
80+
noAuth:
81+
summary: Missing authorization header
82+
value:
83+
error: MISSING_AUTH
84+
message: Authorization header is required.
85+
timestamp: "2026-03-30T14:22:15Z"
86+
badToken:
87+
summary: Expired or invalid token
88+
value:
89+
error: INVALID_TOKEN
90+
message: The provided token is expired or malformed.
91+
timestamp: "2026-03-30T14:22:15Z"
92+
'403':
93+
description: User not authorized to delete this ride
94+
content:
95+
application/json:
96+
schema:
97+
$ref: '#/components/schemas/ErrorResponse'
98+
example:
99+
error: NOT_RIDE_OWNER
100+
message: You do not have permission to delete this ride.
101+
rideId: 550e8400-e29b-41d4-a716-446655440000
102+
timestamp: "2026-03-30T14:22:15Z"
103+
'404':
104+
description: Ride not found
105+
content:
106+
application/json:
107+
schema:
108+
$ref: '#/components/schemas/ErrorResponse'
109+
example:
110+
error: RIDE_NOT_FOUND
111+
message: No ride found with the specified ID.
112+
rideId: 550e8400-e29b-41d4-a716-446655440000
113+
timestamp: "2026-03-30T14:22:15Z"
114+
'500':
115+
description: Server error (e.g., database or outbox failure)
116+
content:
117+
application/json:
118+
schema:
119+
$ref: '#/components/schemas/ErrorResponse'
120+
example:
121+
error: OUTBOX_ERROR
122+
message: Failed to persist deletion event. Please try again later.
123+
timestamp: "2026-03-30T14:22:15Z"
124+
125+
components:
126+
securitySchemes:
127+
BearerAuth:
128+
type: http
129+
scheme: bearer
130+
bearerFormat: JWT
131+
description: JWT token obtained from user login. Include in Authorization header as "Bearer {token}"
132+
133+
schemas:
134+
DeleteRideSuccessResponse:
135+
type: object
136+
required:
137+
- rideId
138+
- deletedAt
139+
- message
140+
properties:
141+
rideId:
142+
type: string
143+
format: uuid
144+
description: ID of the deleted ride
145+
example: 550e8400-e29b-41d4-a716-446655440000
146+
deletedAt:
147+
type: string
148+
format: date-time
149+
description: UTC timestamp when the ride was deleted
150+
example: "2026-03-30T14:22:15Z"
151+
message:
152+
type: string
153+
description: Success message
154+
example: Ride deleted successfully.
155+
isIdempotent:
156+
type: boolean
157+
description: Whether this was an idempotent response (ride was already deleted)
158+
example: false
159+
160+
ErrorResponse:
161+
type: object
162+
required:
163+
- error
164+
- message
165+
properties:
166+
error:
167+
type: string
168+
enum:
169+
- INVALID_RIDE_ID
170+
- MISSING_AUTH
171+
- INVALID_TOKEN
172+
- NOT_RIDE_OWNER
173+
- RIDE_NOT_FOUND
174+
- OUTBOX_ERROR
175+
description: Machine-readable error code
176+
example: NOT_RIDE_OWNER
177+
message:
178+
type: string
179+
description: Human-readable error message
180+
example: You do not have permission to delete this ride.
181+
rideId:
182+
type: string
183+
format: uuid
184+
description: The ride ID from the request (may be null if request malformed)
185+
example: 550e8400-e29b-41d4-a716-446655440000
186+
nullable: true
187+
timestamp:
188+
type: string
189+
format: date-time
190+
description: UTC timestamp of the error
191+
example: "2026-03-30T14:22:15Z"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "RideDeleted Event",
4+
"description": "Event emitted when a user deletes a ride from their history. Immutable and append-only.",
5+
"type": "object",
6+
"required": ["eventType", "rideId", "userId", "deletedAt", "deletedBy"],
7+
"properties": {
8+
"eventType": {
9+
"type": "string",
10+
"enum": ["RideDeleted"],
11+
"description": "Event type identifier"
12+
},
13+
"rideId": {
14+
"type": "string",
15+
"format": "uuid",
16+
"description": "UUID of the ride being deleted",
17+
"example": "550e8400-e29b-41d4-a716-446655440000"
18+
},
19+
"userId": {
20+
"type": "string",
21+
"description": "ID of the ride owner (user who created the ride). Must match deletedBy for user-initiated deletions.",
22+
"example": "user-42"
23+
},
24+
"deletedAt": {
25+
"type": "string",
26+
"format": "date-time",
27+
"description": "UTC timestamp when the deletion was persisted to the event store. Set by the server.",
28+
"example": "2026-03-30T14:22:15Z"
29+
},
30+
"deletedBy": {
31+
"type": "string",
32+
"description": "ID of the user who requested the deletion. In the current design, always equals userId. Future use: may differ if admin deletion is implemented.",
33+
"example": "user-42"
34+
}
35+
},
36+
"additionalProperties": false,
37+
"examples": [
38+
{
39+
"eventType": "RideDeleted",
40+
"rideId": "550e8400-e29b-41d4-a716-446655440000",
41+
"userId": "user-42",
42+
"deletedAt": "2026-03-30T14:22:15Z",
43+
"deletedBy": "user-42"
44+
},
45+
{
46+
"eventType": "RideDeleted",
47+
"rideId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
48+
"userId": "user-99",
49+
"deletedAt": "2026-03-30T10:05:30Z",
50+
"deletedBy": "user-99"
51+
}
52+
],
53+
"notes": {
54+
"immutability": "This event is immutable. Once persisted to the event store, it is never modified or deleted.",
55+
"audit": "This event is retained indefinitely in the event store for compliance and temporal queries.",
56+
"idempotency": "Multiple deletion requests for the same ride will only append one RideDeleted event. Subsequent requests return idempotent success without appending duplicate events.",
57+
"backwards_compatibility": "Future versions may allow deletedBy to differ from userId (e.g., for admin deletion). Consumers should not assume they are always equal.",
58+
"related_events": ["RideCreated", "RideEdited"]
59+
}
60+
}

0 commit comments

Comments
 (0)