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:
2026-03-06 20:19:41 +01:00
parent 0b2b84dafc
commit 6aeb4b8bca
83 changed files with 6486 additions and 660 deletions

View 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.