Migrate project artifacts to spec-kit format
- Move cross-cutting docs (personas, design system, implementation phases, Ideen.md) to .specify/memory/ - Move cross-cutting research and plans to .specify/memory/research/ and .specify/memory/plans/ - Extract 5 setup tasks from spec/setup-tasks.md into individual specs/001-005/spec.md files with spec-kit template format - Extract 20 user stories from spec/userstories.md into individual specs/006-026/spec.md files with spec-kit template format - Relocate feature-specific research and plan docs into specs/[feature]/ - Add spec-kit constitution, templates, scripts, and slash commands - Slim down CLAUDE.md to Claude-Code-specific config, delegate principles to .specify/memory/constitution.md - Update ralph.sh with stream-json output and per-iteration logging - Delete old spec/ and docs/agents/ directories - Gitignore Ralph iteration JSONL logs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
96
specs/024-cancel-event/spec.md
Normal file
96
specs/024-cancel-event/spec.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Feature Specification: Cancel an Event as Organizer
|
||||
|
||||
**Feature**: `024-cancel-event`
|
||||
**Created**: 2026-03-06
|
||||
**Status**: Draft
|
||||
**Source**: Migrated from spec/userstories.md
|
||||
|
||||
## User Scenarios & Testing
|
||||
|
||||
### User Story 1 - Cancel event with optional message (Priority: P1)
|
||||
|
||||
The event organizer, from the organizer view, triggers a dedicated "Cancel event" action. A confirmation step is required before finalising. The organizer may optionally enter a cancellation message (reason or explanation). After confirmation, the server sets the event to cancelled state with the provided message. The event page immediately displays a "cancelled" state visible to all visitors. No RSVP submissions are accepted by the server from this point.
|
||||
|
||||
**Why this priority**: Cancellation is a fundamental lifecycle action — guests need to be clearly informed when an event is cancelled rather than discovering it silently. This is the core of US-18.
|
||||
|
||||
**Independent Test**: Can be fully tested by creating an event, triggering cancel (with and without a message), and verifying the event page shows a "cancelled" indicator and the RSVP form is absent.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** an organizer with a valid organizer token in localStorage, **When** they click "Cancel event", **Then** a confirmation step is shown before any change is made.
|
||||
2. **Given** the organizer confirms cancellation without entering a message, **When** the server processes the request, **Then** the event is marked cancelled and the event page shows a "cancelled" state with no message.
|
||||
3. **Given** the organizer confirms cancellation with a message, **When** the server processes the request, **Then** the event page displays the "cancelled" state along with the cancellation message.
|
||||
4. **Given** a cancelled event, **When** a guest attempts to submit an RSVP, **Then** the server rejects the submission and the RSVP form is not shown on the event page.
|
||||
5. **Given** a cancelled event that has not yet expired, **When** any visitor opens the event URL, **Then** the full event page renders with a clear "cancelled" indicator — no partial data is hidden.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Adjust expiry date during cancellation (Priority: P2)
|
||||
|
||||
When cancelling, the organizer can optionally adjust the event's expiry date to control how long the cancellation notice remains visible before automatic data deletion (US-12). The adjusted date must be in the future, consistent with US-5's expiry date constraint.
|
||||
|
||||
**Why this priority**: The expiry date adjustment is a convenience — organizers may want to keep the cancellation notice visible for a specific period (e.g. one more week) or trigger earlier cleanup. The core cancellation (P1) works without it.
|
||||
|
||||
**Independent Test**: Can be tested by cancelling an event while setting a new expiry date in the future, then verifying the event's expiry date was updated and data persists until that date.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the organizer confirms cancellation with a new expiry date set in the future, **When** the server processes the request, **Then** the event is cancelled and its expiry date is updated to the provided value.
|
||||
2. **Given** the organizer provides an expiry date in the past or set to today during cancellation, **When** the server processes the request, **Then** the request is rejected with a clear validation error.
|
||||
3. **Given** the organizer confirms cancellation without adjusting the expiry date, **When** the server processes the request, **Then** the existing expiry date is unchanged.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Edit cancellation message after cancellation (Priority: P3)
|
||||
|
||||
After an event is cancelled, the organizer can update the cancellation message (e.g. to correct a typo or add further explanation). The cancelled state itself cannot be changed.
|
||||
|
||||
**Why this priority**: Editing the message is a refinement capability. The core behaviour (cancellation + message at time of cancellation) is sufficient for P1 and P2.
|
||||
|
||||
**Independent Test**: Can be tested by cancelling an event, then submitting an updated cancellation message via the organizer view, and verifying the new message is displayed on the event page.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a cancelled event with a valid organizer token, **When** the organizer submits an updated cancellation message, **Then** the event page displays the new message.
|
||||
2. **Given** a cancelled event with a valid organizer token, **When** the organizer attempts to un-cancel (set the event back to active), **Then** the server rejects the request — cancellation is a one-way state transition.
|
||||
3. **Given** a cancelled event with an absent or invalid organizer token, **When** a request is made to edit the cancellation message, **Then** the server rejects the request.
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- Organizer token absent or invalid: the "Cancel event" action is not shown in the UI and the server rejects any cancel or message-edit request with an appropriate error response.
|
||||
- RSVP on a cancelled event: the server rejects RSVP submissions with a clear error; the RSVP form is hidden on the client.
|
||||
- Cancellation message is optional: omitting it is valid — the event still transitions to cancelled state with no message displayed.
|
||||
- Cancellation + expiry: after the expiry date passes, the event is deleted by the cleanup process (US-12) regardless of cancelled state; the cancellation data is removed as part of the full event deletion.
|
||||
- Already-expired event at time of cancellation: [NEEDS EXPANSION] — it is unclear whether cancellation should be allowed if the event has already expired but not yet been cleaned up. This edge case should be addressed during implementation.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: The Event entity MUST persist a `is_cancelled` boolean (default false) and an optional `cancellation_message` string, both server-side.
|
||||
- **FR-002**: The cancel endpoint MUST require a valid organizer token; requests without a valid token are rejected.
|
||||
- **FR-003**: The cancel endpoint MUST accept an optional plain-text cancellation message.
|
||||
- **FR-004**: The cancel endpoint MUST accept an optional updated expiry date, which MUST be in the future; if provided and not in the future, the request is rejected with a clear validation error.
|
||||
- **FR-005**: Cancellation MUST be a one-way state transition: once cancelled, the event cannot be set back to active via any API endpoint.
|
||||
- **FR-006**: The event page MUST display a "cancelled" state indicator and the cancellation message (if present) for any visitor once the event is cancelled.
|
||||
- **FR-007**: The RSVP endpoint MUST reject submissions for cancelled events; the RSVP form MUST be hidden on the client for cancelled events.
|
||||
- **FR-008**: The organizer MUST be able to update the cancellation message after cancellation via a dedicated update action; this action MUST require a valid organizer token.
|
||||
- **FR-009**: The UI MUST present a confirmation step before submitting the cancellation request; the cancel action MUST NOT be triggerable in a single click without confirmation.
|
||||
- **FR-010**: The "Cancel event" action and organizer-specific cancel UI MUST NOT be rendered when no valid organizer token is present in localStorage.
|
||||
- **FR-011**: No personal data, IP addresses, or identifiers MUST be logged during cancellation or cancellation message updates.
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **CancellationState**: Value type on Event. Fields: `is_cancelled` (boolean, persistent), `cancellation_message` (optional string, persistent). Not a separate entity — stored on the Event record.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: An organizer with a valid token can cancel an event (with or without a message) in a single confirmed action, and the cancelled state is immediately reflected on the event page.
|
||||
- **SC-002**: After cancellation, no guest can successfully submit an RSVP via the API, regardless of client-side state.
|
||||
- **SC-003**: The event page correctly renders a "cancelled" indicator (and message if provided) for any visitor after cancellation — no RSVP form, no false "active" state.
|
||||
- **SC-004**: Cancellation data (state and message) is removed automatically when the event expires and is deleted by the cleanup process (US-12).
|
||||
- **SC-005**: No client or API call can revert a cancelled event to an active state.
|
||||
Reference in New Issue
Block a user